From b86ec0c75fb714f58c587de20032eb41febb39c2 Mon Sep 17 00:00:00 2001 From: grant Date: Thu, 11 Mar 2004 13:03:58 +0000 Subject: import the required bits of bootstrap-pkgsrc from 20040311. --- bootstrap/README | 24 + bootstrap/README.AIX | 42 + bootstrap/README.Darwin | 33 + bootstrap/README.FreeBSD | 23 + bootstrap/README.IRIX | 33 + bootstrap/README.Interix | 38 + bootstrap/README.Linux | 7 + bootstrap/README.MacOSX | 30 + bootstrap/README.OpenBSD | 32 + bootstrap/README.Solaris | 56 + bootstrap/bmake/ChangeLog | 219 ++ bootstrap/bmake/FILES | 83 + bootstrap/bmake/Makefile.in | 116 + bootstrap/bmake/PSD.doc/Makefile | 8 + bootstrap/bmake/PSD.doc/tutorial.ms | 3744 ++++++++++++++++++++++++++++++ bootstrap/bmake/README | 110 + bootstrap/bmake/aclocal.m4 | 77 + bootstrap/bmake/arch.c | 1344 +++++++++++ bootstrap/bmake/bit.h | 102 + bootstrap/bmake/buf.c | 308 +++ bootstrap/bmake/buf.h | 79 + bootstrap/bmake/compat.c | 636 +++++ bootstrap/bmake/cond.c | 1352 +++++++++++ bootstrap/bmake/config.h.in | 182 ++ bootstrap/bmake/configure | 2966 +++++++++++++++++++++++ bootstrap/bmake/configure.in | 210 ++ bootstrap/bmake/dir.c | 1476 ++++++++++++ bootstrap/bmake/dir.h | 72 + bootstrap/bmake/find_lib.sh | 13 + bootstrap/bmake/for.c | 390 ++++ bootstrap/bmake/getenv.c | 90 + bootstrap/bmake/getopt.c | 179 ++ bootstrap/bmake/hash.c | 429 ++++ bootstrap/bmake/hash.h | 118 + bootstrap/bmake/install-sh | 201 ++ bootstrap/bmake/job.c | 3483 +++++++++++++++++++++++++++ bootstrap/bmake/job.h | 265 +++ bootstrap/bmake/list.h | 300 +++ bootstrap/bmake/lst.h | 166 ++ bootstrap/bmake/lst.lib/Makefile | 10 + bootstrap/bmake/lst.lib/lstAppend.c | 124 + bootstrap/bmake/lst.lib/lstAtEnd.c | 81 + bootstrap/bmake/lst.lib/lstAtFront.c | 82 + bootstrap/bmake/lst.lib/lstClose.c | 88 + bootstrap/bmake/lst.lib/lstConcat.c | 187 ++ bootstrap/bmake/lst.lib/lstDatum.c | 82 + bootstrap/bmake/lst.lib/lstDeQueue.c | 92 + bootstrap/bmake/lst.lib/lstDestroy.c | 113 + bootstrap/bmake/lst.lib/lstDupl.c | 110 + bootstrap/bmake/lst.lib/lstEnQueue.c | 84 + bootstrap/bmake/lst.lib/lstFind.c | 81 + bootstrap/bmake/lst.lib/lstFindFrom.c | 105 + bootstrap/bmake/lst.lib/lstFirst.c | 82 + bootstrap/bmake/lst.lib/lstForEach.c | 83 + bootstrap/bmake/lst.lib/lstForEachFrom.c | 123 + bootstrap/bmake/lst.lib/lstInit.c | 87 + bootstrap/bmake/lst.lib/lstInsert.c | 124 + bootstrap/bmake/lst.lib/lstInt.h | 113 + bootstrap/bmake/lst.lib/lstIsAtEnd.c | 92 + bootstrap/bmake/lst.lib/lstIsEmpty.c | 80 + bootstrap/bmake/lst.lib/lstLast.c | 82 + bootstrap/bmake/lst.lib/lstMember.c | 80 + bootstrap/bmake/lst.lib/lstNext.c | 125 + bootstrap/bmake/lst.lib/lstOpen.c | 92 + bootstrap/bmake/lst.lib/lstRemove.c | 142 ++ bootstrap/bmake/lst.lib/lstReplace.c | 84 + bootstrap/bmake/lst.lib/lstSucc.c | 84 + bootstrap/bmake/lst.lib/makefile.boot.in | 45 + bootstrap/bmake/machine.sh | 80 + bootstrap/bmake/main.c | 1743 ++++++++++++++ bootstrap/bmake/make-conf.h | 133 ++ bootstrap/bmake/make.1 | 1238 ++++++++++ bootstrap/bmake/make.c | 1107 +++++++++ bootstrap/bmake/make.h | 438 ++++ bootstrap/bmake/makefile.boot.in | 60 + bootstrap/bmake/missing/sys/cdefs.h | 176 ++ bootstrap/bmake/mk/bsd.README | 683 ++++++ bootstrap/bmake/mk/bsd.dep.mk | 62 + bootstrap/bmake/mk/bsd.depall.mk | 5 + bootstrap/bmake/mk/bsd.doc.mk | 84 + bootstrap/bmake/mk/bsd.files.mk | 40 + bootstrap/bmake/mk/bsd.hostprog.mk | 137 ++ bootstrap/bmake/mk/bsd.inc.mk | 29 + bootstrap/bmake/mk/bsd.info.mk | 64 + bootstrap/bmake/mk/bsd.kernobj.mk | 33 + bootstrap/bmake/mk/bsd.kinc.mk | 145 ++ bootstrap/bmake/mk/bsd.kmod.mk | 98 + bootstrap/bmake/mk/bsd.lib.mk | 524 +++++ bootstrap/bmake/mk/bsd.links.mk | 44 + bootstrap/bmake/mk/bsd.man.mk | 199 ++ bootstrap/bmake/mk/bsd.nls.mk | 65 + bootstrap/bmake/mk/bsd.obj.mk | 91 + bootstrap/bmake/mk/bsd.prog.mk | 224 ++ bootstrap/bmake/mk/bsd.subdir.mk | 62 + bootstrap/bmake/mk/bsd.sys.mk | 134 ++ bootstrap/bmake/mk/sys.mk | 207 ++ bootstrap/bmake/mkdeps.sh | 322 +++ bootstrap/bmake/nonints.h | 154 ++ bootstrap/bmake/parse.c | 2825 ++++++++++++++++++++++ bootstrap/bmake/pathnames.h | 52 + bootstrap/bmake/ranlib.h | 30 + bootstrap/bmake/setenv.c | 154 ++ bootstrap/bmake/sigcompat.c | 333 +++ bootstrap/bmake/sprite.h | 113 + bootstrap/bmake/str.c | 472 ++++ bootstrap/bmake/suff.c | 2544 ++++++++++++++++++++ bootstrap/bmake/targ.c | 726 ++++++ bootstrap/bmake/trace.c | 125 + bootstrap/bmake/trace.h | 56 + bootstrap/bmake/util.c | 506 ++++ bootstrap/bmake/var.c | 2919 +++++++++++++++++++++++ bootstrap/bmake/wait.h | 81 + bootstrap/bootstrap | 543 +++++ bootstrap/cleanup | 17 + bootstrap/files/install-sh | 281 +++ bootstrap/files/irix.patch | 40 + bootstrap/files/packages.cat7 | 757 ++++++ bootstrap/files/strip-sh | 26 + bootstrap/mkbinarykit | 116 + bootstrap/mods/bmake/Makefile.in | 116 + bootstrap/mods/mk/Darwin.bsd.lib.mk | 524 +++++ bootstrap/mods/mk/Darwin.bsd.man.mk | 199 ++ bootstrap/mods/mk/Darwin.sys.mk | 210 ++ bootstrap/mods/mk/FreeBSD.bsd.man.mk | 137 ++ bootstrap/mods/mk/IRIX.bsd.lib.mk | 524 +++++ bootstrap/mods/mk/IRIX.own.mk | 244 ++ bootstrap/mods/mk/IRIX.sys.mk | 193 ++ bootstrap/mods/mk/Linux.bsd.lib.mk | 524 +++++ bootstrap/mods/mk/Linux.sys.mk | 194 ++ bootstrap/mods/mk/NetBSD.bsd.man.mk | 199 ++ bootstrap/mods/mk/NetBSD.sys.mk | 207 ++ bootstrap/mods/mk/OpenBSD.bsd.man.mk | 137 ++ bootstrap/mods/mk/OpenBSD.bsd.own.mk | 306 +++ bootstrap/mods/mk/OpenBSD.bsd.prog.mk | 176 ++ bootstrap/mods/mk/OpenBSD.sys.mk | 202 ++ bootstrap/mods/mk/SunOS.bsd.sys.mk | 141 ++ bootstrap/mods/mk/SunOS.sys.mk | 211 ++ bootstrap/mods/mk/bsd.own.mk.in | 328 +++ bootstrap/mods/mk/generic.sys.mk | 208 ++ bootstrap/pkg.sh | 569 +++++ bootstrap/ufsdiskimage | 106 + 141 files changed, 48545 insertions(+) create mode 100644 bootstrap/README create mode 100644 bootstrap/README.AIX create mode 100644 bootstrap/README.Darwin create mode 100644 bootstrap/README.FreeBSD create mode 100644 bootstrap/README.IRIX create mode 100644 bootstrap/README.Interix create mode 100644 bootstrap/README.Linux create mode 100644 bootstrap/README.MacOSX create mode 100644 bootstrap/README.OpenBSD create mode 100644 bootstrap/README.Solaris create mode 100644 bootstrap/bmake/ChangeLog create mode 100644 bootstrap/bmake/FILES create mode 100644 bootstrap/bmake/Makefile.in create mode 100644 bootstrap/bmake/PSD.doc/Makefile create mode 100644 bootstrap/bmake/PSD.doc/tutorial.ms create mode 100644 bootstrap/bmake/README create mode 100644 bootstrap/bmake/aclocal.m4 create mode 100644 bootstrap/bmake/arch.c create mode 100644 bootstrap/bmake/bit.h create mode 100644 bootstrap/bmake/buf.c create mode 100644 bootstrap/bmake/buf.h create mode 100644 bootstrap/bmake/compat.c create mode 100644 bootstrap/bmake/cond.c create mode 100644 bootstrap/bmake/config.h.in create mode 100755 bootstrap/bmake/configure create mode 100644 bootstrap/bmake/configure.in create mode 100644 bootstrap/bmake/dir.c create mode 100644 bootstrap/bmake/dir.h create mode 100755 bootstrap/bmake/find_lib.sh create mode 100644 bootstrap/bmake/for.c create mode 100644 bootstrap/bmake/getenv.c create mode 100644 bootstrap/bmake/getopt.c create mode 100644 bootstrap/bmake/hash.c create mode 100644 bootstrap/bmake/hash.h create mode 100755 bootstrap/bmake/install-sh create mode 100644 bootstrap/bmake/job.c create mode 100644 bootstrap/bmake/job.h create mode 100644 bootstrap/bmake/list.h create mode 100644 bootstrap/bmake/lst.h create mode 100644 bootstrap/bmake/lst.lib/Makefile create mode 100644 bootstrap/bmake/lst.lib/lstAppend.c create mode 100644 bootstrap/bmake/lst.lib/lstAtEnd.c create mode 100644 bootstrap/bmake/lst.lib/lstAtFront.c create mode 100644 bootstrap/bmake/lst.lib/lstClose.c create mode 100644 bootstrap/bmake/lst.lib/lstConcat.c create mode 100644 bootstrap/bmake/lst.lib/lstDatum.c create mode 100644 bootstrap/bmake/lst.lib/lstDeQueue.c create mode 100644 bootstrap/bmake/lst.lib/lstDestroy.c create mode 100644 bootstrap/bmake/lst.lib/lstDupl.c create mode 100644 bootstrap/bmake/lst.lib/lstEnQueue.c create mode 100644 bootstrap/bmake/lst.lib/lstFind.c create mode 100644 bootstrap/bmake/lst.lib/lstFindFrom.c create mode 100644 bootstrap/bmake/lst.lib/lstFirst.c create mode 100644 bootstrap/bmake/lst.lib/lstForEach.c create mode 100644 bootstrap/bmake/lst.lib/lstForEachFrom.c create mode 100644 bootstrap/bmake/lst.lib/lstInit.c create mode 100644 bootstrap/bmake/lst.lib/lstInsert.c create mode 100644 bootstrap/bmake/lst.lib/lstInt.h create mode 100644 bootstrap/bmake/lst.lib/lstIsAtEnd.c create mode 100644 bootstrap/bmake/lst.lib/lstIsEmpty.c create mode 100644 bootstrap/bmake/lst.lib/lstLast.c create mode 100644 bootstrap/bmake/lst.lib/lstMember.c create mode 100644 bootstrap/bmake/lst.lib/lstNext.c create mode 100644 bootstrap/bmake/lst.lib/lstOpen.c create mode 100644 bootstrap/bmake/lst.lib/lstRemove.c create mode 100644 bootstrap/bmake/lst.lib/lstReplace.c create mode 100644 bootstrap/bmake/lst.lib/lstSucc.c create mode 100644 bootstrap/bmake/lst.lib/makefile.boot.in create mode 100755 bootstrap/bmake/machine.sh create mode 100644 bootstrap/bmake/main.c create mode 100644 bootstrap/bmake/make-conf.h create mode 100644 bootstrap/bmake/make.1 create mode 100644 bootstrap/bmake/make.c create mode 100644 bootstrap/bmake/make.h create mode 100644 bootstrap/bmake/makefile.boot.in create mode 100644 bootstrap/bmake/missing/sys/cdefs.h create mode 100644 bootstrap/bmake/mk/bsd.README create mode 100644 bootstrap/bmake/mk/bsd.dep.mk create mode 100644 bootstrap/bmake/mk/bsd.depall.mk create mode 100644 bootstrap/bmake/mk/bsd.doc.mk create mode 100644 bootstrap/bmake/mk/bsd.files.mk create mode 100644 bootstrap/bmake/mk/bsd.hostprog.mk create mode 100644 bootstrap/bmake/mk/bsd.inc.mk create mode 100644 bootstrap/bmake/mk/bsd.info.mk create mode 100644 bootstrap/bmake/mk/bsd.kernobj.mk create mode 100644 bootstrap/bmake/mk/bsd.kinc.mk create mode 100644 bootstrap/bmake/mk/bsd.kmod.mk create mode 100644 bootstrap/bmake/mk/bsd.lib.mk create mode 100644 bootstrap/bmake/mk/bsd.links.mk create mode 100644 bootstrap/bmake/mk/bsd.man.mk create mode 100644 bootstrap/bmake/mk/bsd.nls.mk create mode 100644 bootstrap/bmake/mk/bsd.obj.mk create mode 100644 bootstrap/bmake/mk/bsd.prog.mk create mode 100644 bootstrap/bmake/mk/bsd.subdir.mk create mode 100644 bootstrap/bmake/mk/bsd.sys.mk create mode 100644 bootstrap/bmake/mk/sys.mk create mode 100755 bootstrap/bmake/mkdeps.sh create mode 100644 bootstrap/bmake/nonints.h create mode 100644 bootstrap/bmake/parse.c create mode 100644 bootstrap/bmake/pathnames.h create mode 100644 bootstrap/bmake/ranlib.h create mode 100644 bootstrap/bmake/setenv.c create mode 100644 bootstrap/bmake/sigcompat.c create mode 100644 bootstrap/bmake/sprite.h create mode 100644 bootstrap/bmake/str.c create mode 100644 bootstrap/bmake/suff.c create mode 100644 bootstrap/bmake/targ.c create mode 100644 bootstrap/bmake/trace.c create mode 100644 bootstrap/bmake/trace.h create mode 100644 bootstrap/bmake/util.c create mode 100644 bootstrap/bmake/var.c create mode 100644 bootstrap/bmake/wait.h create mode 100755 bootstrap/bootstrap create mode 100755 bootstrap/cleanup create mode 100755 bootstrap/files/install-sh create mode 100644 bootstrap/files/irix.patch create mode 100644 bootstrap/files/packages.cat7 create mode 100755 bootstrap/files/strip-sh create mode 100755 bootstrap/mkbinarykit create mode 100644 bootstrap/mods/bmake/Makefile.in create mode 100644 bootstrap/mods/mk/Darwin.bsd.lib.mk create mode 100644 bootstrap/mods/mk/Darwin.bsd.man.mk create mode 100644 bootstrap/mods/mk/Darwin.sys.mk create mode 100644 bootstrap/mods/mk/FreeBSD.bsd.man.mk create mode 100644 bootstrap/mods/mk/IRIX.bsd.lib.mk create mode 100644 bootstrap/mods/mk/IRIX.own.mk create mode 100644 bootstrap/mods/mk/IRIX.sys.mk create mode 100644 bootstrap/mods/mk/Linux.bsd.lib.mk create mode 100644 bootstrap/mods/mk/Linux.sys.mk create mode 100644 bootstrap/mods/mk/NetBSD.bsd.man.mk create mode 100644 bootstrap/mods/mk/NetBSD.sys.mk create mode 100644 bootstrap/mods/mk/OpenBSD.bsd.man.mk create mode 100644 bootstrap/mods/mk/OpenBSD.bsd.own.mk create mode 100644 bootstrap/mods/mk/OpenBSD.bsd.prog.mk create mode 100644 bootstrap/mods/mk/OpenBSD.sys.mk create mode 100644 bootstrap/mods/mk/SunOS.bsd.sys.mk create mode 100644 bootstrap/mods/mk/SunOS.sys.mk create mode 100644 bootstrap/mods/mk/bsd.own.mk.in create mode 100644 bootstrap/mods/mk/generic.sys.mk create mode 100755 bootstrap/pkg.sh create mode 100755 bootstrap/ufsdiskimage (limited to 'bootstrap') diff --git a/bootstrap/README b/bootstrap/README new file mode 100644 index 00000000000..5d1b56d1e85 --- /dev/null +++ b/bootstrap/README @@ -0,0 +1,24 @@ +$NetBSD: README,v 1.1.1.1 2004/03/11 13:03:59 grant Exp $ + +To try to get pkgsrc working on your system, please try the following +as root: + +# ./bootstrap [ --prefix=${PREFIX} ] [ --pkgdbdir=${PKGDBDIR} ] \ + [ --pkgsrcdir=${PKGSRCDIR} ] + +The defaults for the arguments are as follows: + + --prefix /usr/pkg + --pkgdbdir /var/db/pkg + --pkgsrcdir /usr/pkgsrc + +It is perfectly acceptable to place ${PKGDBDIR} under ${PREFIX}. + +Make sure that you have a working C compiler and make(1) binary in +your path. + +See http://www.netbsd.org/Documentation/software/packages.html for +more information about bootstrapping and using pkgsrc. + +We'd be very interested in hearing of any successes or failures on +"unknown" (to us) systems. diff --git a/bootstrap/README.AIX b/bootstrap/README.AIX new file mode 100644 index 00000000000..25f636ee98d --- /dev/null +++ b/bootstrap/README.AIX @@ -0,0 +1,42 @@ +$NetBSD: README.AIX,v 1.1.1.1 2004/03/11 13:03:59 grant Exp $ + +You will need gcc. The AIX xlC compiler doesn't work, but probably +could be made to. gcc-3.3.2 has been tested. + +bootstrap-pkgsrc has been tested on AIX 4.3. It will probably work on newer +releases also. + +Please note the use of GNU binutils on AIX is not supported. +Make sure GNU ld is not in your path. + +If you are using gcc +-------------------- +It makes life much simpler if you only use the same gcc consistently +for building all packages. + +It is recommended that an external gcc be used only for bootstrapping +gcc from pkgsrc, lang/gcc3 is recommended. + +Post bootstrap setup +-------------------- + +Here is an example mk.conf: + +--- Cut here --- +# Configure scripts don't always get this right +MACHINE_GNU_PLATFORM=powerpc-ibm-aix4.3.3.0 +CC=gcc +CHECK_SHLIBS=no +RENAME= +USE_GCC3=yes +PKGMAKECONF=${MAKECONF} +--- Cut here --- + +You also need to setup a few environment variables: + +PKG_DBDIR=/usr/pkg/pkgdb +MAKECONF=/usr/pkg/etc/mk.conf +MAKESYSPATH=/usr/pkg/share/mk + +You'll also want to set PATH, MANPATH and LIBPATH + diff --git a/bootstrap/README.Darwin b/bootstrap/README.Darwin new file mode 100644 index 00000000000..1aeb9b32975 --- /dev/null +++ b/bootstrap/README.Darwin @@ -0,0 +1,33 @@ +$NetBSD: README.Darwin,v 1.1.1.1 2004/03/11 13:03:58 grant Exp $ + +If you are using Mac OS X: +Before you start, you will need to download and install the Mac OS X Developer +Tools from Apple's Developer Connection. See http://developer.apple.com/macosx/ +for details. Also, make sure you install X11 for Mac OS X and the X11 SDK from +http://www.apple.com/macosx/x11/download/ if you intend to build packages +that use the X11 Window System. + +Terse instructions: + +$ ./ufsdiskimage create ~/Documents/NetBSD 512 # megabytes - season to taste +$ ./ufsdiskimage mount ~/Documents/NetBSD +$ sudo chown `id -u`:`id -g` /Volumes/NetBSD +$ curl -O \ + ftp://ftp.netbsd.org/pub/NetBSD/NetBSD-current/tar_files/pkgsrc.tar.gz +$ tar -C /Volumes/NetBSD -zxvf pkgsrc.tar.gz && rm pkgsrc.tar.gz +$ sudo ./bootstrap \ + --prefix=/Volumes/NetBSD/pkg \ + --pkgdbdir=/Volumes/NetBSD/pkgdb \ + --pkgsrcdir=/Volumes/NetBSD/pkgsrc + +Note: if you already have a UFS partition, or have a spare partition[*] +that you can format as UFS, use that instead of the UFS disk image. +It'll be somewhat faster and will mount automatically at boot time. + +(Why can't you just use the HFS+ filesystem you've already got? +Because pkgsrc currently requires the underlying filesystem to be +case-sensitive, and HFS+ isn't.) + +[*] - If the partition you want to convert to UFS from HFS+ under +Mac OS X is on the boot disk, then the disk tools won't do anything +with it. See "README.MacOSX". diff --git a/bootstrap/README.FreeBSD b/bootstrap/README.FreeBSD new file mode 100644 index 00000000000..6d889774f0c --- /dev/null +++ b/bootstrap/README.FreeBSD @@ -0,0 +1,23 @@ +$NetBSD: README.FreeBSD,v 1.1.1.1 2004/03/11 13:03:58 grant Exp $ + +Please read the general README file as well. + +Care should be taken so that the tools that this kit installs do not conflict +with the FreeBSD userland tools. There are several steps: + +1. FreeBSD stores its ports pkg database in /var/db/pkg. It is therefore +recommended that you choose a different location (e.g. /usr/pkgdb) by +using the --pkgdbdir option to the bootstrap script. + +2. If you do not intend to use the FreeBSD ports tools, it's probably a +good idea to move them out of the way to avoid confusion, e.g.: + cd /usr/sbin + mv pkg_add pkg_add.orig + mv pkg_create pkg_create.orig + mv pkg_delete pkg_delete.orig + mv pkg_info pkg_info.orig + +3. An example /etc/mk.conf file will be placed in mk.conf.example file +when you use the bootstrap script. + +bootstrap-pkgsrc has been tested on FreeBSD 4.7 and 5.0 (i386). diff --git a/bootstrap/README.IRIX b/bootstrap/README.IRIX new file mode 100644 index 00000000000..aeb23927cb5 --- /dev/null +++ b/bootstrap/README.IRIX @@ -0,0 +1,33 @@ +$NetBSD: README.IRIX,v 1.1.1.1 2004/03/11 13:03:59 grant Exp $ + +You will need a working C compiler, either gcc or SGI's MIPS and MIPSpro +compiler (cc/c89). Please set the CC environment variable according to your +preference. If you do not have a license for the MIPSpro compiler suite, you +can download a gcc tardist file from http://freeware.sgi.com/. + +Please note that you will need Irix 6.5.17 or higher, as this is the earliest +version of Irix providing support for if_indextoname(3), if_nametoindex(3), +etc. + +At this point in time, pkgsrc only supports one ABI. That is, you can not +switch between the old 32-bit ABI, the new 32-bit ABI and the 64-bit ABI. If +you start out using "abi=n32", that's what all your packages will be built +with. + +Therefore, please make sure that you have no conflicting CFLAGS in your +environment or the /etc/mk.conf. Particularly, make sure that you do +not try to link n32 object files with lib64 or vice versa. Check your +/etc/compiler.defaults and $SGI_ABI! + +If you have the actual pkgsrc tree mounted via NFS from a different host, +please make sure to set WRKOBJDIR to a local directory, as it appears that +Irix linker occasionally runs into issues when trying to link over a network +mounted filesystem. + +The bootstrapping process should set all the right options for programs +such as imake(1), but you may want to set some options depending on your +local setup. Please see pkgsrc/mk/bsd.pkg.defaults.mk and, of course, +your compilers man pages for details. + +bootstrap-pkgsrc has been tested on Irix 6.5.18 on: + - an O2 with 1 195 MHZ IP32 Processor (R10000) diff --git a/bootstrap/README.Interix b/bootstrap/README.Interix new file mode 100644 index 00000000000..a1c01e5a521 --- /dev/null +++ b/bootstrap/README.Interix @@ -0,0 +1,38 @@ +$NetBSD: README.Interix,v 1.1.1.1 2004/03/11 13:03:59 grant Exp $ + +Please read the general README file as well. + +NOTE: Currently, Interix support in pkgsrc is unstable, so errors and +problems are expected. Known caveats are listed at the bottom of this +document. + +Interix is a POSIX compatible subsystem for the Windows NT kernel, providing +a Unix-like environment with a tighter kernel integration than available +with Cygwin. It is part of the Windows Services for Unix package, available +for Windows 2000, XP, and 2003. SFU can be downloaded from: + + http://www.microsoft.com/windows/sfu/ + +At an absolute minimum, the following packages must be installed from the +Windows Services for Unix 3.5 distribution in order to use bootstrap-pkgsrc: + + Utilities -> Base Utilities + Interix GNU Components -> (all) + Remote Connectivity + Interix SDK + +The Remote Connectivity subcomponent, Windows Remote Shell Service, does not +need to be installed, but Remote Connectivity itself should be installed in +order to have a working inetd. + +===== + +KNOWN CAVEATS + +* As of this writing, the necessary Interix support stubs have not yet been + added to pkgsrc/mk. + +* On Windows under Interix, the "root" user is actually named + "+Administrator". This may require some modification to pkgsrc to + introduce a parameterized root user for installation purposes. + (Likewise, the bootstrap script sets the root group to "+Administrators".) diff --git a/bootstrap/README.Linux b/bootstrap/README.Linux new file mode 100644 index 00000000000..130de362e01 --- /dev/null +++ b/bootstrap/README.Linux @@ -0,0 +1,7 @@ +$NetBSD: README.Linux,v 1.1.1.1 2004/03/11 13:03:59 grant Exp $ + +Please read the general README file as well. + +Some versions of Linux (for example Debian GNU/Linux) need either libtermcap +or libcurses (libncurses). Installing the distributions libncurses-dev +package (or equivalent) should fix the problem. diff --git a/bootstrap/README.MacOSX b/bootstrap/README.MacOSX new file mode 100644 index 00000000000..31ec99d0d6f --- /dev/null +++ b/bootstrap/README.MacOSX @@ -0,0 +1,30 @@ +$NetBSD: README.MacOSX,v 1.1.1.1 2004/03/11 13:03:59 grant Exp $ + +Please read "README.Darwin" first, as it applies to Mac OS X. + +Since most Macintoshes come with only 1 disk installed, and you +want to have your pkgsrc UFS partition on that disk, there's a +little trick you will have to do. + +The problem is that none of the disk tools will let you touch a +disk that is booted from. In my case, I have a 30G drive that I +partitioned 4G for Classic/OS9, 4G for pkgsrc, and the rest for OS +X. Now, you can unmount the pkgsrc partition, but even if you +newfs it, the partition map will show the partition as Apple_HFS +and not Apple_UFS as automounter needs it to say. The result of +that newfs would be that the partition wouldn't be automounted, +and if you manually mount it, it won't appear in Finder. + +You'll need to boot off of the OS X Installation (User) CD. When +the Installtion program starts, go up to the menu and select Disk +Utility. Now, you will be able to select the partition you want +to be UFS, and Format it Apple UFS. + +Once you've done that, you Quit the Disk Utility and Quit the +Installer... which will reboot your computer. Now the new UFS +partition will show up, but the permissions will be set to root, +so you won't be able to write to it. You'll have to chown the +mount point to you (/Volumes/whatever). + +This note is as of 10.2 (Jaguar) and applies to earlier versions. +[Hopefully Apple will fix Disk Utility in 10.3 (Panther)]. diff --git a/bootstrap/README.OpenBSD b/bootstrap/README.OpenBSD new file mode 100644 index 00000000000..3e2aef6d90e --- /dev/null +++ b/bootstrap/README.OpenBSD @@ -0,0 +1,32 @@ +$NetBSD: README.OpenBSD,v 1.1.1.1 2004/03/11 13:03:59 grant Exp $ + +Please read the general README file as well. + +Care should be taken so that the tools that this kit installs do not conflict +with the OpenBSD userland tools. There are several steps: + +1. OpenBSD stores its ports pkg database in /var/db/pkg. It is therefore +recommended that you choose a different location (e.g. /usr/pkgdb) by +using the --pkgdbdir option to the bootstrap script. + +2. If you do not intend to use the OpenBSD ports tools, it's probably a +good idea to move them out of the way to avoid confusion, e.g.: + cd /usr/sbin + mv pkg_add pkg_add.orig + mv pkg_create pkg_create.orig + mv pkg_delete pkg_delete.orig + mv pkg_info pkg_info.orig + +3. An example /etc/mk.conf file will be placed in mk.conf.example file +when you use the bootstrap script. OpenBSD's make program uses /etc/mk.conf +as well. You can work around this by enclosing all the pkgsrc specific parts +of the mk.conf file with: + +.ifdef BSD_PKG_MK +# Pkgsrc stuff, e.g. insert mk.conf.example or similar here +.else +# OpenBSD stuff +.endif + +bootstrap-pkgsrc has been tested on OpenBSD 3.2 (i386). Some testing has +been done on 3.0 as well. diff --git a/bootstrap/README.Solaris b/bootstrap/README.Solaris new file mode 100644 index 00000000000..104aa7176f2 --- /dev/null +++ b/bootstrap/README.Solaris @@ -0,0 +1,56 @@ +$NetBSD: README.Solaris,v 1.1.1.1 2004/03/11 13:03:59 grant Exp $ + +You will need a working C compiler. Both gcc 2.95.3 and Sun +WorkShop 5 have been tested. + +The following packages are required on Solaris 8 for the bootstrap +process and to build packages. + + - SUNWsprot + - SUNWarc + - SUNWbtool + - SUNWtoo + - SUNWlibm + +Please note the use of GNU binutils on Solaris is not supported. + +If you are using gcc +-------------------- +It makes life much simpler if you only use the same gcc consistently +for building all packages. + +See http://www.netbsd.org/Documentation/software/packages.html for +binary kits and initial packages, including gcc. + +It is recommended that an external gcc be used only for bootstrapping, +then remove gcc and install the pkgsrc binary package. + +Binary packages of gcc can be found through +http://www.sun.com/bigadmin/common/freewareSearch.html + +If you are using Sun WorkShop +----------------------------- +You will need at least the following packages installed (from WorkShop +5.0): + + SPROcc Sun WorkShop Compiler C 5.0 + SPROcpl Sun WorkShop Compiler C++ 5.0 + SPROild Sun WorkShop Incremental Linker + SPROlang Sun WorkShop Compilers common components + +You should set CC and CXX in /etc/mk.conf, eg. + + CC= cc + CXX= CC + +You may also want to build 64-bit binaries, eg. + + CFLAGS= -xtarget=ultra -xarch=v9 + + +Whichever compiler you use, please ensure the compiler tools and +your $prefix are in your PATH. This includes /usr/ccs/{bin,lib} +and eg. /usr/pkg/{bin,sbin}. + +bootstrap-pkgsrc has been tested on Solaris 2.6 -> 9. + diff --git a/bootstrap/bmake/ChangeLog b/bootstrap/bmake/ChangeLog new file mode 100644 index 00000000000..f0411f16266 --- /dev/null +++ b/bootstrap/bmake/ChangeLog @@ -0,0 +1,219 @@ +Tue Oct 16 12:18:42 2001 Simon J. Gerraty + + * Merge with NetBSD make + pick up fix for .END failure in compat mode. + pick up fix for extra va_end() in ParseVErrorInternal. + +Thu Oct 11 13:20:06 2001 Simon J. Gerraty + + * configure.in: for systems that have sys/cdefs.h check if it is + compatible. If not, include the one under missing, but tell it to + include the native one too - necessary on Linux. + + * missing/sys/cdefs.h: if NEED_HOST_CDEFS_H is defined, use + include_next (for gcc) to get the native sys/cdefs.h + +Tue Aug 21 02:29:34 2001 Simon J. Gerraty + + * job.c (JobFinish): Fix an earlier merge bug that resulted in + leaking descriptors when using -jN. + + * job.c (JobPrintCommand): See if "curdir" exists before + attempting to chdir(). Doing the chdir directly in make (when in + compat mode) fails silently, so let the -jN version do the same. + This can happen when building kernels in an object tree and + playing clever games to reset .CURDIR. + + * Merged with NetBSD make + pick up .USEBEFORE + +Tue Jun 26 23:45:11 2001 Simon J. Gerraty + + * makefile.boot.in: Give bmake.boot a MAKESYSPATH that might work. + +Tue Jun 12 16:48:57 2001 Simon J. Gerraty + + * var.c (Var_Set): Add 4th (flags) arg so VarLoopExpand can tell + us not to export the iterator variable when using VAR_CMD context. + +Sun Jun 10 21:55:21 2001 Simon J. Gerraty + + * job.c (Job_CatchChildren): don't call Job_CatchOutput() here, + its the wrong "fix". + +Sat Jun 9 00:11:24 2001 Simon J. Gerraty + + * Redesigned export of VAR_CMD's via MAKEFLAGS. + We now simply append the variable names to .MAKEOVERRIDES, and + handle duplicate suppression and quoting in ExportMAKEFLAGS using: + ${.MAKEOVERRIDES:O:u:@v@$v=${$v:Q}@} + Apart from fixing quoting bugs in previous version, this allows us + to export vars to the environment by simply doing: + .MAKEOVERRIDES+= PATH + Merged again with NetBSD make, but the above is the only change. + + * configure.in: added + --disable-pwd-override disable $PWD overriding getcwd() + --disable-check-make-chdir disable make trying to guess + when it should automatically cd ${.CURDIR} + + * Merge with NetBSD make, changes include: + parse.c (ParseDoDependency): Spot that the syntax error is + caused by an unresolved cvs/rcs conflict and say so. + var.c: most of Var* functions now take a ctxt as 1st arg. + now does variable substituion on rhs of sysv style modifiers. + + * var.c (Var_Set): exporting of command line variables (VAR_CMD) + is now done here. We append the name='value' to .MAKEOVERRIDES + rather than directly into MAKEFLAGS as this allows a Makefile to + use .MAKEOVERRIDES= to disable this behaviour. GNU make uses a + very similar mechanism. Note that in adding name='value' to + .MAKEOVERRIDES we do the moral equivalent of: + .MAKEOVERRIDES:= ${.MAKEOVERRIDES:Nname=*} name='val' + +Fri Jun 1 14:08:02 2001 Simon J. Gerraty + + * make-conf.h (USE_IOVEC): make it conditional on HAVE_SYS_UIO_H + + * Merged with NetBSD make + make -dx can now be used to run commands via sh -x + better error messages on exec failures. + +Thu May 31 01:44:54 2001 Simon J. Gerraty + + * Makefile.in (main.o): depends on ${SRCS} ${MAKEFILE} so that + MAKE_VERSION gets updated. Also don't use ?= for MAKE_VERSION, + MACHINE etc otherwise they propagate from the previous bmake. + + * configure.in (machine): allow --with-machine=generic to make + configure use machine.sh to set MACHINE. + + * job.c (JobInterrupt): convert to using WAIT_T and friends. + + * Makefile.in: mention in bmake.1 that we use autoconf. + + * make.1: mention MAKE_PRINT_VAR_ON_ERROR. + +Wed May 30 23:17:18 2001 Simon J. Gerraty + + * main.c (ReadMakefile): don't set MAKEFILE if reading ".depend" + as that rather defeats the usefulness of ${MAKEFILE}. + + * main.c (MainParseArgs): append command line variable assignments + to MAKEFLAGS so that they get propagated to child make's. + Apparently this is required POSIX behaviour? Its useful anyway. + +Tue May 29 02:20:07 2001 Simon J. Gerraty + + * compat.c (CompatRunCommand): don't use perror() since stdio may + cause problems in child of vfork(). + + * compat.c, main.c: Call PrintOnError() when we are going to bail. + This routine prints out the .curdir where we stopped and will also + display any vars listed in ${MAKE_PRINT_VAR_ON_ERROR}. + + * main.c: add ${.newline} to hold a "\n" - sometimes handy in + :@ expansion. + + * var.c: VarLoopExpand: ignore addSpace if a \n is present. + + * Added RCSid's for the files we've touched. + +Thu May 24 15:41:37 2001 Simon J. Gerraty + + * configure.in: Thanks to some clues from mdb@juniper.net, + added autoconf magic to control setting of MACHINE, MACHINE_ARCH + as well as what ends up in _PATH_DEFSYSPATH. We now have: + + --with-machine=MACHINE explicitly set MACHINE + --with-force-machine=MACHINE set FORCE_MACHINE + --with-machine_arch=MACHINE_ARCH explicitly set MACHINE_ARCH + --with-default-sys-path=PATH:DIR:LIST use an explicit _PATH_DEFSYSPATH + --with-prefix-sys-path=PATH:DIR:LIST prefix _PATH_PREFIX_SYSPATH + --with-path-objdirprefix=PATH override _PATH_OBJDIRPREFIX + + If _PATH_OBJDIRPREFIX is set to "no" we won't define it. + + * makefile: added a pathetically simple makefile to drive + bootstrapping. Running configure by hand is more useful. + + * Makefile.in: added MAKE_VERSION, and reworked things to be less + dependent on NetBSD bsd.*.mk + + * pathnames.h: allow NO_PATH_OBJDIRPREFIX to stop us defining + _PATH_OBJDIRPREFIX for those that don't want a default. + construct _PATH_DEFSYSPATH from the info we get from configure. + + * main.c: allow for no _PATH_OBJDIRPREFIX, set ${MAKE_VERSION} + if MAKE_VERSION is defined. + + * compat.c: when we bail, print out the .CURDIR we were in. + +Sat May 12 00:34:12 2001 Simon J. Gerraty + + * Merged with NetBSD make + + * var.c: fixed a bug in the handling of the modifier :P + if the node as found but the path was null, we segfault trying to + duplicate it. + +Mon Mar 5 16:20:33 2001 Simon J. Gerraty + + * Merged with NetBSD make + + * make.c: Make_OODate's test for a library out of date was using + cmtime where it should have used mtime (my bug). + + * compat.c: Use perror() to tell us what really went wrong when we + cannot exec a command. + +Fri Dec 15 10:11:08 2000 Simon J. Gerraty + + * Merged with NetBSD make + +Sat Jun 10 10:11:08 2000 Simon J. Gerraty + + * Merged with NetBSD make + +Thu Jun 1 10:11:08 2000 Simon J. Gerraty + + * Merged with NetBSD make + +Tue May 30 10:11:08 2000 Simon J. Gerraty + + * Merged with NetBSD make + +Thu Apr 27 00:07:47 2000 Simon J. Gerraty + + * util.c: don't provide signal() since we use sigcompat.c + + * Makefile.in: added a build target. + + * var.c (Var_Parse): added ODE modifiers :U, :D, :L, :P, :@ and :! + These allow some quite clever magic. + + * main.c (main): added support for getenv(MAKESYSPATH). + +Mon Apr 2 16:25:13 2000 Simon J. Gerraty + + * Disable $PWD overriding getcwd() if MAKEOBJDIRPREFIX is set. + This avoids objdir having a different value depending on how a + directory was reached (via command line, or subdir.mk). + + * If FORCE_MACHINE is defined, ignore getenv("MACHINE"). + +Mon Apr 2 23:15:31 2000 Simon J. Gerraty + + * Do a chdir(${.CURDIR}) before invoking ${.MAKE} or ${.MAKE:T} if + MAKEOBJDIRPREFIX is set and NOCHECKMAKECHDIR is not. + I've been testing this in NetBSD's make for some weeks. + + * Turn Makefile into Makefile.in and make it useful. + +Tue Feb 29 22:08:00 2000 Simon J. Gerraty + + * Imported NetBSD's -current make(1) and resolve conflicts. + + * Applied autoconf patches from bmake v2 + + * Imported clean code base from NetBSD-1.0 diff --git a/bootstrap/bmake/FILES b/bootstrap/bmake/FILES new file mode 100644 index 00000000000..4ff1226be12 --- /dev/null +++ b/bootstrap/bmake/FILES @@ -0,0 +1,83 @@ +FILES +ChangeLog +bmake.cat1 +Makefile.in +PSD.doc/Makefile +PSD.doc/tutorial.ms +README +arch.c +bit.h +buf.c +buf.h +compat.c +cond.c +make-conf.h +config.h.in +configure +aclocal.m4 +configure.in +dir.c +dir.h +find_lib.sh +for.c +getenv.c +getopt.c +hash.c +hash.h +install-sh +job.c +job.h +trace.c +trace.h +list.h +lst.h +lst.lib/Makefile +lst.lib/lstAppend.c +lst.lib/lstAtEnd.c +lst.lib/lstAtFront.c +lst.lib/lstClose.c +lst.lib/lstConcat.c +lst.lib/lstDatum.c +lst.lib/lstDeQueue.c +lst.lib/lstDestroy.c +lst.lib/lstDupl.c +lst.lib/lstEnQueue.c +lst.lib/lstFind.c +lst.lib/lstFindFrom.c +lst.lib/lstFirst.c +lst.lib/lstForEach.c +lst.lib/lstForEachFrom.c +lst.lib/lstInit.c +lst.lib/lstInsert.c +lst.lib/lstInt.h +lst.lib/lstIsAtEnd.c +lst.lib/lstIsEmpty.c +lst.lib/lstLast.c +lst.lib/lstMember.c +lst.lib/lstNext.c +lst.lib/lstOpen.c +lst.lib/lstRemove.c +lst.lib/lstReplace.c +lst.lib/lstSucc.c +lst.lib/makefile.boot.in +machine.sh +main.c +make.1 +make.c +make.h +makefile.boot.in +missing/sys/cdefs.h +mkdeps.sh +nonints.h +parse.c +pathnames.h +ranlib.h +setenv.c +sigcompat.c +sprite.h +str.c +suff.c +targ.c +util.c +var.c +wait.h diff --git a/bootstrap/bmake/Makefile.in b/bootstrap/bmake/Makefile.in new file mode 100644 index 00000000000..baa2dc8b379 --- /dev/null +++ b/bootstrap/bmake/Makefile.in @@ -0,0 +1,116 @@ +# $NetBSD: Makefile.in,v 1.1.1.1 2004/03/11 13:04:14 grant Exp $ +# @(#)Makefile 5.2 (Berkeley) 12/28/90 + +# $Id: Makefile.in,v 1.1.1.1 2004/03/11 13:04:14 grant Exp $ + +# you can use this Makefile if you have an earlier version of bmake. +prefix= @prefix@ +srcdir= @srcdir@ +CC?= @CC@ +BUILD_DATE!= date +%Y%m%d +MAKE_VERSION:= bmake-3.1.12 ${BUILD_DATE} +MACHINE=@machine@ +MACHINE_ARCH=@machine_arch@ + +CFLAGS+= -I. -I${srcdir} @DEFS@ @CPPFLAGS@ ${XDEFS} ${CFLAGS_${.TARGET:T}} +CFLAGS_main.o= "-DMAKE_VERSION=\"${MAKE_VERSION}\"" +LIBOBJS= @LIBOBJS@ + +PROG= bmake +SRCS= arch.c buf.c compat.c cond.c dir.c for.c hash.c job.c main.c \ + make.c parse.c str.c suff.c targ.c trace.c var.c util.c +SRCS+= lstAppend.c lstAtEnd.c lstAtFront.c lstClose.c lstConcat.c \ + lstDatum.c lstDeQueue.c lstDestroy.c lstDupl.c lstEnQueue.c \ + lstFind.c lstFindFrom.c lstFirst.c lstForEach.c lstForEachFrom.c \ + lstInit.c lstInsert.c lstIsAtEnd.c lstIsEmpty.c lstLast.c \ + lstMember.c lstNext.c lstOpen.c lstRemove.c lstReplace.c lstSucc.c + +.if !empty(LIBOBJS) +SRCS+= ${LIBOBJS:.o=.c} +.endif + +.PATH: ${srcdir} +.PATH: ${srcdir}/lst.lib + +WFORMAT= 1 +OS!= uname -s +ARCH!= uname -m + +.if (${OS} == "NetBSD") && make(install) && exists(${DESTDIR}/usr/share/doc) +SUBDIR= PSD.doc +.endif + +.if (${OS} != "NetBSD" && ${OS} != "FreeBSD" && ${OS} != "OpenBSD") +# XXX not sure if we still want this given that configure +# lets us force or not the definition of MACHINE. +CFLAGS_main.o+= "-DFORCE_MACHINE=\"${MACHINE}\"" +NOMAN=no +SRCS+= getenv.c +INSTALL?=${srcdir}/install-sh +.if (${MACHINE} == "sun386") +# even I don't have one of these anymore :-) +CFLAGS+= -DPORTAR +.elif (${MACHINE} != "sunos") +SRCS+= sigcompat.c +CFLAGS+= -DSIGNAL_FLAGS=SA_RESTART +.endif +.endif + +CFLAGS_main.o+= "-D@force_machine@MACHINE=\"${MACHINE}\"" "-DMACHINE_ARCH=\"${MACHINE_ARCH}\"" + +EXTRACT_MAN=no + +MAN=${PROG}.1 +.if (${PROG} != "make") +${MAN}: make.1 + @echo making ${PROG}.1 + @sed -e '/^.Nm/s/make/${PROG}/' -e '/^.Sh HISTORY/,$$d' ${srcdir}/make.1 > $@ + @(echo ".Sh HISTORY"; echo ".Nm"; echo "is derrived from NetBSD's"; echo ".Xr make 1 ."; echo It uses autoconf to facilitate portability to other platforms.) >> $@ + +.endif + +.if exists(${srcdir}/../Makefile.inc) +.include "${srcdir}/../Makefile.inc" +.endif +.-include "prog.mk" +.ifdef OBJS +# prog.mk likely found. +.include "subdir.mk" +.else +.include "bsd.prog.mk" +.include "bsd.subdir.mk" +.endif + +# Force these +BINDIR= ${prefix}/bin +MANDIR= ${prefix}/man + +.if ${OS} == "FreeBSD" || ${OS} == "OpenBSD" +# freebsd's bsd.man.mk works differently +MAN1=${MAN} +MANDIR= ${prefix}/man/man +MANDEST= ${MANDIR}1 +.endif +MANDEST?= ${MANDIR} + +arch.o: config.h +# make sure that MAKE_VERSION gets updated. +main.o: ${SRCS} ${MAKEFILE} + +MK?=${prefix}/share/mk +MKSRC?=${srcdir}/mk + +beforeinstall: + test -d ${DESTDIR}${BINDIR} || ${INSTALL} -m 775 -d ${DESTDIR}${BINDIR} + test -d ${DESTDIR}${MANDEST} || ${INSTALL} -m 775 -d ${DESTDIR}${MANDEST} + +install-mk: +.if exists(${MKSRC}/bsd.prog.mk) + test -d ${DESTDIR}${MK} || ${INSTALL} -m 775 -d ${DESTDIR}${MK} + ${INSTALL} -m 644 ${MKSRC}/[ac-z]*.mk ${DESTDIR}${MK} + test -s ${DESTDIR}${MK}/bsd.own.mk || ${INSTALL} -m 644 ${MKSRC}/bsd*.mk ${DESTDIR}${MK} + test -s ${DESTDIR}${MK}/sys.mk || ${INSTALL} -m 644 mk/sys.mk ${DESTDIR}${MK} + test -s ${DESTDIR}${MK}/sys.mk || ${INSTALL} -m 644 ${MKSRC}/${MACHINE}.sys.mk ${DESTDIR}${MK}/sys.mk +.else + @echo need to unpack mk.tar.gz under ${srcdir} or set MKSRC; false +.endif diff --git a/bootstrap/bmake/PSD.doc/Makefile b/bootstrap/bmake/PSD.doc/Makefile new file mode 100644 index 00000000000..0500db65228 --- /dev/null +++ b/bootstrap/bmake/PSD.doc/Makefile @@ -0,0 +1,8 @@ +# $NetBSD: Makefile,v 1.1.1.1 2004/03/11 13:04:14 grant Exp $ +# @(#)Makefile 8.1 (Berkeley) 8/14/93 + +DIR= psd/12.make +SRCS= tutorial.ms +MACROS= -ms + +.include diff --git a/bootstrap/bmake/PSD.doc/tutorial.ms b/bootstrap/bmake/PSD.doc/tutorial.ms new file mode 100644 index 00000000000..6546db2fd38 --- /dev/null +++ b/bootstrap/bmake/PSD.doc/tutorial.ms @@ -0,0 +1,3744 @@ +.\" $NetBSD: tutorial.ms,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $ +.\" Copyright (c) 1988, 1989 by Adam de Boor +.\" Copyright (c) 1989 by Berkeley Softworks +.\" Copyright (c) 1988, 1989, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Adam de Boor. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)tutorial.ms 8.1 (Berkeley) 8/18/93 +.\" +.EH 'PSD:12-%''PMake \*- A Tutorial' +.OH 'PMake \*- A Tutorial''PSD:12-%' +.\" xH is a macro to provide numbered headers that are automatically stuffed +.\" into a table-of-contents, properly indented, etc. If the first argument +.\" is numeric, it is taken as the depth for numbering (as for .NH), else +.\" the default (1) is assumed. +.\" +.\" @P The initial paragraph distance. +.\" @Q The piece of section number to increment (or 0 if none given) +.\" @R Section header. +.\" @S Indent for toc entry +.\" @T Argument to NH (can't use @Q b/c giving 0 to NH resets the counter) +.de xH +.NH \\$1 +\\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 +.nr PD .1v +.XS \\n% +.ta 0.6i +\\*(SN \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 +.XE +.nr PD .3v +.. +.\" CW is used to place a string in fixed-width or switch to a +.\" fixed-width font. +.\" C is a typewriter font for a laserwriter. Use something else if +.\" you don't have one... +.de CW +.ie !\\n(.$ .ft C +.el \&\\$3\fC\\$1\fP\\$2 +.. +.\" Anything I put in a display I want to be in fixed-width +.am DS +.CW +.. +.\" The stuff in .No produces a little stop sign in the left margin +.\" that says NOTE in it. Unfortunately, it does cause a break, but +.\" hey. Can't have everything. In case you're wondering how I came +.\" up with such weird commands, they came from running grn on a +.\" gremlin file... +.de No +.br +.ne 0.5i +.po -0.5i +.br +.mk +.nr g3 \\n(.f +.nr g4 \\n(.s +.sp -1 +.\" .st cf +\D's -1u'\D't 5u' +.sp -1 +\h'50u'\D'l 71u 0u'\D'l 50u 50u'\D'l 0u 71u'\D'l -50u 50u'\D'l -71u 0u'\D'l -50u -50u'\D'l 0u -71u'\D'l 50u -50u' +.sp -1 +\D't 3u' +.sp -1 +.sp 7u +\h'53u'\D'p 14 68u 0u 46u 46u 0u 68u -46u 46u -68u 0u -47u -46u 0u -68u 47u -46u' +.sp -1 +.ft R +.ps 6 +.nr g8 \\n(.d +.ds g9 "NOTE +.sp 74u +\h'85u'\v'0.85n'\h-\w\\*(g9u/2u\&\\*(g9 +.sp |\\n(g8u +.sp 166u +\D't 3u'\D's -1u' +.br +.po +.rt +.ft \\n(g3 +.ps \\n(g4 +.. +.de Bp +.ie !\\n(.$ .IP \(bu 2 +.el .IP "\&" 2 +.. +.po +.3i +.TL +PMake \*- A Tutorial +.AU +Adam de Boor +.AI +Berkeley Softworks +2150 Shattuck Ave, Penthouse +Berkeley, CA 94704 +adam@bsw.uu.net +\&...!uunet!bsw!adam +.FS +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. +The University of California, Berkeley Softworks, and Adam de Boor make no +representations about the suitability of this software for any +purpose. It is provided "as is" without express or implied warranty. +.FE +.PP +.xH 1 Introduction +.LP +PMake is a program for creating other programs, or anything else you +can think of for it to do. The basic idea behind PMake is that, for +any given system, be it a program or a document or whatever, there +will be some files that depend on the state of other files (on when +they were last modified). PMake takes these dependencies, which you +must specify, and uses them to build whatever it is you want it to +build. +.LP +PMake is almost fully-compatible with Make, with which you may already +be familiar. PMake's most important feature is its ability to run +several different jobs at once, making the creation of systems +considerably faster. It also has a great deal more functionality than +Make. Throughout the text, whenever something is mentioned that is an +important difference between PMake and Make (i.e. something that will +cause a makefile to fail if you don't do something about it), or is +simply important, it will be flagged with a little sign in the left +margin, like this: +.No +.LP +This tutorial is divided into three main sections corresponding to basic, +intermediate and advanced PMake usage. If you already know Make well, +you will only need to skim chapter 2 (there are some aspects of +PMake that I consider basic to its use that didn't exist in Make). +Things in chapter 3 make life much easier, while those in chapter 4 +are strictly for those who know what they are doing. Chapter 5 has +definitions for the jargon I use and chapter 6 contains possible +solutions to the problems presented throughout the tutorial. +.xH 1 The Basics of PMake +.LP +PMake takes as input a file that tells a) which files depend on which +other files to be complete and b) what to do about files that are +``out-of-date.'' This file is known as a ``makefile'' and is usually +.Ix 0 def makefile +kept in the top-most directory of the system to be built. While you +can call the makefile anything you want, PMake will look for +.CW Makefile +and +.CW makefile +(in that order) in the current directory if you don't tell it +otherwise. +.Ix 0 def makefile default +To specify a different makefile, use the +.B \-f +flag (e.g. +.CW "pmake -f program.mk" ''). `` +.Ix 0 ref flags -f +.Ix 0 ref makefile other +.LP +A makefile has four different types of lines in it: +.RS +.IP \(bu 2 +File dependency specifications +.IP \(bu 2 +Creation commands +.IP \(bu 2 +Variable assignments +.IP \(bu 2 +Comments, include statements and conditional directives +.RE +.LP +Any line may be continued over multiple lines by ending it with a +backslash. +.Ix 0 def "continuation line" +The backslash, following newline and any initial whitespace +on the following line are compressed into a single space before the +input line is examined by PMake. +.xH 2 Dependency Lines +.LP +As mentioned in the introduction, in any system, there are +dependencies between the files that make up the system. For instance, +in a program made up of several C source files and one header file, +the C files will need to be re-compiled should the header file be +changed. For a document of several chapters and one macro file, the +chapters will need to be reprocessed if any of the macros changes. +.Ix 0 def "dependency" +These are dependencies and are specified by means of dependency lines in +the makefile. +.LP +.Ix 0 def "dependency line" +On a dependency line, there are targets and sources, separated by a +one- or two-character operator. +The targets ``depend'' on the sources and are usually created from +them. +.Ix 0 def target +.Ix 0 def source +.Ix 0 ref operator +Any number of targets and sources may be specified on a dependency line. +All the targets in the line are made to depend on all the sources. +Targets and sources need not be actual files, but every source must be +either an actual file or another target in the makefile. +If you run out of room, use a backslash at the end of the line to continue onto +the next one. +.LP +Any file may be a target and any file may be a source, but the +relationship between the two (or however many) is determined by the +``operator'' that separates them. +.Ix 0 def operator +Three types of operators exist: one specifies that the datedness of a +target is determined by the state of its sources, while another +specifies other files (the sources) that need to be dealt with before +the target can be re-created. The third operator is very similar to +the first, with the additional condition that the target is +out-of-date if it has no sources. These operations are represented by +the colon, the exclamation point and the double-colon, respectively, and are +mutually exclusive. Their exact semantics are as follows: +.IP ":" +.Ix 0 def operator colon +.Ix 0 def : +If a colon is used, a target on the line is considered to be +``out-of-date'' (and in need of creation) if +.RS +.IP \(bu 2 +any of the sources has been modified more recently than the target, or +.IP \(bu 2 +the target doesn't exist. +.RE +.Ix 0 def out-of-date +.IP "\&" +Under this operation, steps will be taken to re-create the target only +if it is found to be out-of-date by using these two rules. +.IP "!" +.Ix 0 def operator force +.Ix 0 def ! +If an exclamation point is used, the target will always be re-created, +but this will not happen until all of its sources have been examined +and re-created, if necessary. +.IP "::" +.Ix 0 def operator double-colon +.Ix 0 def :: +If a double-colon is used, a target is out-of-date if: +.RS +.IP \(bu 2 +any of the sources has been modified more recently than the target, or +.IP \(bu 2 +the target doesn't exist, or +.IP \(bu 2 +the target has no sources. +.RE +.IP "\&" +If the target is out-of-date according to these rules, it will be re-created. +This operator also does something else to the targets, but I'll go +into that in the next section (``Shell Commands''). +.LP +Enough words, now for an example. Take that C program I mentioned +earlier. Say there are three C files +.CW a.c , ( +.CW b.c +and +.CW c.c ) +each of which +includes the file +.CW defs.h . +The dependencies between the files could then be expressed as follows: +.DS +program : a.o b.o c.o +a.o b.o c.o : defs.h +a.o : a.c +b.o : b.c +c.o : c.c +.DE +.LP +You may be wondering at this point, where +.CW a.o , +.CW b.o +and +.CW c.o +came in and why +.I they +depend on +.CW defs.h +and the C files don't. The reason is quite simple: +.CW program +cannot be made by linking together .c files \*- it must be +made from .o files. Likewise, if you change +.CW defs.h , +it isn't the .c files that need to be re-created, it's the .o files. +If you think of dependencies in these terms \*- which files (targets) +need to be created from which files (sources) \*- you should have no problems. +.LP +An important thing to notice about the above example, is that all the +\&.o files appear as targets on more than one line. This is perfectly +all right: the target is made to depend on all the sources mentioned +on all the dependency lines. E.g. +.CW a.o +depends on both +.CW defs.h +and +.CW a.c . +.Ix 0 ref dependency +.No +.LP +The order of the dependency lines in the makefile is +important: the first target on the first dependency line in the +makefile will be the one that gets made if you don't say otherwise. +That's why +.CW program +comes first in the example makefile, above. +.LP +Both targets and sources may contain the standard C-Shell wildcard +characters +.CW { , ( +.CW } , +.CW * , +.CW ? , +.CW [ , +and +.CW ] ), +but the non-curly-brace ones may only appear in the final component +(the file portion) of the target or source. The characters mean the +following things: +.IP \fB{}\fP +These enclose a comma-separated list of options and cause the pattern +to be expanded once for each element of the list. Each expansion +contains a different element. For example, +.CW src/{whiffle,beep,fish}.c +expands to the three words +.CW src/whiffle.c , +.CW src/beep.c , +and +.CW src/fish.c . +These braces may be nested and, unlike the other wildcard characters, +the resulting words need not be actual files. All other wildcard +characters are expanded using the files that exist when PMake is +started. +.IP \fB*\fP +This matches zero or more characters of any sort. +.CW src/*.c +will expand to the same three words as above as long as +.CW src +contains those three files (and no other files that end in +.CW .c ). +.IP \fB?\fP +Matches any single character. +.IP \fB[]\fP +This is known as a character class and contains either a list of +single characters, or a series of character ranges +.CW a-z , ( +for example means all characters between a and z), or both. It matches +any single character contained in the list. E.g. +.CW [A-Za-z] +will match all letters, while +.CW [0123456789] +will match all numbers. +.xH 2 Shell Commands +.LP +``Isn't that nice,'' you say to yourself, ``but how are files +actually `re-created,' as he likes to spell it?'' +The re-creation is accomplished by commands you place in the makefile. +These commands are passed to the Bourne shell (better known as +``/bin/sh'') to be executed and are +.Ix 0 ref shell +.Ix 0 ref re-creation +.Ix 0 ref update +expected to do what's necessary to update the target file (PMake +doesn't actually check to see if the target was created. It just +assumes it's there). +.Ix 0 ref target +.LP +Shell commands in a makefile look a lot like shell commands you would +type at a terminal, with one important exception: each command in a +makefile +.I must +be preceded by at least one tab. +.LP +Each target has associated with it a shell script made up of +one or more of these shell commands. The creation script for a target +should immediately follow the dependency line for that target. While +any given target may appear on more than one dependency line, only one +of these dependency lines may be followed by a creation script, unless +the `::' operator was used on the dependency line. +.Ix 0 ref operator double-colon +.Ix 0 ref :: +.No +.LP +If the double-colon was used, each dependency line for the target +may be followed by a shell script. That script will only be executed +if the target on the associated dependency line is out-of-date with +respect to the sources on that line, according to the rules I gave +earlier. +I'll give you a good example of this later on. +.LP +To expand on the earlier makefile, you might add commands as follows: +.DS +program : a.o b.o c.o + cc a.o b.o c.o \-o program +a.o b.o c.o : defs.h +a.o : a.c + cc \-c a.c +b.o : b.c + cc \-c b.c +c.o : c.c + cc \-c c.c +.DE +.LP +Something you should remember when writing a makefile is, the +commands will be executed if the +.I target +on the dependency line is out-of-date, not the sources. +.Ix 0 ref target +.Ix 0 ref source +.Ix 0 ref out-of-date +In this example, the command +.CW "cc \-c a.c" '' `` +will be executed if +.CW a.o +is out-of-date. Because of the `:' operator, +.Ix 0 ref : +.Ix 0 ref operator colon +this means that should +.CW a.c +.I or +.CW defs.h +have been modified more recently than +.CW a.o , +the command will be executed +.CW a.o "\&" ( +will be considered out-of-date). +.Ix 0 ref out-of-date +.LP +Remember how I said the only difference between a makefile shell +command and a regular shell command was the leading tab? I lied. There +is another way in which makefile commands differ from regular ones. +The first two characters after the initial whitespace are treated +specially. +If they are any combination of `@' and `\-', they cause PMake to do +different things. +.LP +In most cases, shell commands are printed before they're +actually executed. This is to keep you informed of what's going on. If +an `@' appears, however, this echoing is suppressed. In the case of an +.CW echo +command, say +.CW "echo Linking index" ,'' `` +it would be +rather silly to see +.DS +echo Linking index +Linking index +.DE +.LP +so PMake allows you to place an `@' before the command +.CW "@echo Linking index" '') (`` +to prevent the command from being printed. +.LP +The other special character is the `\-'. In case you didn't know, +shell commands finish with a certain ``exit status.'' This status is +made available by the operating system to whatever program invoked the +command. Normally this status will be 0 if everything went ok and +non-zero if something went wrong. For this reason, PMake will consider +an error to have occurred if one of the shells it invokes returns a non-zero +status. When it detects an error, PMake's usual action is to abort +whatever it's doing and exit with a non-zero status itself (any other +targets that were being created will continue being made, but nothing +new will be started. PMake will exit after the last job finishes). +This behavior can be altered, however, by placing a `\-' at the front +of a command +.CW "\-mv index index.old" ''), (`` +certain command-line arguments, +or doing other things, to be detailed later. In such +a case, the non-zero status is simply ignored and PMake keeps chugging +along. +.No +.LP +Because all the commands are given to a single shell to execute, such +things as setting shell variables, changing directories, etc., last +beyond the command in which they are found. This also allows shell +compound commands (like +.CW for +loops) to be entered in a natural manner. +Since this could cause problems for some makefiles that depend on +each command being executed by a single shell, PMake has a +.B \-B +.Ix 0 ref compatibility +.Ix 0 ref flags -B +flag (it stands for backwards-compatible) that forces each command to +be given to a separate shell. It also does several other things, all +of which I discourage since they are now old-fashioned.\|.\|.\|. +.No +.LP +A target's shell script is fed to the shell on its (the shell's) input stream. +This means that any commands, such as +.CW ci +that need to get input from the terminal won't work right \*- they'll +get the shell's input, something they probably won't find to their +liking. A simple way around this is to give a command like this: +.DS +ci $(SRCS) < /dev/tty +.DE +This would force the program's input to come from the terminal. If you +can't do this for some reason, your only other alternative is to use +PMake in its fullest compatibility mode. See +.B Compatibility +in chapter 4. +.Ix 0 ref compatibility +.LP +.xH 2 Variables +.LP +PMake, like Make before it, has the ability to save text in variables +to be recalled later at your convenience. Variables in PMake are used +much like variables in the shell and, by tradition, consist of +all upper-case letters (you don't +.I have +to use all upper-case letters. +In fact there's nothing to stop you from calling a variable +.CW @^&$%$ . +Just tradition). Variables are assigned-to using lines of the form +.Ix 0 def variable assignment +.DS +VARIABLE = value +.DE +.Ix 0 def variable assignment +appended-to by +.DS +VARIABLE += value +.DE +.Ix 0 def variable appending +.Ix 0 def variable assignment appended +.Ix 0 def += +conditionally assigned-to (if the variable isn't already defined) by +.DS +VARIABLE ?= value +.DE +.Ix 0 def variable assignment conditional +.Ix 0 def ?= +and assigned-to with expansion (i.e. the value is expanded (see below) +before being assigned to the variable\*-useful for placing a value at +the beginning of a variable, or other things) by +.DS +VARIABLE := value +.DE +.Ix 0 def variable assignment expanded +.Ix 0 def := +.LP +Any whitespace before +.I value +is stripped off. When appending, a space is placed between the old +value and the stuff being appended. +.LP +The final way a variable may be assigned to is using +.DS +VARIABLE != shell-command +.DE +.Ix 0 def variable assignment shell-output +.Ix 0 def != +In this case, +.I shell-command +has all its variables expanded (see below) and is passed off to a +shell to execute. The output of the shell is then placed in the +variable. Any newlines (other than the final one) are replaced by +spaces before the assignment is made. This is typically used to find +the current directory via a line like: +.DS +CWD != pwd +.DE +.LP +.B Note: +this is intended to be used to execute commands that produce small amounts +of output (e.g. ``pwd''). The implementation is less than intelligent and will +likely freeze if you execute something that produces thousands of +bytes of output (8 Kb is the limit on many UNIX systems). +.LP +The value of a variable may be retrieved by enclosing the variable +name in parentheses or curly braces and preceding the whole thing +with a dollar sign. +.LP +For example, to set the variable CFLAGS to the string +.CW "\-I/sprite/src/lib/libc \-O" ,'' `` +you would place a line +.DS +CFLAGS = \-I/sprite/src/lib/libc \-O +.DE +in the makefile and use the word +.CW "$(CFLAGS)" +wherever you would like the string +.CW "\-I/sprite/src/lib/libc \-O" +to appear. This is called variable expansion. +.Ix 0 def variable expansion +.No +.LP +Unlike Make, PMake will not expand a variable unless it knows +the variable exists. E.g. if you have a +.CW "${i}" +in a shell command and you have not assigned a value to the variable +.CW i +(the empty string is considered a value, by the way), where Make would have +substituted the empty string, PMake will leave the +.CW "${i}" +alone. +To keep PMake from substituting for a variable it knows, precede the +dollar sign with another dollar sign. +(e.g. to pass +.CW "${HOME}" +to the shell, use +.CW "$${HOME}" ). +This causes PMake, in effect, to expand the +.CW $ +macro, which expands to a single +.CW $ . +For compatibility, Make's style of variable expansion will be used +if you invoke PMake with any of the compatibility flags (\c +.B \-V , +.B \-B +or +.B \-M . +The +.B \-V +flag alters just the variable expansion). +.Ix 0 ref flags -V +.Ix 0 ref flags -B +.Ix 0 ref flags -M +.Ix 0 ref compatibility +.LP +.Ix 0 ref variable expansion +There are two different times at which variable expansion occurs: +When parsing a dependency line, the expansion occurs immediately +upon reading the line. If any variable used on a dependency line is +undefined, PMake will print a message and exit. +Variables in shell commands are expanded when the command is +executed. +Variables used inside another variable are expanded whenever the outer +variable is expanded (the expansion of an inner variable has no effect +on the outer variable. I.e. if the outer variable is used on a dependency +line and in a shell command, and the inner variable changes value +between when the dependency line is read and the shell command is +executed, two different values will be substituted for the outer +variable). +.Ix 0 def variable types +.LP +Variables come in four flavors, though they are all expanded the same +and all look about the same. They are (in order of expanding scope): +.RS +.IP \(bu 2 +Local variables. +.Ix 0 ref variable local +.IP \(bu 2 +Command-line variables. +.Ix 0 ref variable command-line +.IP \(bu 2 +Global variables. +.Ix 0 ref variable global +.IP \(bu 2 +Environment variables. +.Ix 0 ref variable environment +.RE +.LP +The classification of variables doesn't matter much, except that the +classes are searched from the top (local) to the bottom (environment) +when looking up a variable. The first one found wins. +.xH 3 Local Variables +.LP +.Ix 0 def variable local +Each target can have as many as seven local variables. These are +variables that are only ``visible'' within that target's shell script +and contain such things as the target's name, all of its sources (from +all its dependency lines), those sources that were out-of-date, etc. +Four local variables are defined for all targets. They are: +.RS +.IP ".TARGET" +.Ix 0 def variable local .TARGET +.Ix 0 def .TARGET +The name of the target. +.IP ".OODATE" +.Ix 0 def variable local .OODATE +.Ix 0 def .OODATE +The list of the sources for the target that were considered out-of-date. +The order in the list is not guaranteed to be the same as the order in +which the dependencies were given. +.IP ".ALLSRC" +.Ix 0 def variable local .ALLSRC +.Ix 0 def .ALLSRC +The list of all sources for this target in the order in which they +were given. +.IP ".PREFIX" +.Ix 0 def variable local .PREFIX +.Ix 0 def .PREFIX +The target without its suffix and without any leading path. E.g. for +the target +.CW ../../lib/compat/fsRead.c , +this variable would contain +.CW fsRead . +.RE +.LP +Three other local variables are set only for certain targets under +special circumstances. These are the ``.IMPSRC,'' +.Ix 0 ref variable local .IMPSRC +.Ix 0 ref .IMPSRC +``.ARCHIVE,'' +.Ix 0 ref variable local .ARCHIVE +.Ix 0 ref .ARCHIVE +and ``.MEMBER'' +.Ix 0 ref variable local .MEMBER +.Ix 0 ref .MEMBER +variables. When they are set and how they are used is described later. +.LP +Four of these variables may be used in sources as well as in shell +scripts. +.Ix 0 def "dynamic source" +.Ix 0 def source dynamic +These are ``.TARGET'', ``.PREFIX'', ``.ARCHIVE'' and ``.MEMBER''. The +variables in the sources are expanded once for each target on the +dependency line, providing what is known as a ``dynamic source,'' +.Rd 0 +allowing you to specify several dependency lines at once. For example, +.DS +$(OBJS) : $(.PREFIX).c +.DE +will create a dependency between each object file and its +corresponding C source file. +.xH 3 Command-line Variables +.LP +.Ix 0 def variable command-line +Command-line variables are set when PMake is first invoked by giving a +variable assignment as one of the arguments. For example, +.DS +pmake "CFLAGS = -I/sprite/src/lib/libc -O" +.DE +would make +.CW CFLAGS +be a command-line variable with the given value. Any assignments to +.CW CFLAGS +in the makefile will have no effect, because once it +is set, there is (almost) nothing you can do to change a command-line +variable (the search order, you see). Command-line variables may be +set using any of the four assignment operators, though only +.CW = +and +.CW ?= +behave as you would expect them to, mostly because assignments to +command-line variables are performed before the makefile is read, thus +the values set in the makefile are unavailable at the time. +.CW += +.Ix 0 ref += +.Ix 0 ref variable assignment appended +is the same as +.CW = , +because the old value of the variable is sought only in the scope in +which the assignment is taking place (for reasons of efficiency that I +won't get into here). +.CW := +and +.CW ?= +.Ix 0 ref := +.Ix 0 ref ?= +.Ix 0 ref variable assignment expanded +.Ix 0 ref variable assignment conditional +will work if the only variables used are in the environment. +.CW != +is sort of pointless to use from the command line, since the same +effect can no doubt be accomplished using the shell's own command +substitution mechanisms (backquotes and all that). +.xH 3 Global Variables +.LP +.Ix 0 def variable global +Global variables are those set or appended-to in the makefile. +There are two classes of global variables: those you set and those PMake sets. +As I said before, the ones you set can have any name you want them to have, +except they may not contain a colon or an exclamation point. +The variables PMake sets (almost) always begin with a +period and always contain upper-case letters, only. The variables are +as follows: +.RS +.IP .PMAKE +.Ix 0 def variable global .PMAKE +.Ix 0 def .PMAKE +.Ix 0 def variable global MAKE +.Ix 0 def MAKE +The name by which PMake was invoked is stored in this variable. For +compatibility, the name is also stored in the MAKE variable. +.IP .MAKEFLAGS +.Ix 0 def variable global .MAKEFLAGS +.Ix 0 def .MAKEFLAGS variable +.Ix 0 def variable global MFLAGS +.Ix 0 def MFLAGS +All the relevant flags with which PMake was invoked. This does not +include such things as +.B \-f +or variable assignments. Again for compatibility, this value is stored +in the MFLAGS variable as well. +.RE +.LP +Two other variables, ``.INCLUDES'' and ``.LIBS,'' are covered in the +section on special targets in chapter 3. +.Ix 0 ref variable global .INCLUDES +.Ix 0 ref variable global .LIBS +.LP +Global variables may be deleted using lines of the form: +.Ix 0 def #undef +.Ix 0 def variable deletion +.DS +#undef \fIvariable\fP +.DE +The +.CW # ' ` +must be the first character on the line. Note that this may only be +done on global variables. +.xH 3 Environment Variables +.LP +.Ix 0 def variable environment +Environment variables are passed by the shell that invoked PMake and +are given by PMake to each shell it invokes. They are expanded like +any other variable, but they cannot be altered in any way. +.LP +One special environment variable, +.CW PMAKE , +.Ix 0 def variable environment PMAKE +is examined by PMake for command-line flags, variable assignments, +etc., it should always use. This variable is examined before the +actual arguments to PMake are. In addition, all flags given to PMake, +either through the +.CW PMAKE +variable or on the command line, are placed in this environment +variable and exported to each shell PMake executes. Thus recursive +invocations of PMake automatically receive the same flags as the +top-most one. +.LP +Using all these variables, you can compress the sample makefile even more: +.DS +OBJS = a.o b.o c.o +program : $(OBJS) + cc $(.ALLSRC) \-o $(.TARGET) +$(OBJS) : defs.h +a.o : a.c + cc \-c a.c +b.o : b.c + cc \-c b.c +c.o : c.c + cc \-c c.c +.DE +.Ix 0 ref variable local .ALLSRC +.Ix 0 ref .ALLSRC +.Ix 0 ref variable local .TARGET +.Ix 0 ref .TARGET +.Rd 3 +.xH 2 Comments +.LP +.Ix 0 def comments +Comments in a makefile start with a `#' character and extend to the +end of the line. They may appear +anywhere you want them, except in a shell command (though the shell +will treat it as a comment, too). If, for some reason, you need to use the `#' +in a variable or on a dependency line, put a backslash in front of it. +PMake will compress the two into a single `#' (Note: this isn't true +if PMake is operating in full-compatibility mode). +.Ix 0 ref flags -M +.Ix 0 ref compatibility +.xH 2 Parallelism +.No +.LP +PMake was specifically designed to re-create several targets at once, +when possible. You do not have to do anything special to cause this to +happen (unless PMake was configured to not act in parallel, in which +case you will have to make use of the +.B \-L +and +.B \-J +flags (see below)), +.Ix 0 ref flags -L +.Ix 0 ref flags -J +but you do have to be careful at times. +.LP +There are several problems you are likely to encounter. One is +that some makefiles (and programs) are written in such a way that it is +impossible for two targets to be made at once. The program +.CW xstr , +for example, +always modifies the files +.CW strings +and +.CW x.c . +There is no way to change it. Thus you cannot run two of them at once +without something being trashed. Similarly, if you have commands +in the makefile that always send output to the same file, you will not +be able to make more than one target at once unless you change the +file you use. You can, for instance, add a +.CW $$$$ +to the end of the file name to tack on the process ID of the shell +executing the command (each +.CW $$ +expands to a single +.CW $ , +thus giving you the shell variable +.CW $$ ). +Since only one shell is used for all the +commands, you'll get the same file name for each command in the +script. +.LP +The other problem comes from improperly-specified dependencies that +worked in Make because of its sequential, depth-first way of examining +them. While I don't want to go into depth on how PMake +works (look in chapter 4 if you're interested), I will warn you that +files in two different ``levels'' of the dependency tree may be +examined in a different order in PMake than they were in Make. For +example, given the makefile +.DS +a : b c +b : d +.DE +PMake will examine the targets in the order +.CW c , +.CW d , +.CW b , +.CW a . +If the makefile's author expected PMake to abort before making +.CW c +if an error occurred while making +.CW b , +or if +.CW b +needed to exist before +.CW c +was made, +s/he will be sorely disappointed. The dependencies are +incomplete, since in both these cases, +.CW c +would depend on +.CW b . +So watch out. +.LP +Another problem you may face is that, while PMake is set up to handle the +output from multiple jobs in a graceful fashion, the same is not so for input. +It has no way to regulate input to different jobs, +so if you use the redirection from +.CW /dev/tty +I mentioned earlier, you must be careful not to run two of the jobs at once. +.xH 2 Writing and Debugging a Makefile +.LP +Now you know most of what's in a makefile, what do you do next? There +are two choices: (1) use one of the uncommonly-available makefile +generators or (2) write your own makefile (I leave out the third choice of +ignoring PMake and doing everything by hand as being beyond the bounds +of common sense). +.LP +When faced with the writing of a makefile, it is usually best to start +from first principles: just what +.I are +you trying to do? What do you want the makefile finally to produce? +.LP +To begin with a somewhat traditional example, let's say you need to +write a makefile to create a program, +.CW expr , +that takes standard infix expressions and converts them to prefix form (for +no readily apparent reason). You've got three source files, in C, that +make up the program: +.CW main.c , +.CW parse.c , +and +.CW output.c . +Harking back to my pithy advice about dependency lines, you write the +first line of the file: +.DS +expr : main.o parse.o output.o +.DE +because you remember +.CW expr +is made from +.CW .o +files, not +.CW .c +files. Similarly for the +.CW .o +files you produce the lines: +.DS +main.o : main.c +parse.o : parse.c +output.o : output.c +main.o parse.o output.o : defs.h +.DE +.LP +Great. You've now got the dependencies specified. What you need now is +commands. These commands, remember, must produce the target on the +dependency line, usually by using the sources you've listed. +You remember about local variables? Good, so it should come +to you as no surprise when you write +.DS +expr : main.o parse.o output.o + cc -o $(.TARGET) $(.ALLSRC) +.DE +Why use the variables? If your program grows to produce postfix +expressions too (which, of course, requires a name change or two), it +is one fewer place you have to change the file. You cannot do this for +the object files, however, because they depend on their corresponding +source files +.I and +.CW defs.h , +thus if you said +.DS + cc -c $(.ALLSRC) +.DE +you'd get (for +.CW main.o ): +.DS + cc -c main.c defs.h +.DE +which is wrong. So you round out the makefile with these lines: +.DS +main.o : main.c + cc -c main.c +parse.o : parse.c + cc -c parse.c +output.o : output.c + cc -c output.c +.DE +.LP +The makefile is now complete and will, in fact, create the program you +want it to without unnecessary compilations or excessive typing on +your part. There are two things wrong with it, however (aside from it +being altogether too long, something I'll address in chapter 3): +.IP 1) +The string +.CW "main.o parse.o output.o" '' `` +is repeated twice, necessitating two changes when you add postfix +(you were planning on that, weren't you?). This is in direct violation +of de Boor's First Rule of writing makefiles: +.QP +.I +Anything that needs to be written more than once +should be placed in a variable. +.IP "\&" +I cannot emphasize this enough as being very important to the +maintenance of a makefile and its program. +.IP 2) +There is no way to alter the way compilations are performed short of +editing the makefile and making the change in all places. This is evil +and violates de Boor's Second Rule, which follows directly from the +first: +.QP +.I +Any flags or programs used inside a makefile should be placed in a variable so +they may be changed, temporarily or permanently, with the greatest ease. +.LP +The makefile should more properly read: +.DS +OBJS = main.o parse.o output.o +expr : $(OBJS) + $(CC) $(CFLAGS) -o $(.TARGET) $(.ALLSRC) +main.o : main.c + $(CC) $(CFLAGS) -c main.c +parse.o : parse.c + $(CC) $(CFLAGS) -c parse.c +output.o : output.c + $(CC) $(CFLAGS) -c output.c +$(OBJS) : defs.h +.DE +Alternatively, if you like the idea of dynamic sources mentioned in +section 2.3.1, +.Rm 0 2.3.1 +.Rd 4 +.Ix 0 ref "dynamic source" +.Ix 0 ref source dynamic +you could write it like this: +.DS +OBJS = main.o parse.o output.o +expr : $(OBJS) + $(CC) $(CFLAGS) -o $(.TARGET) $(.ALLSRC) +$(OBJS) : $(.PREFIX).c defs.h + $(CC) $(CFLAGS) -c $(.PREFIX).c +.DE +These two rules and examples lead to de Boor's First Corollary: +.QP +.I +Variables are your friends. +.LP +Once you've written the makefile comes the sometimes-difficult task of +.Ix 0 ref debugging +making sure the darn thing works. Your most helpful tool to make sure +the makefile is at least syntactically correct is the +.B \-n +.Ix 0 ref flags -n +flag, which allows you to see if PMake will choke on the makefile. The +second thing the +.B \-n +flag lets you do is see what PMake would do without it actually doing +it, thus you can make sure the right commands would be executed were +you to give PMake its head. +.LP +When you find your makefile isn't behaving as you hoped, the first +question that comes to mind (after ``What time is it, anyway?'') is +``Why not?'' In answering this, two flags will serve you well: +.CW "-d m" '' `` +.Ix 0 ref flags -d +and +.CW "-p 2" .'' `` +.Ix 0 ref flags -p +The first causes PMake to tell you as it examines each target in the +makefile and indicate why it is deciding whatever it is deciding. You +can then use the information printed for other targets to see where +you went wrong. The +.CW "-p 2" '' `` +flag makes PMake print out its internal state when it is done, +allowing you to see that you forgot to make that one chapter depend on +that file of macros you just got a new version of. The output from +.CW "-p 2" '' `` +is intended to resemble closely a real makefile, but with additional +information provided and with variables expanded in those commands +PMake actually printed or executed. +.LP +Something to be especially careful about is circular dependencies. +.Ix 0 def dependency circular +E.g. +.DS +a : b +b : c d +d : a +.DE +In this case, because of how PMake works, +.CW c +is the only thing PMake will examine, because +.CW d +and +.CW a +will effectively fall off the edge of the universe, making it +impossible to examine +.CW b +(or them, for that matter). +PMake will tell you (if run in its normal mode) all the targets +involved in any cycle it looked at (i.e. if you have two cycles in the +graph (naughty, naughty), but only try to make a target in one of +them, PMake will only tell you about that one. You'll have to try to +make the other to find the second cycle). When run as Make, it will +only print the first target in the cycle. +.xH 2 Invoking PMake +.LP +.Ix 0 ref flags +.Ix 0 ref arguments +.Ix 0 ref usage +PMake comes with a wide variety of flags to choose from. +They may appear in any order, interspersed with command-line variable +assignments and targets to create. +The flags are as follows: +.IP "\fB\-d\fP \fIwhat\fP" +.Ix 0 def flags -d +.Ix 0 ref debugging +This causes PMake to spew out debugging information that +may prove useful to you. If you can't +figure out why PMake is doing what it's doing, you might try using +this flag. The +.I what +parameter is a string of single characters that tell PMake what +aspects you are interested in. Most of what I describe will make +little sense to you, unless you've dealt with Make before. Just +remember where this table is and come back to it as you read on. +The characters and the information they produce are as follows: +.RS +.IP a +Archive searching and caching. +.IP c +Conditional evaluation. +.IP d +The searching and caching of directories. +.IP j +Various snippets of information related to the running of the multiple +shells. Not particularly interesting. +.IP m +The making of each target: what target is being examined; when it was +last modified; whether it is out-of-date; etc. +.IP p +Makefile parsing. +.IP r +Remote execution. +.IP s +The application of suffix-transformation rules. (See chapter 3) +.IP t +The maintenance of the list of targets. +.IP v +Variable assignment. +.RE +.IP "\&" +Of these all, the +.CW m +and +.CW s +letters will be most useful to you. +If the +.B \-d +is the final argument or the argument from which it would get these +key letters (see below for a note about which argument would be used) +begins with a +.B \- , +all of these debugging flags will be set, resulting in massive amounts +of output. +.IP "\fB\-f\fP \fImakefile\fP" +.Ix 0 def flags -f +Specify a makefile to read different from the standard makefiles +.CW Makefile "\&" ( +or +.CW makefile ). +.Ix 0 ref makefile default +.Ix 0 ref makefile other +If +.I makefile +is ``\-'', PMake uses the standard input. This is useful for making +quick and dirty makefiles.\|.\|. +.Ix 0 ref makefile "quick and dirty" +.IP \fB\-h\fP +.Ix 0 def flags -h +Prints out a summary of the various flags PMake accepts. It can also +be used to find out what level of concurrency was compiled into the +version of PMake you are using (look at +.B \-J +and +.B \-L ) +and various other information on how PMake was configured. +.Ix 0 ref configuration +.Ix 0 ref makefile system +.IP \fB\-i\fP +.Ix 0 def flags -i +If you give this flag, PMake will ignore non-zero status returned +by any of its shells. It's like placing a `\-' before all the commands +in the makefile. +.IP \fB\-k\fP +.Ix 0 def flags -k +This is similar to +.B \-i +in that it allows PMake to continue when it sees an error, but unlike +.B \-i , +where PMake continues blithely as if nothing went wrong, +.B \-k +causes it to recognize the error and only continue work on those +things that don't depend on the target, either directly or indirectly (through +depending on something that depends on it), whose creation returned the error. +The `k' is for ``keep going''.\|.\|. +.Ix 0 ref target +.IP \fB\-l\fP +.Ix 0 def flags -l +PMake has the ability to lock a directory against other +people executing it in the same directory (by means of a file called +``LOCK.make'' that it creates and checks for in the directory). This +is a Good Thing because two people doing the same thing in the same place +can be disastrous for the final product (too many cooks and all that). +Whether this locking is the default is up to your system +administrator. If locking is on, +.B \-l +will turn it off, and vice versa. Note that this locking will not +prevent \fIyou\fP from invoking PMake twice in the same place \*- if +you own the lock file, PMake will warn you about it but continue to execute. +.IP "\fB\-m\fP \fIdirectory\fP" +.Ix 0 def flags -m +Tells PMake another place to search for included makefiles via the <...> +style. Several +.B \-m +options can be given to form a search path. If this construct is used the +default system makefile search path is completely overridden. +To be explained in chapter 3, section 3.2. +.Rm 2 3.2 +.IP \fB\-n\fP +.Ix 0 def flags -n +This flag tells PMake not to execute the commands needed to update the +out-of-date targets in the makefile. Rather, PMake will simply print +the commands it would have executed and exit. This is particularly +useful for checking the correctness of a makefile. If PMake doesn't do +what you expect it to, it's a good chance the makefile is wrong. +.IP "\fB\-p\fP \fInumber\fP" +.Ix 0 def flags -p +.Ix 0 ref debugging +This causes PMake to print its input in a reasonable form, though +not necessarily one that would make immediate sense to anyone but me. The +.I number +is a bitwise-or of 1 and 2 where 1 means it should print the input +before doing any processing and 2 says it should print it after +everything has been re-created. Thus +.CW "\-p 3" +would print it twice\*-once before processing and once after (you +might find the difference between the two interesting). This is mostly +useful to me, but you may find it informative in some bizarre circumstances. +.IP \fB\-q\fP +.Ix 0 def flags -q +If you give PMake this flag, it will not try to re-create anything. It +will just see if anything is out-of-date and exit non-zero if so. +.IP \fB\-r\fP +.Ix 0 def flags -r +When PMake starts up, it reads a default makefile that tells it what +sort of system it's on and gives it some idea of what to do if you +don't tell it anything. I'll tell you about it in chapter 3. If you +give this flag, PMake won't read the default makefile. +.IP \fB\-s\fP +.Ix 0 def flags -s +This causes PMake to not print commands before they're executed. It +is the equivalent of putting an `@' before every command in the +makefile. +.IP \fB\-t\fP +.Ix 0 def flags -t +Rather than try to re-create a target, PMake will simply ``touch'' it +so as to make it appear up-to-date. If the target didn't exist before, +it will when PMake finishes, but if the target did exist, it will +appear to have been updated. +.IP \fB\-v\fP +.Ix 0 def flags -v +This is a mixed-compatibility flag intended to mimic the System V +version of Make. It is the same as giving +.B \-B , +and +.B \-V +as well as turning off directory locking. Targets can still be created +in parallel, however. This is the mode PMake will enter if it is +invoked either as +.CW smake '' `` +or +.CW vmake ''. `` +.IP \fB\-x\fP +.Ix 0 def flags -x +This tells PMake it's ok to export jobs to other machines, if they're +available. It is used when running in Make mode, as exporting in this +mode tends to make things run slower than if the commands were just +executed locally. +.IP \fB\-B\fP +.Ix 0 ref compatibility +.Ix 0 def flags -B +Forces PMake to be as backwards-compatible with Make as possible while +still being itself. +This includes: +.RS +.IP \(bu 2 +Executing one shell per shell command +.IP \(bu 2 +Expanding anything that looks even vaguely like a variable, with the +empty string replacing any variable PMake doesn't know. +.IP \(bu 2 +Refusing to allow you to escape a `#' with a backslash. +.IP \(bu 2 +Permitting undefined variables on dependency lines and conditionals +(see below). Normally this causes PMake to abort. +.RE +.IP \fB\-C\fP +.Ix 0 def flags -C +This nullifies any and all compatibility mode flags you may have given +or implied up to the time the +.B \-C +is encountered. It is useful mostly in a makefile that you wrote for PMake +to avoid bad things happening when someone runs PMake as +.CW make '' `` +or has things set in the environment that tell it to be compatible. +.B \-C +is +.I not +placed in the +.CW PMAKE +environment variable or the +.CW .MAKEFLAGS +or +.CW MFLAGS +global variables. +.Ix 0 ref variable environment PMAKE +.Ix 0 ref variable global .MAKEFLAGS +.Ix 0 ref variable global MFLAGS +.Ix 0 ref .MAKEFLAGS variable +.Ix 0 ref MFLAGS +.IP "\fB\-D\fP \fIvariable\fP" +.Ix 0 def flags -D +Allows you to define a variable to have +.CW 1 '' `` +as its value. The variable is a global variable, not a command-line +variable. This is useful mostly for people who are used to the C +compiler arguments and those using conditionals, which I'll get into +in section 4.3 +.Rm 1 4.3 +.IP "\fB\-I\fP \fIdirectory\fP" +.Ix 0 def flags -I +Tells PMake another place to search for included makefiles. Yet +another thing to be explained in chapter 3 (section 3.2, to be +precise). +.Rm 2 3.2 +.IP "\fB\-J\fP \fInumber\fP" +.Ix 0 def flags -J +Gives the absolute maximum number of targets to create at once on both +local and remote machines. +.IP "\fB\-L\fP \fInumber\fP" +.Ix 0 def flags -L +This specifies the maximum number of targets to create on the local +machine at once. This may be 0, though you should be wary of doing +this, as PMake may hang until a remote machine becomes available, if +one is not available when it is started. +.IP \fB\-M\fP +.Ix 0 ref compatibility +.Ix 0 def flags -M +This is the flag that provides absolute, complete, full compatibility +with Make. It still allows you to use all but a few of the features of +PMake, but it is non-parallel. This is the mode PMake enters if you +call it +.CW make .'' `` +.IP \fB\-P\fP +.Ix 0 def flags -P +.Ix 0 ref "output control" +When creating targets in parallel, several shells are executing at +once, each wanting to write its own two cent's-worth to the screen. +This output must be captured by PMake in some way in order to prevent +the screen from being filled with garbage even more indecipherable +than you usually see. PMake has two ways of doing this, one of which +provides for much cleaner output and a clear separation between the +output of different jobs, the other of which provides a more immediate +response so one can tell what is really happpening. The former is done +by notifying you when the creation of a target starts, capturing the +output and transferring it to the screen all at once when the job +finishes. The latter is done by catching the output of the shell (and +its children) and buffering it until an entire line is received, then +printing that line preceded by an indication of which job produced +the output. Since I prefer this second method, it is the one used by +default. The first method will be used if you give the +.B \-P +flag to PMake. +.IP \fB\-V\fP +.Ix 0 def flags -V +As mentioned before, the +.B \-V +flag tells PMake to use Make's style of expanding variables, +substituting the empty string for any variable it doesn't know. +.IP \fB\-W\fP +.Ix 0 def flags -W +There are several times when PMake will print a message at you that is +only a warning, i.e. it can continue to work in spite of your having +done something silly (such as forgotten a leading tab for a shell +command). Sometimes you are well aware of silly things you have done +and would like PMake to stop bothering you. This flag tells it to shut +up about anything non-fatal. +.IP \fB\-X\fP +.Ix 0 def flags -X +This flag causes PMake to not attempt to export any jobs to another +machine. +.LP +Several flags may follow a single `\-'. Those flags that require +arguments take them from successive parameters. E.g. +.DS +pmake -fDnI server.mk DEBUG /chip2/X/server/include +.DE +will cause PMake to read +.CW server.mk +as the input makefile, define the variable +.CW DEBUG +as a global variable and look for included makefiles in the directory +.CW /chip2/X/server/include . +.xH 2 Summary +.LP +A makefile is made of four types of lines: +.RS +.IP \(bu 2 +Dependency lines +.IP \(bu 2 +Creation commands +.IP \(bu 2 +Variable assignments +.IP \(bu 2 +Comments, include statements and conditional directives +.RE +.LP +A dependency line is a list of one or more targets, an operator +.CW : ', (` +.CW :: ', ` +or +.CW ! '), ` +and a list of zero or more sources. Sources may contain wildcards and +certain local variables. +.LP +A creation command is a regular shell command preceded by a tab. In +addition, if the first two characters after the tab (and other +whitespace) are a combination of +.CW @ ' ` +or +.CW - ', ` +PMake will cause the command to not be printed (if the character is +.CW @ ') ` +or errors from it to be ignored (if +.CW - '). ` +A blank line, dependency line or variable assignment terminates a +creation script. There may be only one creation script for each target +with a +.CW : ' ` +or +.CW ! ' ` +operator. +.LP +Variables are places to store text. They may be unconditionally +assigned-to using the +.CW = ' ` +.Ix 0 ref = +.Ix 0 ref variable assignment +operator, appended-to using the +.CW += ' ` +.Ix 0 ref += +.Ix 0 ref variable assignment appended +operator, conditionally (if the variable is undefined) assigned-to +with the +.CW ?= ' ` +.Ix 0 ref ?= +.Ix 0 ref variable assignment conditional +operator, and assigned-to with variable expansion with the +.CW := ' ` +.Ix 0 ref := +.Ix 0 ref variable assignment expanded +operator. The output of a shell command may be assigned to a variable +using the +.CW != ' ` +.Ix 0 ref != +.Ix 0 ref variable assignment shell-output +operator. Variables may be expanded (their value inserted) by enclosing +their name in parentheses or curly braces, preceded by a dollar sign. +A dollar sign may be escaped with another dollar sign. Variables are +not expanded if PMake doesn't know about them. There are seven local +variables: +.CW .TARGET , +.CW .ALLSRC , +.CW .OODATE , +.CW .PREFIX , +.CW .IMPSRC , +.CW .ARCHIVE , +and +.CW .MEMBER . +Four of them +.CW .TARGET , ( +.CW .PREFIX , +.CW .ARCHIVE , +and +.CW .MEMBER ) +may be used to specify ``dynamic sources.'' +.Ix 0 ref "dynamic source" +.Ix 0 ref source dynamic +Variables are good. Know them. Love them. Live them. +.LP +Debugging of makefiles is best accomplished using the +.B \-n , +.B "\-d m" , +and +.B "\-p 2" +flags. +.xH 2 Exercises +.ce +\s+4\fBTBA\fP\s0 +.xH 1 Short-cuts and Other Nice Things +.LP +Based on what I've told you so far, you may have gotten the impression +that PMake is just a way of storing away commands and making sure you +don't forget to compile something. Good. That's just what it is. +However, the ways I've described have been inelegant, at best, and +painful, at worst. +This chapter contains things that make the +writing of makefiles easier and the makefiles themselves shorter and +easier to modify (and, occasionally, simpler). In this chapter, I +assume you are somewhat more +familiar with Sprite (or UNIX, if that's what you're using) than I did +in chapter 2, just so you're on your toes. +So without further ado... +.xH 2 Transformation Rules +.LP +As you know, a file's name consists of two parts: a base name, which +gives some hint as to the contents of the file, and a suffix, which +usually indicates the format of the file. +Over the years, as +.UX +has developed, +naming conventions, with regard to suffixes, have also developed that have +become almost as incontrovertible as Law. E.g. a file ending in +.CW .c +is assumed to contain C source code; one with a +.CW .o +suffix is assumed to be a compiled, relocatable object file that may +be linked into any program; a file with a +.CW .ms +suffix is usually a text file to be processed by Troff with the \-ms +macro package, and so on. +One of the best aspects of both Make and PMake comes from their +understanding of how the suffix of a file pertains to its contents and +their ability to do things with a file based soley on its suffix. This +ability comes from something known as a transformation rule. A +transformation rule specifies how to change a file with one suffix +into a file with another suffix. +.LP +A transformation rule looks much like a dependency line, except the +target is made of two known suffixes stuck together. Suffixes are made +known to PMake by placing them as sources on a dependency line whose +target is the special target +.CW .SUFFIXES . +E.g. +.DS +\&.SUFFIXES : .o .c +\&.c.o : + $(CC) $(CFLAGS) -c $(.IMPSRC) +.DE +The creation script attached to the target is used to transform a file with +the first suffix (in this case, +.CW .c ) +into a file with the second suffix (here, +.CW .o ). +In addition, the target inherits whatever attributes have been applied +to the transformation rule. +The simple rule given above says that to transform a C source file +into an object file, you compile it using +.CW cc +with the +.CW \-c +flag. +This rule is taken straight from the system makefile. Many +transformation rules (and suffixes) are defined there, and I refer you +to it for more examples (type +.CW "pmake -h" '' `` +to find out where it is). +.LP +There are several things to note about the transformation rule given +above: +.RS +.IP 1) +The +.CW .IMPSRC +variable. +.Ix 0 def variable local .IMPSRC +.Ix 0 def .IMPSRC +This variable is set to the ``implied source'' (the file from which +the target is being created; the one with the first suffix), which, in this +case, is the .c file. +.IP 2) +The +.CW CFLAGS +variable. Almost all of the transformation rules in the system +makefile are set up using variables that you can alter in your +makefile to tailor the rule to your needs. In this case, if you want +all your C files to be compiled with the +.B \-g +flag, to provide information for +.CW dbx , +you would set the +.CW CFLAGS +variable to contain +.CW -g +.CW "CFLAGS = -g" '') (`` +and PMake would take care of the rest. +.RE +.LP +To give you a quick example, the makefile in 2.3.4 +.Rm 3 2.3.4 +could be changed to this: +.DS +OBJS = a.o b.o c.o +program : $(OBJS) + $(CC) -o $(.TARGET) $(.ALLSRC) +$(OBJS) : defs.h +.DE +The transformation rule I gave above takes the place of the 6 lines\** +.FS +This is also somewhat cleaner, I think, than the dynamic source +solution presented in 2.6 +.FE +.Rm 4 2.6 +.DS +a.o : a.c + cc -c a.c +b.o : b.c + cc -c b.c +c.o : c.c + cc -c c.c +.DE +.LP +Now you may be wondering about the dependency between the +.CW .o +and +.CW .c +files \*- it's not mentioned anywhere in the new makefile. This is +because it isn't needed: one of the effects of applying a +transformation rule is the target comes to depend on the implied +source. That's why it's called the implied +.I source . +.LP +For a more detailed example. Say you have a makefile like this: +.DS +a.out : a.o b.o + $(CC) $(.ALLSRC) +.DE +and a directory set up like this: +.DS +total 4 +-rw-rw-r-- 1 deboor 34 Sep 7 00:43 Makefile +-rw-rw-r-- 1 deboor 119 Oct 3 19:39 a.c +-rw-rw-r-- 1 deboor 201 Sep 7 00:43 a.o +-rw-rw-r-- 1 deboor 69 Sep 7 00:43 b.c +.DE +While just typing +.CW pmake '' `` +will do the right thing, it's much more informative to type +.CW "pmake -d s" ''. `` +This will show you what PMake is up to as it processes the files. In +this case, PMake prints the following: +.DS +Suff_FindDeps (a.out) + using existing source a.o + applying .o -> .out to "a.o" +Suff_FindDeps (a.o) + trying a.c...got it + applying .c -> .o to "a.c" +Suff_FindDeps (b.o) + trying b.c...got it + applying .c -> .o to "b.c" +Suff_FindDeps (a.c) + trying a.y...not there + trying a.l...not there + trying a.c,v...not there + trying a.y,v...not there + trying a.l,v...not there +Suff_FindDeps (b.c) + trying b.y...not there + trying b.l...not there + trying b.c,v...not there + trying b.y,v...not there + trying b.l,v...not there +--- a.o --- +cc -c a.c +--- b.o --- +cc -c b.c +--- a.out --- +cc a.o b.o +.DE +.LP +.CW Suff_FindDeps +is the name of a function in PMake that is called to check for implied +sources for a target using transformation rules. +The transformations it tries are, naturally +enough, limited to the ones that have been defined (a transformation +may be defined multiple times, by the way, but only the most recent +one will be used). You will notice, however, that there is a definite +order to the suffixes that are tried. This order is set by the +relative positions of the suffixes on the +.CW .SUFFIXES +line \*- the earlier a suffix appears, the earlier it is checked as +the source of a transformation. Once a suffix has been defined, the +only way to change its position in the pecking order is to remove all +the suffixes (by having a +.CW .SUFFIXES +dependency line with no sources) and redefine them in the order you +want. (Previously-defined transformation rules will be automatically +redefined as the suffixes they involve are re-entered.) +.LP +Another way to affect the search order is to make the dependency +explicit. In the above example, +.CW a.out +depends on +.CW a.o +and +.CW b.o . +Since a transformation exists from +.CW .o +to +.CW .out , +PMake uses that, as indicated by the +.CW "using existing source a.o" '' `` +message. +.LP +The search for a transformation starts from the suffix of the target +and continues through all the defined transformations, in the order +dictated by the suffix ranking, until an existing file with the same +base (the target name minus the suffix and any leading directories) is +found. At that point, one or more transformation rules will have been +found to change the one existing file into the target. +.LP +For example, ignoring what's in the system makefile for now, say you +have a makefile like this: +.DS +\&.SUFFIXES : .out .o .c .y .l +\&.l.c : + lex $(.IMPSRC) + mv lex.yy.c $(.TARGET) +\&.y.c : + yacc $(.IMPSRC) + mv y.tab.c $(.TARGET) +\&.c.o : + cc -c $(.IMPSRC) +\&.o.out : + cc -o $(.TARGET) $(.IMPSRC) +.DE +and the single file +.CW jive.l . +If you were to type +.CW "pmake -rd ms jive.out" ,'' `` +you would get the following output for +.CW jive.out : +.DS +Suff_FindDeps (jive.out) + trying jive.o...not there + trying jive.c...not there + trying jive.y...not there + trying jive.l...got it + applying .l -> .c to "jive.l" + applying .c -> .o to "jive.c" + applying .o -> .out to "jive.o" +.DE +and this is why: PMake starts with the target +.CW jive.out , +figures out its suffix +.CW .out ) ( +and looks for things it can transform to a +.CW .out +file. In this case, it only finds +.CW .o , +so it looks for the file +.CW jive.o . +It fails to find it, so it looks for transformations into a +.CW .o +file. Again it has only one choice: +.CW .c . +So it looks for +.CW jive.c +and, as you know, fails to find it. At this point it has two choices: +it can create the +.CW .c +file from either a +.CW .y +file or a +.CW .l +file. Since +.CW .y +came first on the +.CW .SUFFIXES +line, it checks for +.CW jive.y +first, but can't find it, so it looks for +.CW jive.l +and, lo and behold, there it is. +At this point, it has defined a transformation path as follows: +.CW .l +\(-> +.CW .c +\(-> +.CW .o +\(-> +.CW .out +and applies the transformation rules accordingly. For completeness, +and to give you a better idea of what PMake actually did with this +three-step transformation, this is what PMake printed for the rest of +the process: +.DS +Suff_FindDeps (jive.o) + using existing source jive.c + applying .c -> .o to "jive.c" +Suff_FindDeps (jive.c) + using existing source jive.l + applying .l -> .c to "jive.l" +Suff_FindDeps (jive.l) +Examining jive.l...modified 17:16:01 Oct 4, 1987...up-to-date +Examining jive.c...non-existent...out-of-date +--- jive.c --- +lex jive.l +\&.\|.\|. meaningless lex output deleted .\|.\|. +mv lex.yy.c jive.c +Examining jive.o...non-existent...out-of-date +--- jive.o --- +cc -c jive.c +Examining jive.out...non-existent...out-of-date +--- jive.out --- +cc -o jive.out jive.o +.DE +.LP +One final question remains: what does PMake do with targets that have +no known suffix? PMake simply pretends it actually has a known suffix +and searches for transformations accordingly. +The suffix it chooses is the source for the +.CW .NULL +.Ix 0 ref .NULL +target mentioned later. In the system makefile, +.CW .out +is chosen as the ``null suffix'' +.Ix 0 def suffix null +.Ix 0 def "null suffix" +because most people use PMake to create programs. You are, however, +free and welcome to change it to a suffix of your own choosing. +The null suffix is ignored, however, when PMake is in compatibility +mode (see chapter 4). +.xH 2 Including Other Makefiles +.Ix 0 def makefile inclusion +.Rd 2 +.LP +Just as for programs, it is often useful to extract certain parts of a +makefile into another file and just include it in other makefiles +somehow. Many compilers allow you say something like +.DS +#include "defs.h" +.DE +to include the contents of +.CW defs.h +in the source file. PMake allows you to do the same thing for +makefiles, with the added ability to use variables in the filenames. +An include directive in a makefile looks either like this: +.DS +#include +.DE +or this +.DS +#include "file" +.DE +The difference between the two is where PMake searches for the file: +the first way, PMake will look for +the file only in the system makefile directory (or directories) +(to find out what that directory is, give PMake the +.B \-h +flag). +.Ix 0 ref flags -h +The system makefile directory search path can be overridden via the +.B \-m +option. +.Ix 0 ref flags -m +For files in double-quotes, the search is more complex: +.RS +.IP 1) +The directory of the makefile that's including the file. +.IP 2) +The current directory (the one in which you invoked PMake). +.IP 3) +The directories given by you using +.B \-I +flags, in the order in which you gave them. +.IP 4) +Directories given by +.CW .PATH +dependency lines (see chapter 4). +.IP 5) +The system makefile directory. +.RE +.LP +in that order. +.LP +You are free to use PMake variables in the filename\*-PMake will +expand them before searching for the file. You must specify the +searching method with either angle brackets or double-quotes +.I outside +of a variable expansion. I.e. the following +.DS +SYSTEM = + +#include $(SYSTEM) +.DE +won't work. +.xH 2 Saving Commands +.LP +.Ix 0 def ... +There may come a time when you will want to save certain commands to +be executed when everything else is done. For instance: you're +making several different libraries at one time and you want to create the +members in parallel. Problem is, +.CW ranlib +is another one of those programs that can't be run more than once in +the same directory at the same time (each one creates a file called +.CW __.SYMDEF +into which it stuffs information for the linker to use. Two of them +running at once will overwrite each other's file and the result will +be garbage for both parties). You might want a way to save the ranlib +commands til the end so they can be run one after the other, thus +keeping them from trashing each other's file. PMake allows you to do +this by inserting an ellipsis (``.\|.\|.'') as a command between +commands to be run at once and those to be run later. +.LP +So for the +.CW ranlib +case above, you might do this: +.Rd 5 +.DS +lib1.a : $(LIB1OBJS) + rm -f $(.TARGET) + ar cr $(.TARGET) $(.ALLSRC) + ... + ranlib $(.TARGET) + +lib2.a : $(LIB2OBJS) + rm -f $(.TARGET) + ar cr $(.TARGET) $(.ALLSRC) + ... + ranlib $(.TARGET) +.DE +.Ix 0 ref variable local .TARGET +.Ix 0 ref variable local .ALLSRC +This would save both +.DS +ranlib $(.TARGET) +.DE +commands until the end, when they would run one after the other +(using the correct value for the +.CW .TARGET +variable, of course). +.LP +Commands saved in this manner are only executed if PMake manages to +re-create everything without an error. +.xH 2 Target Attributes +.LP +PMake allows you to give attributes to targets by means of special +sources. Like everything else PMake uses, these sources begin with a +period and are made up of all upper-case letters. There are various +reasons for using them, and I will try to give examples for most of +them. Others you'll have to find uses for yourself. Think of it as ``an +exercise for the reader.'' By placing one (or more) of these as a source on a +dependency line, you are ``marking the target(s) with that +attribute.'' That's just the way I phrase it, so you know. +.LP +Any attributes given as sources for a transformation rule are applied +to the target of the transformation rule when the rule is applied. +.Ix 0 def attributes +.Ix 0 ref source +.Ix 0 ref target +.nr pw 12 +.IP .DONTCARE \n(pw +.Ix 0 def attributes .DONTCARE +.Ix 0 def .DONTCARE +If a target is marked with this attribute and PMake can't figure out +how to create it, it will ignore this fact and assume the file isn't +really needed or actually exists and PMake just can't find it. This may prove +wrong, but the error will be noted later on, not when PMake tries to create +the target so marked. This attribute also prevents PMake from +attempting to touch the target if it is given the +.B \-t +flag. +.Ix 0 ref flags -t +.IP .EXEC \n(pw +.Ix 0 def attributes .EXEC +.Ix 0 def .EXEC +This attribute causes its shell script to be executed while having no +effect on targets that depend on it. This makes the target into a sort +of subroutine. An example. Say you have some LISP files that need to +be compiled and loaded into a LISP process. To do this, you echo LISP +commands into a file and execute a LISP with this file as its input +when everything's done. Say also that you have to load other files +from another system before you can compile your files and further, +that you don't want to go through the loading and dumping unless one +of +.I your +files has changed. Your makefile might look a little bit +like this (remember, this is an educational example, and don't worry +about the +.CW COMPILE +rule, all will soon become clear, grasshopper): +.DS +system : init a.fasl b.fasl c.fasl + for i in $(.ALLSRC); + do + echo -n '(load "' >> input + echo -n ${i} >> input + echo '")' >> input + done + echo '(dump "$(.TARGET)")' >> input + lisp < input + +a.fasl : a.l init COMPILE +b.fasl : b.l init COMPILE +c.fasl : c.l init COMPILE +COMPILE : .USE + echo '(compile "$(.ALLSRC)")' >> input +init : .EXEC + echo '(load-system)' > input +.DE +.Ix 0 ref .USE +.Ix 0 ref attributes .USE +.Ix 0 ref variable local .ALLSRC +.IP "\&" +.CW .EXEC +sources, don't appear in the local variables of targets that depend on +them (nor are they touched if PMake is given the +.B \-t +flag). +.Ix 0 ref flags -t +Note that all the rules, not just that for +.CW system , +include +.CW init +as a source. This is because none of the other targets can be made +until +.CW init +has been made, thus they depend on it. +.IP .EXPORT \n(pw +.Ix 0 def attributes .EXPORT +.Ix 0 def .EXPORT +This is used to mark those targets whose creation should be sent to +another machine if at all possible. This may be used by some +exportation schemes if the exportation is expensive. You should ask +your system administrator if it is necessary. +.IP .EXPORTSAME \n(pw +.Ix 0 def attributes .EXPORTSAME +.Ix 0 def .EXPORTSAME +Tells the export system that the job should be exported to a machine +of the same architecture as the current one. Certain operations (e.g. +running text through +.CW nroff ) +can be performed the same on any architecture (CPU and +operating system type), while others (e.g. compiling a program with +.CW cc ) +must be performed on a machine with the same architecture. Not all +export systems will support this attribute. +.IP .IGNORE \n(pw +.Ix 0 def attributes .IGNORE +.Ix 0 def .IGNORE attribute +Giving a target the +.CW .IGNORE +attribute causes PMake to ignore errors from any of the target's commands, as +if they all had `\-' before them. +.IP .INVISIBLE \n(pw +.Ix 0 def attributes .INVISIBLE +.Ix 0 def .INVISIBLE +This allows you to specify one target as a source for another without +the one affecting the other's local variables. Useful if, say, you +have a makefile that creates two programs, one of which is used to +create the other, so it must exist before the other is created. You +could say +.DS +prog1 : $(PROG1OBJS) prog2 MAKEINSTALL +prog2 : $(PROG2OBJS) .INVISIBLE MAKEINSTALL +.DE +where +.CW MAKEINSTALL +is some complex .USE rule (see below) that depends on the +.Ix 0 ref .USE +.CW .ALLSRC +variable containing the right things. Without the +.CW .INVISIBLE +attribute for +.CW prog2 , +the +.CW MAKEINSTALL +rule couldn't be applied. This is not as useful as it should be, and +the semantics may change (or the whole thing go away) in the +not-too-distant future. +.IP .JOIN \n(pw +.Ix 0 def attributes .JOIN +.Ix 0 def .JOIN +This is another way to avoid performing some operations in parallel +while permitting everything else to be done so. Specifically it +forces the target's shell script to be executed only if one or more of the +sources was out-of-date. In addition, the target's name, +in both its +.CW .TARGET +variable and all the local variables of any target that depends on it, +is replaced by the value of its +.CW .ALLSRC +variable. +As an example, suppose you have a program that has four libraries that +compile in the same directory along with, and at the same time as, the +program. You again have the problem with +.CW ranlib +that I mentioned earlier, only this time it's more severe: you +can't just put the ranlib off to the end since the program +will need those libraries before it can be re-created. You can do +something like this: +.DS +program : $(OBJS) libraries + cc -o $(.TARGET) $(.ALLSRC) + +libraries : lib1.a lib2.a lib3.a lib4.a .JOIN + ranlib $(.OODATE) +.DE +.Ix 0 ref variable local .TARGET +.Ix 0 ref variable local .ALLSRC +.Ix 0 ref variable local .OODATE +.Ix 0 ref .TARGET +.Ix 0 ref .ALLSRC +.Ix 0 ref .OODATE +In this case, PMake will re-create the +.CW $(OBJS) +as necessary, along with +.CW lib1.a , +.CW lib2.a , +.CW lib3.a +and +.CW lib4.a . +It will then execute +.CW ranlib +on any library that was changed and set +.CW program 's +.CW .ALLSRC +variable to contain what's in +.CW $(OBJS) +followed by +.CW "lib1.a lib2.a lib3.a lib4.a" .'' `` +In case you're wondering, it's called +.CW .JOIN +because it joins together different threads of the ``input graph'' at +the target marked with the attribute. +Another aspect of the .JOIN attribute is it keeps the target from +being created if the +.B \-t +flag was given. +.Ix 0 ref flags -t +.IP .MAKE \n(pw +.Ix 0 def attributes .MAKE +.Ix 0 def .MAKE +The +.CW .MAKE +attribute marks its target as being a recursive invocation of PMake. +This forces PMake to execute the script associated with the target (if +it's out-of-date) even if you gave the +.B \-n +or +.B \-t +flag. By doing this, you can start at the top of a system and type +.DS +pmake -n +.DE +and have it descend the directory tree (if your makefiles are set up +correctly), printing what it would have executed if you hadn't +included the +.B \-n +flag. +.IP .NOEXPORT \n(pw +.Ix 0 def attributes .NOEXPORT +.Ix 0 def .NOEXPORT attribute +If possible, PMake will attempt to export the creation of all targets to +another machine (this depends on how PMake was configured). Sometimes, +the creation is so simple, it is pointless to send it to another +machine. If you give the target the +.CW .NOEXPORT +attribute, it will be run locally, even if you've given PMake the +.B "\-L 0" +flag. +.IP .NOTMAIN \n(pw +.Ix 0 def attributes .NOTMAIN +.Ix 0 def .NOTMAIN +Normally, if you do not specify a target to make in any other way, +PMake will take the first target on the first dependency line of a +makefile as the target to create. That target is known as the ``Main +Target'' and is labeled as such if you print the dependencies out +using the +.B \-p +flag. +.Ix 0 ref flags -p +Giving a target this attribute tells PMake that the target is +definitely +.I not +the Main Target. +This allows you to place targets in an included makefile and +have PMake create something else by default. +.IP .PRECIOUS \n(pw +.Ix 0 def attributes .PRECIOUS +.Ix 0 def .PRECIOUS attribute +When PMake is interrupted (you type control-C at the keyboard), it +will attempt to clean up after itself by removing any half-made +targets. If a target has the +.CW .PRECIOUS +attribute, however, PMake will leave it alone. An additional side +effect of the `::' operator is to mark the targets as +.CW .PRECIOUS . +.Ix 0 ref operator double-colon +.Ix 0 ref :: +.IP .SILENT \n(pw +.Ix 0 def attributes .SILENT +.Ix 0 def .SILENT attribute +Marking a target with this attribute keeps its commands from being +printed when they're executed, just as if they had an `@' in front of them. +.IP .USE \n(pw +.Ix 0 def attributes .USE +.Ix 0 def .USE +By giving a target this attribute, you turn it into PMake's equivalent +of a macro. When the target is used as a source for another target, +the other target acquires the commands, sources and attributes (except +.CW .USE ) +of the source. +If the target already has commands, the +.CW .USE +target's commands are added to the end. If more than one .USE-marked +source is given to a target, the rules are applied sequentially. +.IP "\&" \n(pw +The typical .USE rule (as I call them) will use the sources of the +target to which it is applied (as stored in the +.CW .ALLSRC +variable for the target) as its ``arguments,'' if you will. +For example, you probably noticed that the commands for creating +.CW lib1.a +and +.CW lib2.a +in the example in section 3.3 +.Rm 5 3.3 +were exactly the same. You can use the +.CW .USE +attribute to eliminate the repetition, like so: +.DS +lib1.a : $(LIB1OBJS) MAKELIB +lib2.a : $(LIB2OBJS) MAKELIB + +MAKELIB : .USE + rm -f $(.TARGET) + ar cr $(.TARGET) $(.ALLSRC) + ... + ranlib $(.TARGET) +.DE +.Ix 0 ref variable local .TARGET +.Ix 0 ref variable local .ALLSRC +.IP "\&" \n(pw +Several system makefiles (not to be confused with The System Makefile) +make use of these .USE rules to make your +life easier (they're in the default, system makefile directory...take a look). +Note that the .USE rule source itself +.CW MAKELIB ) ( +does not appear in any of the targets's local variables. +There is no limit to the number of times I could use the +.CW MAKELIB +rule. If there were more libraries, I could continue with +.CW "lib3.a : $(LIB3OBJS) MAKELIB" '' `` +and so on and so forth. +.xH 2 Special Targets +.LP +As there were in Make, so there are certain targets that have special +meaning to PMake. When you use one on a dependency line, it is the +only target that may appear on the left-hand-side of the operator. +.Ix 0 ref target +.Ix 0 ref operator +As for the attributes and variables, all the special targets +begin with a period and consist of upper-case letters only. +I won't describe them all in detail because some of them are rather +complex and I'll describe them in more detail than you'll want in +chapter 4. +The targets are as follows: +.nr pw 10 +.IP .BEGIN \n(pw +.Ix 0 def .BEGIN +Any commands attached to this target are executed before anything else +is done. You can use it for any initialization that needs doing. +.IP .DEFAULT \n(pw +.Ix 0 def .DEFAULT +This is sort of a .USE rule for any target (that was used only as a +source) that PMake can't figure out any other way to create. It's only +``sort of'' a .USE rule because only the shell script attached to the +.CW .DEFAULT +target is used. The +.CW .IMPSRC +variable of a target that inherits +.CW .DEFAULT 's +commands is set to the target's own name. +.Ix 0 ref .IMPSRC +.Ix 0 ref variable local .IMPSRC +.IP .END \n(pw +.Ix 0 def .END +This serves a function similar to +.CW .BEGIN , +in that commands attached to it are executed once everything has been +re-created (so long as no errors occurred). It also serves the extra +function of being a place on which PMake can hang commands you put off +to the end. Thus the script for this target will be executed before +any of the commands you save with the ``.\|.\|.''. +.Ix 0 ref ... +.IP .EXPORT \n(pw +The sources for this target are passed to the exportation system compiled +into PMake. Some systems will use these sources to configure +themselves. You should ask your system administrator about this. +.IP .IGNORE \n(pw +.Ix 0 def .IGNORE target +.Ix 0 ref .IGNORE attribute +.Ix 0 ref attributes .IGNORE +This target marks each of its sources with the +.CW .IGNORE +attribute. If you don't give it any sources, then it is like +giving the +.B \-i +flag when you invoke PMake \*- errors are ignored for all commands. +.Ix 0 ref flags -i +.IP .INCLUDES \n(pw +.Ix 0 def .INCLUDES target +.Ix 0 def variable global .INCLUDES +.Ix 0 def .INCLUDES variable +The sources for this target are taken to be suffixes that indicate a +file that can be included in a program source file. +The suffix must have already been declared with +.CW .SUFFIXES +(see below). +Any suffix so marked will have the directories on its search path +(see +.CW .PATH , +below) placed in the +.CW .INCLUDES +variable, each preceded by a +.B \-I +flag. This variable can then be used as an argument for the compiler +in the normal fashion. The +.CW .h +suffix is already marked in this way in the system makefile. +.Ix 0 ref makefile system +E.g. if you have +.DS +\&.SUFFIXES : .bitmap +\&.PATH.bitmap : /usr/local/X/lib/bitmaps +\&.INCLUDES : .bitmap +.DE +PMake will place +.CW "-I/usr/local/X/lib/bitmaps" '' `` +in the +.CW .INCLUDES +variable and you can then say +.DS +cc $(.INCLUDES) -c xprogram.c +.DE +(Note: the +.CW .INCLUDES +variable is not actually filled in until the entire makefile has been read.) +.IP .INTERRUPT \n(pw +.Ix 0 def .INTERRUPT +When PMake is interrupted, +it will execute the commands in the script for this target, if it +exists. +.IP .LIBS \n(pw +.Ix 0 def .LIBS target +.Ix 0 def .LIBS variable +.Ix 0 def variable global .LIBS +This does for libraries what +.CW .INCLUDES +does for include files, except the flag used is +.B \-L , +as required by those linkers that allow you to tell them where to find +libraries. The variable used is +.CW .LIBS . +Be forewarned that PMake may not have been compiled to do this if the +linker on your system doesn't accept the +.B \-L +flag, though the +.CW .LIBS +variable will always be defined once the makefile has been read. +.IP .MAIN \n(pw +.Ix 0 def .MAIN +If you didn't give a target (or targets) to create when you invoked +PMake, it will take the sources of this target as the targets to +create. +.IP .MAKEFLAGS \n(pw +.Ix 0 def .MAKEFLAGS target +This target provides a way for you to always specify flags for PMake +when the makefile is used. The flags are just as they would be typed +to the shell (except you can't use shell variables unless they're in +the environment), +though the +.B \-f +and +.B \-r +flags have no effect. +.IP .NULL \n(pw +.Ix 0 def .NULL +.Ix 0 ref suffix null +.Ix 0 ref "null suffix" +This allows you to specify what suffix PMake should pretend a file has +if, in fact, it has no known suffix. Only one suffix may be so +designated. The last source on the dependency line is the suffix that +is used (you should, however, only give one suffix.\|.\|.). +.IP .PATH \n(pw +.Ix 0 def .PATH +If you give sources for this target, PMake will take them as +directories in which to search for files it cannot find in the current +directory. If you give no sources, it will clear out any directories +added to the search path before. Since the effects of this all get +very complex, I'll leave it til chapter four to give you a complete +explanation. +.IP .PATH\fIsuffix\fP \n(pw +.Ix 0 ref .PATH +This does a similar thing to +.CW .PATH , +but it does it only for files with the given suffix. The suffix must +have been defined already. Look at +.B "Search Paths" +(section 4.1) +.Rm 6 4.1 +for more information. +.IP .PRECIOUS \n(pw +.Ix 0 def .PRECIOUS target +.Ix 0 ref .PRECIOUS attribute +.Ix 0 ref attributes .PRECIOUS +Similar to +.CW .IGNORE , +this gives the +.CW .PRECIOUS +attribute to each source on the dependency line, unless there are no +sources, in which case the +.CW .PRECIOUS +attribute is given to every target in the file. +.IP .RECURSIVE \n(pw +.Ix 0 def .RECURSIVE +.Ix 0 ref attributes .MAKE +.Ix 0 ref .MAKE +This target applies the +.CW .MAKE +attribute to all its sources. It does nothing if you don't give it any sources. +.IP .SHELL \n(pw +.Ix 0 def .SHELL +PMake is not constrained to only using the Bourne shell to execute +the commands you put in the makefile. You can tell it some other shell +to use with this target. Check out +.B "A Shell is a Shell is a Shell" +(section 4.4) +.Rm 7 4.4 +for more information. +.IP .SILENT \n(pw +.Ix 0 def .SILENT target +.Ix 0 ref .SILENT attribute +.Ix 0 ref attributes .SILENT +When you use +.CW .SILENT +as a target, it applies the +.CW .SILENT +attribute to each of its sources. If there are no sources on the +dependency line, then it is as if you gave PMake the +.B \-s +flag and no commands will be echoed. +.IP .SUFFIXES \n(pw +.Ix 0 def .SUFFIXES +This is used to give new file suffixes for PMake to handle. Each +source is a suffix PMake should recognize. If you give a +.CW .SUFFIXES +dependency line with no sources, PMake will forget about all the +suffixes it knew (this also nukes the null suffix). +For those targets that need to have suffixes defined, this is how you do it. +.LP +In addition to these targets, a line of the form +.DS +\fIattribute\fP : \fIsources\fP +.DE +applies the +.I attribute +to all the targets listed as +.I sources . +.xH 2 Modifying Variable Expansion +.LP +.Ix 0 def variable expansion modified +.Ix 0 ref variable expansion +.Ix 0 def variable modifiers +Variables need not always be expanded verbatim. PMake defines several +modifiers that may be applied to a variable's value before it is +expanded. You apply a modifier by placing it after the variable name +with a colon between the two, like so: +.DS +${\fIVARIABLE\fP:\fImodifier\fP} +.DE +Each modifier is a single character followed by something specific to +the modifier itself. +You may apply as many modifiers as you want \*- each one is applied to +the result of the previous and is separated from the previous by +another colon. +.LP +There are seven ways to modify a variable's expansion, most of which +come from the C shell variable modification characters: +.RS +.IP "M\fIpattern\fP" +.Ix 0 def :M +.Ix 0 def modifier match +This is used to select only those words (a word is a series of +characters that are neither spaces nor tabs) that match the given +.I pattern . +The pattern is a wildcard pattern like that used by the shell, where +.CW * +means 0 or more characters of any sort; +.CW ? +is any single character; +.CW [abcd] +matches any single character that is either `a', `b', `c' or `d' +(there may be any number of characters between the brackets); +.CW [0-9] +matches any single character that is between `0' and `9' (i.e. any +digit. This form may be freely mixed with the other bracket form), and +`\\' is used to escape any of the characters `*', `?', `[' or `:', +leaving them as regular characters to match themselves in a word. +For example, the system makefile +.CW +uses +.CW "$(CFLAGS:M-[ID]*)" '' `` +to extract all the +.CW \-I +and +.CW \-D +flags that would be passed to the C compiler. This allows it to +properly locate include files and generate the correct dependencies. +.IP "N\fIpattern\fP" +.Ix 0 def :N +.Ix 0 def modifier nomatch +This is identical to +.CW :M +except it substitutes all words that don't match the given pattern. +.IP "S/\fIsearch-string\fP/\fIreplacement-string\fP/[g]" +.Ix 0 def :S +.Ix 0 def modifier substitute +Causes the first occurrence of +.I search-string +in the variable to be replaced by +.I replacement-string , +unless the +.CW g +flag is given at the end, in which case all occurrences of the string +are replaced. The substitution is performed on each word in the +variable in turn. If +.I search-string +begins with a +.CW ^ , +the string must match starting at the beginning of the word. If +.I search-string +ends with a +.CW $ , +the string must match to the end of the word (these two may be +combined to force an exact match). If a backslash precedes these two +characters, however, they lose their special meaning. Variable +expansion also occurs in the normal fashion inside both the +.I search-string +and the +.I replacement-string , +.B except +that a backslash is used to prevent the expansion of a +.CW $ , +not another dollar sign, as is usual. +Note that +.I search-string +is just a string, not a pattern, so none of the usual +regular-expression/wildcard characters have any special meaning save +.CW ^ +and +.CW $ . +In the replacement string, +the +.CW & +character is replaced by the +.I search-string +unless it is preceded by a backslash. +You are allowed to use any character except +colon or exclamation point to separate the two strings. This so-called +delimiter character may be placed in either string by preceding it +with a backslash. +.IP T +.Ix 0 def :T +.Ix 0 def modifier tail +Replaces each word in the variable expansion by its last +component (its ``tail''). For example, given +.DS +OBJS = ../lib/a.o b /usr/lib/libm.a +TAILS = $(OBJS:T) +.DE +the variable +.CW TAILS +would expand to +.CW "a.o b libm.a" .'' `` +.IP H +.Ix 0 def :H +.Ix 0 def modifier head +This is similar to +.CW :T , +except that every word is replaced by everything but the tail (the +``head''). Using the same definition of +.CW OBJS , +the string +.CW "$(OBJS:H)" '' `` +would expand to +.CW "../lib /usr/lib" .'' `` +Note that the final slash on the heads is removed and +anything without a head is replaced by the empty string. +.IP E +.Ix 0 def :E +.Ix 0 def modifier extension +.Ix 0 def modifier suffix +.Ix 0 ref suffix "variable modifier" +.CW :E +replaces each word by its suffix (``extension''). So +.CW "$(OBJS:E)" '' `` +would give you +.CW ".o .a" .'' `` +.IP R +.Ix 0 def :R +.Ix 0 def modifier root +.Ix 0 def modifier base +This replaces each word by everything but the suffix (the ``root'' of +the word). +.CW "$(OBJS:R)" '' `` +expands to `` +.CW "../lib/a b /usr/lib/libm" .'' +.RE +.LP +In addition, the System V style of substitution is also supported. +This looks like: +.DS +$(\fIVARIABLE\fP:\fIsearch-string\fP=\fIreplacement\fP) +.DE +It must be the last modifier in the chain. The search is anchored at +the end of each word, so only suffixes or whole words may be replaced. +.xH 2 More on Debugging +.xH 2 More Exercises +.IP (3.1) +You've got a set programs, each of which is created from its own +assembly-language source file (suffix +.CW .asm ). +Each program can be assembled into two versions, one with error-checking +code assembled in and one without. You could assemble them into files +with different suffixes +.CW .eobj \& ( +and +.CW .obj , +for instance), but your linker only understands files that end in +.CW .obj . +To top it all off, the final executables +.I must +have the suffix +.CW .exe . +How can you still use transformation rules to make your life easier +(Hint: assume the error-checking versions have +.CW ec +tacked onto their prefix)? +.IP (3.2) +Assume, for a moment or two, you want to perform a sort of +``indirection'' by placing the name of a variable into another one, +then you want to get the value of the first by expanding the second +somehow. Unfortunately, PMake doesn't allow constructs like +.DS I +$($(FOO)) +.DE +What do you do? Hint: no further variable expansion is performed after +modifiers are applied, thus if you cause a $ to occur in the +expansion, that's what will be in the result. +.xH 1 PMake for Gods +.LP +This chapter is devoted to those facilities in PMake that allow you to +do a great deal in a makefile with very little work, as well as do +some things you couldn't do in Make without a great deal of work (and +perhaps the use of other programs). The problem with these features, +is they must be handled with care, or you will end up with a mess. +.LP +Once more, I assume a greater familiarity with +.UX +or Sprite than I did in the previous two chapters. +.xH 2 Search Paths +.Rd 6 +.LP +PMake supports the dispersal of files into multiple directories by +allowing you to specify places to look for sources with +.CW .PATH +targets in the makefile. The directories you give as sources for these +targets make up a ``search path.'' Only those files used exclusively +as sources are actually sought on a search path, the assumption being +that anything listed as a target in the makefile can be created by the +makefile and thus should be in the current directory. +.LP +There are two types of search paths +in PMake: one is used for all types of files (including included +makefiles) and is specified with a plain +.CW .PATH +target (e.g. +.CW ".PATH : RCS" ''), `` +while the other is specific to a certain type of file, as indicated by +the file's suffix. A specific search path is indicated by immediately following +the +.CW .PATH +with the suffix of the file. For instance +.DS +\&.PATH.h : /sprite/lib/include /sprite/att/lib/include +.DE +would tell PMake to look in the directories +.CW /sprite/lib/include +and +.CW /sprite/att/lib/include +for any files whose suffix is +.CW .h . +.LP +The current directory is always consulted first to see if a file +exists. Only if it cannot be found there are the directories in the +specific search path, followed by those in the general search path, +consulted. +.LP +A search path is also used when expanding wildcard characters. If the +pattern has a recognizable suffix on it, the path for that suffix will +be used for the expansion. Otherwise the default search path is employed. +.LP +When a file is found in some directory other than the current one, all +local variables that would have contained the target's name +.CW .ALLSRC , ( +and +.CW .IMPSRC ) +will instead contain the path to the file, as found by PMake. +Thus if you have a file +.CW ../lib/mumble.c +and a makefile +.DS +\&.PATH.c : ../lib +mumble : mumble.c + $(CC) -o $(.TARGET) $(.ALLSRC) +.DE +the command executed to create +.CW mumble +would be +.CW "cc -o mumble ../lib/mumble.c" .'' `` +(As an aside, the command in this case isn't strictly necessary, since +it will be found using transformation rules if it isn't given. This is because +.CW .out +is the null suffix by default and a transformation exists from +.CW .c +to +.CW .out . +Just thought I'd throw that in.) +.LP +If a file exists in two directories on the same search path, the file +in the first directory on the path will be the one PMake uses. So if +you have a large system spread over many directories, it would behoove +you to follow a naming convention that avoids such conflicts. +.LP +Something you should know about the way search paths are implemented +is that each directory is read, and its contents cached, exactly once +\&\*- when it is first encountered \*- so any changes to the +directories while PMake is running will not be noted when searching +for implicit sources, nor will they be found when PMake attempts to +discover when the file was last modified, unless the file was created in the +current directory. While people have suggested that PMake should read +the directories each time, my experience suggests that the caching seldom +causes problems. In addition, not caching the directories slows things +down enormously because of PMake's attempts to apply transformation +rules through non-existent files \*- the number of extra file-system +searches is truly staggering, especially if many files without +suffixes are used and the null suffix isn't changed from +.CW .out . +.xH 2 Archives and Libraries +.LP +.UX +and Sprite allow you to merge files into an archive using the +.CW ar +command. Further, if the files are relocatable object files, you can +run +.CW ranlib +on the archive and get yourself a library that you can link into any +program you want. The main problem with archives is they double the +space you need to store the archived files, since there's one copy in +the archive and one copy out by itself. The problem with libraries is +you usually think of them as +.CW -lm +rather than +.CW /usr/lib/libm.a +and the linker thinks they're out-of-date if you so much as look at +them. +.LP +PMake solves the problem with archives by allowing you to tell it to +examine the files in the archives (so you can remove the individual +files without having to regenerate them later). To handle the problem +with libraries, PMake adds an additional way of deciding if a library +is out-of-date: +.IP \(bu 2 +If the table of contents is older than the library, or is missing, the +library is out-of-date. +.LP +A library is any target that looks like +.CW \-l name'' `` +or that ends in a suffix that was marked as a library using the +.CW .LIBS +target. +.CW .a +is so marked in the system makefile. +.LP +Members of an archive are specified as +``\fIarchive\fP(\fImember\fP[ \fImember\fP...])''. +Thus +.CW libdix.a(window.o) '' ``' +specifies the file +.CW window.o +in the archive +.CW libdix.a . +You may also use wildcards to specify the members of the archive. Just +remember that most the wildcard characters will only find +.I existing +files. +.LP +A file that is a member of an archive is treated specially. If the +file doesn't exist, but it is in the archive, the modification time +recorded in the archive is used for the file when determining if the +file is out-of-date. When figuring out how to make an archived member target +(not the file itself, but the file in the archive \*- the +\fIarchive\fP(\fImember\fP) target), special care is +taken with the transformation rules, as follows: +.IP \(bu 2 +\&\fIarchive\fP(\fImember\fP) is made to depend on \fImember\fP. +.IP \(bu 2 +The transformation from the \fImember\fP's suffix to the +\fIarchive\fP's suffix is applied to the \fIarchive\fP(\fImember\fP) target. +.IP \(bu 2 +The \fIarchive\fP(\fImember\fP)'s +.CW .TARGET +variable is set to the name of the \fImember\fP if \fImember\fP is +actually a target, or the path to the member file if \fImember\fP is +only a source. +.IP \(bu 2 +The +.CW .ARCHIVE +variable for the \fIarchive\fP(\fImember\fP) target is set to the name +of the \fIarchive\fP. +.Ix 0 def variable local .ARCHIVE +.Ix 0 def .ARCHIVE +.IP \(bu 2 +The +.CW .MEMBER +variable is set to the actual string inside the parentheses. In most +cases, this will be the same as the +.CW .TARGET +variable. +.Ix 0 def variable local .MEMBER +.Ix 0 def .MEMBER +.IP \(bu 2 +The \fIarchive\fP(\fImember\fP)'s place in the local variables of the +targets that depend on it is taken by the value of its +.CW .TARGET +variable. +.LP +Thus, a program library could be created with the following makefile: +.DS +\&.o.a : + ... + rm -f $(.TARGET:T) +OBJS = obj1.o obj2.o obj3.o +libprog.a : libprog.a($(OBJS)) + ar cru $(.TARGET) $(.OODATE) + ranlib $(.TARGET) +.DE +This will cause the three object files to be compiled (if the +corresponding source files were modified after the object file or, if +that doesn't exist, the archived object file), the out-of-date ones +archived in +.CW libprog.a , +a table of contents placed in the archive and the newly-archived +object files to be removed. +.LP +All this is used in the +.CW makelib.mk +system makefile to create a single library with ease. This makefile +looks like this: +.DS +.SM +# +# Rules for making libraries. The object files that make up the library are +# removed once they are archived. +# +# To make several libararies in parallel, you should define the variable +# "many_libraries". This will serialize the invocations of ranlib. +# +# To use, do something like this: +# +# OBJECTS = +# +# fish.a: fish.a($(OBJECTS)) MAKELIB +# +# + +#ifndef _MAKELIB_MK +_MAKELIB_MK = + +#include + +\&.po.a .o.a : + ... + rm -f $(.MEMBER) + +ARFLAGS ?= crl + +# +# Re-archive the out-of-date members and recreate the library's table of +# contents using ranlib. If many_libraries is defined, put the ranlib off +# til the end so many libraries can be made at once. +# +MAKELIB : .USE .PRECIOUS + ar $(ARFLAGS) $(.TARGET) $(.OODATE) +#ifndef no_ranlib +# ifdef many_libraries + ... +# endif many_libraries + ranlib $(.TARGET) +#endif no_ranlib + +#endif _MAKELIB_MK +.DE +.xH 2 On the Condition... +.Rd 1 +.LP +Like the C compiler before it, PMake allows you to configure the makefile, +based on the current environment, using conditional statements. A +conditional looks like this: +.DS +#if \fIboolean expression\fP +\fIlines\fP +#elif \fIanother boolean expression\fP +\fImore lines\fP +#else +\fIstill more lines\fP +#endif +.DE +They may be nested to a maximum depth of 30 and may occur anywhere +(except in a comment, of course). The +.CW # '' `` +must the very first character on the line. +.LP +Each +.I "boolean expression" +is made up of terms that look like function calls, the standard C +boolean operators +.CW && , +.CW || , +and +.CW ! , +and the standard relational operators +.CW == , +.CW != , +.CW > , +.CW >= , +.CW < , +and +.CW <= , +with +.CW == +and +.CW != +being overloaded to allow string comparisons as well. +.CW && +represents logical AND; +.CW || +is logical OR and +.CW ! +is logical NOT. The arithmetic and string operators take precedence +over all three of these operators, while NOT takes precedence over +AND, which takes precedence over OR. This precedence may be +overridden with parentheses, and an expression may be parenthesized to +your heart's content. Each term looks like a call on one of four +functions: +.nr pw 9 +.Ix 0 def make +.Ix 0 def conditional make +.Ix 0 def if make +.IP make \n(pw +The syntax is +.CW make( \fItarget\fP\c +.CW ) +where +.I target +is a target in the makefile. This is true if the given target was +specified on the command line, or as the source for a +.CW .MAIN +target (note that the sources for +.CW .MAIN +are only used if no targets were given on the command line). +.IP defined \n(pw +.Ix 0 def defined +.Ix 0 def conditional defined +.Ix 0 def if defined +The syntax is +.CW defined( \fIvariable\fP\c +.CW ) +and is true if +.I variable +is defined. Certain variables are defined in the system makefile that +identify the system on which PMake is being run. +.IP exists \n(pw +.Ix 0 def exists +.Ix 0 def conditional exists +.Ix 0 def if exists +The syntax is +.CW exists( \fIfile\fP\c +.CW ) +and is true if the file can be found on the global search path (i.e. +that defined by +.CW .PATH +targets, not by +.CW .PATH \fIsuffix\fP +targets). +.IP empty \n(pw +.Ix 0 def empty +.Ix 0 def conditional empty +.Ix 0 def if empty +This syntax is much like the others, except the string inside the +parentheses is of the same form as you would put between parentheses +when expanding a variable, complete with modifiers and everything. The +function returns true if the resulting string is empty (NOTE: an undefined +variable in this context will cause at the very least a warning +message about a malformed conditional, and at the worst will cause the +process to stop once it has read the makefile. If you want to check +for a variable being defined or empty, use the expression +.CW !defined( \fIvar\fP\c `` +.CW ") || empty(" \fIvar\fP\c +.CW ) '' +as the definition of +.CW || +will prevent the +.CW empty() +from being evaluated and causing an error, if the variable is +undefined). This can be used to see if a variable contains a given +word, for example: +.DS +#if !empty(\fIvar\fP:M\fIword\fP) +.DE +.LP +The arithmetic and string operators may only be used to test the value +of a variable. The lefthand side must contain the variable expansion, +while the righthand side contains either a string, enclosed in +double-quotes, or a number. The standard C numeric conventions (except +for specifying an octal number) apply to both sides. E.g. +.DS +#if $(OS) == 4.3 + +#if $(MACHINE) == "sun3" + +#if $(LOAD_ADDR) < 0xc000 +.DE +are all valid conditionals. In addition, the numeric value of a +variable can be tested as a boolean as follows: +.DS +#if $(LOAD) +.DE +would see if +.CW LOAD +contains a non-zero value and +.DS +#if !$(LOAD) +.DE +would test if +.CW LOAD +contains a zero value. +.LP +In addition to the bare +.CW #if ,'' `` +there are other forms that apply one of the first two functions to each +term. They are as follows: +.DS + ifdef \fRdefined\fP + ifndef \fR!defined\fP + ifmake \fRmake\fP + ifnmake \fR!make\fP +.DE +There are also the ``else if'' forms: +.CW elif , +.CW elifdef , +.CW elifndef , +.CW elifmake , +and +.CW elifnmake . +.LP +For instance, if you wish to create two versions of a program, one of which +is optimized (the production version) and the other of which is for debugging +(has symbols for dbx), you have two choices: you can create two +makefiles, one of which uses the +.CW \-g +flag for the compilation, while the other uses the +.CW \-O +flag, or you can use another target (call it +.CW debug ) +to create the debug version. The construct below will take care of +this for you. I have also made it so defining the variable +.CW DEBUG +(say with +.CW "pmake -D DEBUG" ) +will also cause the debug version to be made. +.DS +#if defined(DEBUG) || make(debug) +CFLAGS += -g +#else +CFLAGS += -O +#endif +.DE +There are, of course, problems with this approach. The most glaring +annoyance is that if you want to go from making a debug version to +making a production version, you have to remove all the object files, +or you will get some optimized and some debug versions in the same +program. Another annoyance is you have to be careful not to make two +targets that ``conflict'' because of some conditionals in the +makefile. For instance +.DS +#if make(print) +FORMATTER = ditroff -Plaser_printer +#endif +#if make(draft) +FORMATTER = nroff -Pdot_matrix_printer +#endif +.DE +would wreak havok if you tried +.CW "pmake draft print" '' `` +since you would use the same formatter for each target. As I said, +this all gets somewhat complicated. +.xH 2 A Shell is a Shell is a Shell +.Rd 7 +.LP +In normal operation, the Bourne Shell (better known as +.CW sh '') `` +is used to execute the commands to re-create targets. PMake also allows you +to specify a different shell for it to use when executing these +commands. There are several things PMake must know about the shell you +wish to use. These things are specified as the sources for the +.CW .SHELL +.Ix 0 ref .SHELL +.Ix 0 ref target .SHELL +target by keyword, as follows: +.IP "\fBpath=\fP\fIpath\fP" +PMake needs to know where the shell actually resides, so it can +execute it. If you specify this and nothing else, PMake will use the +last component of the path and look in its table of the shells it +knows and use the specification it finds, if any. Use this if you just +want to use a different version of the Bourne or C Shell (yes, PMake knows +how to use the C Shell too). +.IP "\fBname=\fP\fIname\fP" +This is the name by which the shell is to be known. It is a single +word and, if no other keywords are specified (other than +.B path ), +it is the name by which PMake attempts to find a specification for +it (as mentioned above). You can use this if you would just rather use +the C Shell than the Bourne Shell +.CW ".SHELL: name=csh" '' (`` +will do it). +.IP "\fBquiet=\fP\fIecho-off command\fP" +As mentioned before, PMake actually controls whether commands are +printed by introducing commands into the shell's input stream. This +keyword, and the next two, control what those commands are. The +.B quiet +keyword is the command used to turn echoing off. Once it is turned +off, echoing is expected to remain off until the echo-on command is given. +.IP "\fBecho=\fP\fIecho-on command\fP" +The command PMake should give to turn echoing back on again. +.IP "\fBfilter=\fP\fIprinted echo-off command\fP" +Many shells will echo the echo-off command when it is given. This +keyword tells PMake in what format the shell actually prints the +echo-off command. Wherever PMake sees this string in the shell's +output, it will delete it and any following whitespace, up to and +including the next newline. See the example at the end of this section +for more details. +.IP "\fBechoFlag=\fP\fIflag to turn echoing on\fP" +Unless a target has been marked +.CW .SILENT , +PMake wants to start the shell running with echoing on. To do this, it +passes this flag to the shell as one of its arguments. If either this +or the next flag begins with a `\-', the flags will be passed to the +shell as separate arguments. Otherwise, the two will be concatenated +(if they are used at the same time, of course). +.IP "\fBerrFlag=\fP\fIflag to turn error checking on\fP" +Likewise, unless a target is marked +.CW .IGNORE , +PMake wishes error-checking to be on from the very start. To this end, +it will pass this flag to the shell as an argument. The same rules for +an initial `\-' apply as for the +.B echoFlag . +.IP "\fBcheck=\fP\fIcommand to turn error checking on\fP" +Just as for echo-control, error-control is achieved by inserting +commands into the shell's input stream. This is the command to make +the shell check for errors. It also serves another purpose if the +shell doesn't have error-control as commands, but I'll get into that +in a minute. Again, once error checking has been turned on, it is +expected to remain on until it is turned off again. +.IP "\fBignore=\fP\fIcommand to turn error checking off\fP" +This is the command PMake uses to turn error checking off. It has +another use if the shell doesn't do error-control, but I'll tell you +about that.\|.\|.\|now. +.IP "\fBhasErrCtl=\fP\fIyes or no\fP" +This takes a value that is either +.B yes +or +.B no . +Now you might think that the existence of the +.B check +and +.B ignore +keywords would be enough to tell PMake if the shell can do +error-control, but you'd be wrong. If +.B hasErrCtl +is +.B yes , +PMake uses the check and ignore commands in a straight-forward manner. +If this is +.B no , +however, their use is rather different. In this case, the check +command is used as a template, in which the string +.B %s +is replaced by the command that's about to be executed, to produce a +command for the shell that will echo the command to be executed. The +ignore command is also used as a template, again with +.B %s +replaced by the command to be executed, to produce a command that will +execute the command to be executed and ignore any error it returns. +When these strings are used as templates, you must provide newline(s) +.CW \en '') (`` +in the appropriate place(s). +.LP +The strings that follow these keywords may be enclosed in single or +double quotes (the quotes will be stripped off) and may contain the +usual C backslash-characters (\en is newline, \er is return, \eb is +backspace, \e' escapes a single-quote inside single-quotes, \e" +escapes a double-quote inside double-quotes). Now for an example. +.LP +This is actually the contents of the +.CW +system makefile, and causes PMake to use the Bourne Shell in such a +way that each command is printed as it is executed. That is, if more +than one command is given on a line, each will be printed separately. +Similarly, each time the body of a loop is executed, the commands +within that loop will be printed, etc. The specification runs like +this: +.DS +# +# This is a shell specification to have the bourne shell echo +# the commands just before executing them, rather than when it reads +# them. Useful if you want to see how variables are being expanded, etc. +# +\&.SHELL : path=/bin/sh \e + quiet="set -" \e + echo="set -x" \e + filter="+ set - " \e + echoFlag=x \e + errFlag=e \e + hasErrCtl=yes \e + check="set -e" \e + ignore="set +e" +.DE +.LP +It tells PMake the following: +.Bp +The shell is located in the file +.CW /bin/sh . +It need not tell PMake that the name of the shell is +.CW sh +as PMake can figure that out for itself (it's the last component of +the path). +.Bp +The command to stop echoing is +.CW "set -" . +.Bp +The command to start echoing is +.CW "set -x" . +.Bp +When the echo off command is executed, the shell will print +.CW "+ set - " +(The `+' comes from using the +.CW \-x +flag (rather than the +.CW \-v +flag PMake usually uses)). PMake will remove all occurrences of this +string from the output, so you don't notice extra commands you didn't +put there. +.Bp +The flag the Bourne Shell will take to start echoing in this way is +the +.CW \-x +flag. The Bourne Shell will only take its flag arguments concatenated +as its first argument, so neither this nor the +.B errFlag +specification begins with a \-. +.Bp +The flag to use to turn error-checking on from the start is +.CW \-e . +.Bp +The shell can turn error-checking on and off, and the commands to do +so are +.CW "set +e" +and +.CW "set -e" , +respectively. +.LP +I should note that this specification is for Bourne Shells that are +not part of Berkeley +.UX , +as shells from Berkeley don't do error control. You can get a similar +effect, however, by changing the last three lines to be: +.DS + hasErrCtl=no \e + check="echo \e"+ %s\e"\en" \e + ignore="sh -c '%s || exit 0\en" +.DE +.LP +This will cause PMake to execute the two commands +.DS +echo "+ \fIcmd\fP" +sh -c '\fIcmd\fP || true' +.DE +for each command for which errors are to be ignored. (In case you are +wondering, the thing for +.CW ignore +tells the shell to execute another shell without error checking on and +always exit 0, since the +.B || +causes the +.CW "exit 0" +to be executed only if the first command exited non-zero, and if the +first command exited zero, the shell will also exit zero, since that's +the last command it executed). +.xH 2 Compatibility +.Ix 0 ref compatibility +.LP +There are three (well, 3 \(12) levels of backwards-compatibility built +into PMake. Most makefiles will need none at all. Some may need a +little bit of work to operate correctly when run in parallel. Each +level encompasses the previous levels (e.g. +.B \-B +(one shell per command) implies +.B \-V ) +The three levels are described in the following three sections. +.xH 3 DEFCON 3 \*- Variable Expansion +.Ix 0 ref compatibility +.LP +As noted before, PMake will not expand a variable unless it knows of a +value for it. This can cause problems for makefiles that expect to +leave variables undefined except in special circumstances (e.g. if +more flags need to be passed to the C compiler or the output from a +text processor should be sent to a different printer). If the +variables are enclosed in curly braces +.CW ${PRINTER} ''), (`` +the shell will let them pass. If they are enclosed in parentheses, +however, the shell will declare a syntax error and the make will come +to a grinding halt. +.LP +You have two choices: change the makefile to define the variables +(their values can be overridden on the command line, since that's +where they would have been set if you used Make, anyway) or always give the +.B \-V +flag (this can be done with the +.CW .MAKEFLAGS +target, if you want). +.xH 3 DEFCON 2 \*- The Number of the Beast +.Ix 0 ref compatibility +.LP +Then there are the makefiles that expect certain commands, such as +changing to a different directory, to not affect other commands in a +target's creation script. You can solve this is either by going +back to executing one shell per command (which is what the +.B \-B +flag forces PMake to do), which slows the process down a good bit and +requires you to use semicolons and escaped newlines for shell constructs, or +by changing the makefile to execute the offending command(s) in a subshell +(by placing the line inside parentheses), like so: +.DS +install :: .MAKE + (cd src; $(.PMAKE) install) + (cd lib; $(.PMAKE) install) + (cd man; $(.PMAKE) install) +.DE +.Ix 0 ref operator double-colon +.Ix 0 ref variable global .PMAKE +.Ix 0 ref .PMAKE +.Ix 0 ref .MAKE +.Ix 0 ref attribute .MAKE +This will always execute the three makes (even if the +.B \-n +flag was given) because of the combination of the ``::'' operator and +the +.CW .MAKE +attribute. Each command will change to the proper directory to perform +the install, leaving the main shell in the directory in which it started. +.xH 3 "DEFCON 1 \*- Imitation is the Not the Highest Form of Flattery" +.Ix 0 ref compatibility +.LP +The final category of makefile is the one where every command requires +input, the dependencies are incompletely specified, or you simply +cannot create more than one target at a time, as mentioned earlier. In +addition, you may not have the time or desire to upgrade the makefile +to run smoothly with PMake. If you are the conservative sort, this is +the compatibility mode for you. It is entered either by giving PMake +the +.B \-M +flag (for Make), or by executing PMake as +.CW make .'' `` +In either case, PMake performs things exactly like Make (while still +supporting most of the nice new features PMake provides). This +includes: +.IP \(bu 2 +No parallel execution. +.IP \(bu 2 +Targets are made in the exact order specified by the makefile. The +sources for each target are made in strict left-to-right order, etc. +.IP \(bu 2 +A single Bourne shell is used to execute each command, thus the +shell's +.CW $$ +variable is useless, changing directories doesn't work across command +lines, etc. +.IP \(bu 2 +If no special characters exist in a command line, PMake will break the +command into words itself and execute the command directly, without +executing a shell first. The characters that cause PMake to execute a +shell are: +.CW # , +.CW = , +.CW | , +.CW ^ , +.CW ( , +.CW ) , +.CW { , +.CW } , +.CW ; , +.CW & , +.CW < , +.CW > , +.CW * , +.CW ? , +.CW [ , +.CW ] , +.CW : , +.CW $ , +.CW ` , +and +.CW \e . +You should notice that these are all the characters that are given +special meaning by the shell (except +.CW ' +and +.CW " , +which PMake deals with all by its lonesome). +.IP \(bu 2 +The use of the null suffix is turned off. +.Ix 0 ref "null suffix" +.Ix 0 ref suffix null +.xH 2 The Way Things Work +.LP +When PMake reads the makefile, it parses sources and targets into +nodes in a graph. The graph is directed only in the sense that PMake +knows which way is up. Each node contains not only links to all its +parents and children (the nodes that depend on it and those on which +it depends, respectively), but also a count of the number of its +children that have already been processed. +.LP +The most important thing to know about how PMake uses this graph is +that the traversal is breadth-first and occurs in two passes. +.LP +After PMake has parsed the makefile, it begins with the nodes the user +has told it to make (either on the command line, or via a +.CW .MAIN +target, or by the target being the first in the file not labeled with +the +.CW .NOTMAIN +attribute) placed in a queue. It continues to take the node off the +front of the queue, mark it as something that needs to be made, pass +the node to +.CW Suff_FindDeps +(mentioned earlier) to find any implicit sources for the node, and +place all the node's children that have yet to be marked at the end of +the queue. If any of the children is a +.CW .USE +rule, its attributes are applied to the parent, then its commands are +appended to the parent's list of commands and its children are linked +to its parent. The parent's unmade children counter is then decremented +(since the +.CW .USE +node has been processed). You will note that this allows a +.CW .USE +node to have children that are +.CW .USE +nodes and the rules will be applied in sequence. +If the node has no children, it is placed at the end of +another queue to be examined in the second pass. This process +continues until the first queue is empty. +.LP +At this point, all the leaves of the graph are in the examination +queue. PMake removes the node at the head of the queue and sees if it +is out-of-date. If it is, it is passed to a function that will execute +the commands for the node asynchronously. When the commands have +completed, all the node's parents have their unmade children counter +decremented and, if the counter is then 0, they are placed on the +examination queue. Likewise, if the node is up-to-date. Only those +parents that were marked on the downward pass are processed in this +way. Thus PMake traverses the graph back up to the nodes the user +instructed it to create. When the examination queue is empty and no +shells are running to create a target, PMake is finished. +.LP +Once all targets have been processed, PMake executes the commands +attached to the +.CW .END +target, either explicitly or through the use of an ellipsis in a shell +script. If there were no errors during the entire process but there +are still some targets unmade (PMake keeps a running count of how many +targets are left to be made), there is a cycle in the graph. PMake does +a depth-first traversal of the graph to find all the targets that +weren't made and prints them out one by one. +.xH 1 Answers to Exercises +.IP (3.1) +This is something of a trick question, for which I apologize. The +trick comes from the UNIX definition of a suffix, which PMake doesn't +necessarily share. You will have noticed that all the suffixes used in +this tutorial (and in UNIX in general) begin with a period +.CW .ms , ( +.CW .c , +etc.). Now, PMake's idea of a suffix is more like English's: it's the +characters at the end of a word. With this in mind, one possible +.Ix 0 def suffix +solution to this problem goes as follows: +.DS I +\&.SUFFIXES : ec.exe .exe ec.obj .obj .asm +ec.objec.exe .obj.exe : + link -o $(.TARGET) $(.IMPSRC) +\&.asmec.obj : + asm -o $(.TARGET) -DDO_ERROR_CHECKING $(.IMPSRC) +\&.asm.obj : + asm -o $(.TARGET) $(.IMPSRC) +.DE +.IP (3.2) +The trick to this one lies in the ``:='' variable-assignment operator +and the ``:S'' variable-expansion modifier. +.Ix 0 ref variable assignment expanded +.Ix 0 ref variable expansion modified +.Ix 0 ref modifier substitute +.Ix 0 ref :S +.Ix 0 ref := +Basically what you want is to take the pointer variable, so to speak, +and transform it into an invocation of the variable at which it +points. You might try something like +.DS I +$(PTR:S/^/\e$(/:S/$/)) +.DE +which places +.CW $( '' `` +at the front of the variable name and +.CW ) '' `` +at the end, thus transforming +.CW VAR ,'' `` +for example, into +.CW $(VAR) ,'' `` +which is just what we want. Unfortunately (as you know if you've tried +it), since, as it says in the hint, PMake does no further substitution +on the result of a modified expansion, that's \fIall\fP you get. The +solution is to make use of ``:='' to place that string into yet +another variable, then invoke the other variable directly: +.DS I +*PTR := $(PTR:S/^/\e$(/:S/$/)/) +.DE +You can then use +.CW $(*PTR) '' `` +to your heart's content. +.de Gp +.XP +\&\fB\\$1:\fP +.. +.xH 1 Glossary of Jargon +.Gp "attribute" +A property given to a target that causes PMake to treat it differently. +.Gp "command script" +The lines immediately following a dependency line that specify +commands to execute to create each of the targets on the dependency +line. Each line in the command script must begin with a tab. +.Gp "command-line variable" +A variable defined in an argument when PMake is first executed. +Overrides all assignments to the same variable name in the makefile. +.Gp "conditional" +A construct much like that used in C that allows a makefile to be +configured on the fly based on the local environment, or on what is being +made by that invocation of PMake. +.Gp "creation script" +Commands used to create a target. See ``command script.'' +.Gp "dependency" +The relationship between a source and a target. This comes in three +flavors, as indicated by the operator between the target and the +source. `:' gives a straight time-wise dependency (if the target is +older than the source, the target is out-of-date), while `!' provides +simply an ordering and always considers the target out-of-date. `::' +is much like `:', save it creates multiple instances of a target each +of which depends on its own list of sources. +.Gp "dynamic source" +This refers to a source that has a local variable invocation in it. It +allows a single dependency line to specify a different source for each +target on the line. +.Gp "global variable" +Any variable defined in a makefile. Takes precedence over variables +defined in the environment, but not over command-line or local variables. +.Gp "input graph" +What PMake constructs from a makefile. Consists of nodes made of the +targets in the makefile, and the links between them (the +dependencies). The links are directed (from source to target) and +there may not be any cycles (loops) in the graph. +.Gp "local variable" +A variable defined by PMake visible only in a target's shell script. +There are seven local variables, not all of which are defined for +every target: +.CW .TARGET , +.CW .ALLSRC , +.CW .OODATE , +.CW .PREFIX , +.CW .IMPSRC , +.CW .ARCHIVE , +and +.CW .MEMBER . +.CW .TARGET , +.CW .PREFIX , +.CW .ARCHIVE , +and +.CW .MEMBER +may be used on dependency lines to create ``dynamic sources.'' +.Gp "makefile" +A file that describes how a system is built. If you don't know what it +is after reading this tutorial.\|.\|.\|. +.Gp "modifier" +A letter, following a colon, used to alter how a variable is expanded. +It has no effect on the variable itself. +.Gp "operator" +What separates a source from a target (on a dependency line) and specifies +the relationship between the two. There are three: +.CW : ', ` +.CW :: ', ` +and +.CW ! '. ` +.Gp "search path" +A list of directories in which a file should be sought. PMake's view +of the contents of directories in a search path does not change once +the makefile has been read. A file is sought on a search path only if +it is exclusively a source. +.Gp "shell" +A program to which commands are passed in order to create targets. +.Gp "source" +Anything to the right of an operator on a dependency line. Targets on +the dependency line are usually created from the sources. +.Gp "special target" +A target that causes PMake to do special things when it's encountered. +.Gp "suffix" +The tail end of a file name. Usually begins with a period, +.CW .c +or +.CW .ms , +e.g. +.Gp "target" +A word to the left of the operator on a dependency line. More +generally, any file that PMake might create. A file may be (and often +is) both a target and a source (what it is depends on how PMake is +looking at it at the time \*- sort of like the wave/particle duality +of light, you know). +.Gp "transformation rule" +A special construct in a makefile that specifies how to create a file +of one type from a file of another, as indicated by their suffixes. +.Gp "variable expansion" +The process of substituting the value of a variable for a reference to +it. Expansion may be altered by means of modifiers. +.Gp "variable" +A place in which to store text that may be retrieved later. Also used +to define the local environment. Conditionals exist that test whether +a variable is defined or not. +.bp +.\" Output table of contents last, with an entry for the index, making +.\" sure to save and restore the last real page number for the index... +.nr @n \n(PN+1 +.\" We are not generating an index +.\" .XS \n(@n +.\" Index +.\" .XE +.nr %% \n% +.PX +.nr % \n(%% diff --git a/bootstrap/bmake/README b/bootstrap/bmake/README new file mode 100644 index 00000000000..005c3228e08 --- /dev/null +++ b/bootstrap/bmake/README @@ -0,0 +1,110 @@ + bmake v3 + +This directory contains a port of the BSD make tool (from NetBSD) +I have run it on SunOS,Solaris,HP-UX 9 and IRIX. + +Version 3 is has been re-worked from scratch to better facilitate +importing newer make(1) versions from NetBSD. The original code base +was NetBSD-1.0, so version 3 was built by doing a fresh import of the +NetBSD-1.0 usr.bin/make, adding the autoconf and other portability +patches to sync it with bmake v2, and then NetBSD's make +of Feb 20, 2000 was imported and conflicts dealt with. +NetBSD's make was again imported on June 6 and December 15, 2000. + +Note: when cvs importing newer versions +it is important to (in usr.bin/make): + +mv config.h make-conf.h +mv Makefile Makefile.in + +before running cvs import. + +Building is simply a matter of: + +configure +make -f makefile.boot +make -f makefile.boot install +make -f makefile.boot install-man +make -f makefile.boot install-mk + +The install-mk target is only useful if you unpacked [bsd-]mk.tar.gz +under the bmake directory. + +if you have GNU make or a make which supports VPATH, you can build it +in a separate directory: + +here=`pwd` +mkdir /tmp/bmake +cd /tmp/bmake +$here/configure +gmake -f makefile.boot +gmake -f makefile.boot install +gmake -f makefile.boot install-man +gmake -f makefile.boot install-mk + +To make much use of bmake you will need the bsd.*.mk macros or my +portable *.mk macros. See +ftp://ftp.quick.com.au/pub/sjg/bsd-mk.tar.gz +ftp://ftp.quick.com.au/pub/sjg/mk.tar.gz + +If you have an earlier version of bmake installed you can use that +with the generated Makefile. + +Apart from new features such as .PARSEDIR picked up from the recent +NetBSD make, this version has improvments (which are also in NetBSD's +make or soon will be) to facilitate using MAKEOBJDIRPREFIX to support +true read-only src trees. See also ChangeLog. + +MAKEOBJDIRPREFIX: + +When MAKEOBJDIRPREFIX is set in the environment make(1) will attempt +to chdir(${MAKEOBJDIRPREFIX}${.CUDRIR}) and use that as its objdir. +Because the directory tree under ${MAKEOBJDIRPREFIX} is a mirror of +the src tree, make ends up chdir'ing to objdirs that would not exist +otherwise. That is, when using normal ./obj dirs (or symlinks) only +Makefiles which include obj.mk get a separate objdir. When using +MAKEOBJDIRPREFIX any directory which has subdirs that use obj.mk will +have an objdir, thus Makefiles which were written expecting to process +in ${.CURDIR} may break. In particular, Makefiles which do: + +build: + ${.MAKE} something + ${.MAKE} else + +will break as the sub-make will not find a Makefile in +${MAKEOBJDIRPREFIX}${.CUDRIR}. Whereas + +build: + cd ${.CURDIR} && ${.MAKE} something + cd ${.CURDIR} && ${.MAKE} else + +will work fine. To avoid the need to re-work these Makefiles we +check for running ${.MAKE} or ${.MAKE:T} without a preceeding cd and +effectively insert one. This feature only operates if +MAKEOBJDIRPREFIX (or MAKEOBJDIR) is set and can be dissabled by +defining NOCHECKMAKECHDIR. + +Another problem arrises from make(1) overriding the physical location +returned by getcwd() with the logical one from $PWD. We dissable this +feature if MAKEOBJDIRPREFIX is set to avoid the situation where +make(1) behaves differently depending on how it got to a directory. +This avoids lossage like the following example. + +If /usr/local/src is a symlink to /d3/src: + +cd /usr/local/src/project +MAKEOBJDIRPREFIX=/tmp/obj make obj +===> sub1 +/d3/src/project/sub1 -> /tmp/obj/d3/src/project/sub1 +... + +cd /usr/local/src/project/sub1 +MAKEOBJDIRPREFIX=/tmp/obj make obj +/usr/local/src/project/sub1 -> /tmp/obj/usr/local/src/project/sub1 + +the expected objdir changes depending on circumstances - which +means that 9 times out of 10 you'll end up trying to polute +curdir because the objdir you expected does not exist. + + +--sjg diff --git a/bootstrap/bmake/aclocal.m4 b/bootstrap/bmake/aclocal.m4 new file mode 100644 index 00000000000..7350a5501a3 --- /dev/null +++ b/bootstrap/bmake/aclocal.m4 @@ -0,0 +1,77 @@ +dnl RCSid: +dnl $Id: aclocal.m4,v 1.1.1.1 2004/03/11 13:04:00 grant Exp $ +dnl + +dnl +dnl AC_CHECK_HEADER_HAS(HEADER, PATTERN, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]])) + +AC_DEFUN(AC_CHECK_HEADER_HAS, +[dnl first check if header exists and if so, see if it contains PATTERN +ac_has_hdr=`echo "ac_cv_header_$1" | sed 'y%./+-%__p_%'` +ac_has_it=`echo "ac_cv_header_$1"_$2 | sed 'y%./+-%__p_%'` +if eval "test \"`echo x'$'$ac_has_hdr`\" = x"; then + AC_CHECK_HEADER($1) +fi +if eval "test \"`echo '$'$ac_has_hdr`\" = yes"; then + ac_x=HAVE_`echo "$1" | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + AC_DEFINE_UNQUOTED($ac_x) + AC_MSG_CHECKING([if $1 has $2]) + AC_CACHE_VAL($ac_has_it, + [eval $ac_has_it=no + AC_EGREP_HEADER($2, $1, eval "$ac_has_it=yes")]) + + if eval "test \"`echo '$'$ac_has_it`\" = yes"; then + AC_MSG_RESULT(yes) + ac_x=HAVE_`echo "$1"_$2 | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + AC_DEFINE_UNQUOTED($ac_x) + ifelse([$3], , :, [$3]) + else + AC_MSG_RESULT(no) + ifelse([$4], , , [$4 +])dnl + fi +fi +]) + +dnl AC_EGREP(PATTERN, FILE, ACTION-IF-FOUND [, +dnl ACTION-IF-NOT-FOUND]) +AC_DEFUN(AC_EGREP, +[ +dnl Prevent m4 from eating character classes: +changequote(, )dnl +if egrep "$1" $2 >/dev/null 2>&1; then +changequote([, ])dnl + ifelse([$3], , :, [$3]) +ifelse([$4], , , [else + $4 +])dnl +fi +]) + +dnl +dnl Test for __attribute__ +dnl + +AC_DEFUN(AC_C___ATTRIBUTE__, [ +AC_MSG_CHECKING(for __attribute__) +AC_CACHE_VAL(ac_cv___attribute__, [ +AC_TRY_COMPILE([ +#include +], +[ +static void foo(void) __attribute__ ((noreturn)); + +static void +foo(void) +{ + exit(1); +} +], +ac_cv___attribute__=yes, +ac_cv___attribute__=no)]) +if test "$ac_cv___attribute__" = "yes"; then + AC_DEFINE(HAVE___ATTRIBUTE__, 1, [define if your compiler has __attribute__]) +fi +AC_MSG_RESULT($ac_cv___attribute__) +]) + diff --git a/bootstrap/bmake/arch.c b/bootstrap/bmake/arch.c new file mode 100644 index 00000000000..c2f840b2bc1 --- /dev/null +++ b/bootstrap/bmake/arch.c @@ -0,0 +1,1344 @@ +/* $NetBSD: arch.c,v 1.1.1.1 2004/03/11 13:04:01 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: arch.c,v 1.1.1.1 2004/03/11 13:04:01 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)arch.c 8.2 (Berkeley) 1/2/94"; +#else +__RCSID("$NetBSD: arch.c,v 1.1.1.1 2004/03/11 13:04:01 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +#if !defined(MAKE_BOOTSTRAP) && !defined(lint) +__IDSTRING(rcs_id,"$Id: arch.c,v 1.1.1.1 2004/03/11 13:04:01 grant Exp $"); +#endif + +/*- + * arch.c -- + * Functions to manipulate libraries, archives and their members. + * + * Once again, cacheing/hashing comes into play in the manipulation + * of archives. The first time an archive is referenced, all of its members' + * headers are read and hashed and the archive closed again. All hashed + * archives are kept on a list which is searched each time an archive member + * is referenced. + * + * The interface to this module is: + * Arch_ParseArchive Given an archive specification, return a list + * of GNode's, one for each member in the spec. + * FAILURE is returned if the specification is + * invalid for some reason. + * + * Arch_Touch Alter the modification time of the archive + * member described by the given node to be + * the current time. + * + * Arch_TouchLib Update the modification time of the library + * described by the given node. This is special + * because it also updates the modification time + * of the library's table of contents. + * + * Arch_MTime Find the modification time of a member of + * an archive *in the archive*. The time is also + * placed in the member's GNode. Returns the + * modification time. + * + * Arch_MemTime Find the modification time of a member of + * an archive. Called when the member doesn't + * already exist. Looks in the archive for the + * modification time. Returns the modification + * time. + * + * Arch_FindLib Search for a library along a path. The + * library name in the GNode should be in + * -l format. + * + * Arch_LibOODate Special function to decide if a library node + * is out-of-date. + * + * Arch_Init Initialize this module. + * + * Arch_End Cleanup this module. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include +#include +#include +#ifdef HAVE_AR_H +#include +#endif +#ifdef HAVE_RANLIB_H +#include +#endif +#ifdef HAVE_UTIME_H +#include +#endif +#include +#include +#include +#include "make.h" +#include "hash.h" +#include "dir.h" + +#ifdef TARGET_MACHINE +#undef MACHINE +#define MACHINE TARGET_MACHINE +#endif +#ifdef TARGET_MACHINE_ARCH +#undef MACHINE_ARCH +#define MACHINE_ARCH TARGET_MACHINE_ARCH +#endif + +static Lst archives; /* Lst of archives we've already examined */ + +typedef struct Arch { + char *name; /* Name of archive */ + Hash_Table members; /* All the members of the archive described + * by key/value pairs */ + char *fnametab; /* Extended name table strings */ + size_t fnamesize; /* Size of the string table */ +} Arch; + +static int ArchFindArchive __P((ClientData, ClientData)); +#ifdef CLEANUP +static void ArchFree __P((ClientData)); +#endif +static struct ar_hdr *ArchStatMember __P((char *, char *, Boolean)); +static FILE *ArchFindMember __P((char *, char *, struct ar_hdr *, char *)); +#if defined(__svr4__) || defined(__SVR4) || defined(__ELF__) +#define SVR4ARCHIVES +static int ArchSVR4Entry __P((Arch *, char *, size_t, FILE *)); +#endif + + +#if defined(_AIX) +# define AR_NAME _ar_name.ar_name +# define AR_FMAG _ar_name.ar_fmag +# define SARMAG SAIAMAG +# define ARMAG AIAMAG +# define ARFMAG AIAFMAG +#endif +#ifndef AR_NAME +# define AR_NAME ar_name +#endif +#ifndef AR_DATE +# define AR_DATE ar_date +#endif +#ifndef AR_SIZE +# define AR_SIZE ar_size +#endif +#ifndef AR_FMAG +# define AR_FMAG ar_fmag +#endif + +#define AR_MAX_NAME_LEN (sizeof(arh.AR_NAME)-1) + +#ifdef CLEANUP +/*- + *----------------------------------------------------------------------- + * ArchFree -- + * Free memory used by an archive + * + * Results: + * None. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static void +ArchFree(ap) + ClientData ap; +{ + Arch *a = (Arch *) ap; + Hash_Search search; + Hash_Entry *entry; + + /* Free memory from hash entries */ + for (entry = Hash_EnumFirst(&a->members, &search); + entry != (Hash_Entry *)NULL; + entry = Hash_EnumNext(&search)) + free((Address) Hash_GetValue (entry)); + + free(a->name); + if (a->fnametab) + free(a->fnametab); + Hash_DeleteTable(&a->members); + free((Address) a); +} +#endif + + +/*- + *----------------------------------------------------------------------- + * Arch_ParseArchive -- + * Parse the archive specification in the given line and find/create + * the nodes for the specified archive members, placing their nodes + * on the given list. + * + * Results: + * SUCCESS if it was a valid specification. The linePtr is updated + * to point to the first non-space after the archive spec. The + * nodes for the members are placed on the given list. + * + * Side Effects: + * Some nodes may be created. The given list is extended. + * + *----------------------------------------------------------------------- + */ +ReturnStatus +Arch_ParseArchive (linePtr, nodeLst, ctxt) + char **linePtr; /* Pointer to start of specification */ + Lst nodeLst; /* Lst on which to place the nodes */ + GNode *ctxt; /* Context in which to expand variables */ +{ + register char *cp; /* Pointer into line */ + GNode *gn; /* New node */ + char *libName; /* Library-part of specification */ + char *memName; /* Member-part of specification */ + char *nameBuf; /* temporary place for node name */ + char saveChar; /* Ending delimiter of member-name */ + Boolean subLibName; /* TRUE if libName should have/had + * variable substitution performed on it */ + + libName = *linePtr; + + subLibName = FALSE; + + for (cp = libName; *cp != '(' && *cp != '\0'; cp++) { + if (*cp == '$') { + /* + * Variable spec, so call the Var module to parse the puppy + * so we can safely advance beyond it... + */ + int length; + Boolean freeIt; + char *result; + + result=Var_Parse(cp, ctxt, TRUE, &length, &freeIt); + if (result == var_Error) { + return(FAILURE); + } else { + subLibName = TRUE; + } + + if (freeIt) { + free(result); + } + cp += length-1; + } + } + + *cp++ = '\0'; + if (subLibName) { + libName = Var_Subst(NULL, libName, ctxt, TRUE); + } + + + for (;;) { + /* + * First skip to the start of the member's name, mark that + * place and skip to the end of it (either white-space or + * a close paren). + */ + Boolean doSubst = FALSE; /* TRUE if need to substitute in memName */ + + while (*cp != '\0' && *cp != ')' && isspace ((unsigned char)*cp)) { + cp++; + } + memName = cp; + while (*cp != '\0' && *cp != ')' && !isspace ((unsigned char)*cp)) { + if (*cp == '$') { + /* + * Variable spec, so call the Var module to parse the puppy + * so we can safely advance beyond it... + */ + int length; + Boolean freeIt; + char *result; + + result=Var_Parse(cp, ctxt, TRUE, &length, &freeIt); + if (result == var_Error) { + return(FAILURE); + } else { + doSubst = TRUE; + } + + if (freeIt) { + free(result); + } + cp += length; + } else { + cp++; + } + } + + /* + * If the specification ends without a closing parenthesis, + * chances are there's something wrong (like a missing backslash), + * so it's better to return failure than allow such things to happen + */ + if (*cp == '\0') { + printf("No closing parenthesis in archive specification\n"); + return (FAILURE); + } + + /* + * If we didn't move anywhere, we must be done + */ + if (cp == memName) { + break; + } + + saveChar = *cp; + *cp = '\0'; + + /* + * XXX: This should be taken care of intelligently by + * SuffExpandChildren, both for the archive and the member portions. + */ + /* + * If member contains variables, try and substitute for them. + * This will slow down archive specs with dynamic sources, of course, + * since we'll be (non-)substituting them three times, but them's + * the breaks -- we need to do this since SuffExpandChildren calls + * us, otherwise we could assume the thing would be taken care of + * later. + */ + if (doSubst) { + char *buf; + char *sacrifice; + char *oldMemName = memName; + size_t sz; + + memName = Var_Subst(NULL, memName, ctxt, TRUE); + + /* + * Now form an archive spec and recurse to deal with nested + * variables and multi-word variable values.... The results + * are just placed at the end of the nodeLst we're returning. + */ + sz = strlen(memName)+strlen(libName)+3; + buf = sacrifice = emalloc(sz); + + snprintf(buf, sz, "%s(%s)", libName, memName); + + if (strchr(memName, '$') && strcmp(memName, oldMemName) == 0) { + /* + * Must contain dynamic sources, so we can't deal with it now. + * Just create an ARCHV node for the thing and let + * SuffExpandChildren handle it... + */ + gn = Targ_FindNode(buf, TARG_CREATE); + + if (gn == NILGNODE) { + free(buf); + return(FAILURE); + } else { + gn->type |= OP_ARCHV; + (void)Lst_AtEnd(nodeLst, (ClientData)gn); + } + } else if (Arch_ParseArchive(&sacrifice, nodeLst, ctxt)!=SUCCESS) { + /* + * Error in nested call -- free buffer and return FAILURE + * ourselves. + */ + free(buf); + return(FAILURE); + } + /* + * Free buffer and continue with our work. + */ + free(buf); + } else if (Dir_HasWildcards(memName)) { + Lst members = Lst_Init(FALSE); + char *member; + size_t sz = MAXPATHLEN, nsz; + nameBuf = emalloc(sz); + + Dir_Expand(memName, dirSearchPath, members); + while (!Lst_IsEmpty(members)) { + member = (char *)Lst_DeQueue(members); + nsz = strlen(libName) + strlen(member) + 3; + if (sz > nsz) + nameBuf = erealloc(nameBuf, sz = nsz * 2); + + snprintf(nameBuf, sz, "%s(%s)", libName, member); + free(member); + gn = Targ_FindNode (nameBuf, TARG_CREATE); + if (gn == NILGNODE) { + free(nameBuf); + return (FAILURE); + } else { + /* + * We've found the node, but have to make sure the rest of + * the world knows it's an archive member, without having + * to constantly check for parentheses, so we type the + * thing with the OP_ARCHV bit before we place it on the + * end of the provided list. + */ + gn->type |= OP_ARCHV; + (void) Lst_AtEnd (nodeLst, (ClientData)gn); + } + } + Lst_Destroy(members, NOFREE); + free(nameBuf); + } else { + size_t sz = strlen(libName) + strlen(memName) + 3; + nameBuf = emalloc(sz); + snprintf(nameBuf, sz, "%s(%s)", libName, memName); + gn = Targ_FindNode (nameBuf, TARG_CREATE); + free(nameBuf); + if (gn == NILGNODE) { + return (FAILURE); + } else { + /* + * We've found the node, but have to make sure the rest of the + * world knows it's an archive member, without having to + * constantly check for parentheses, so we type the thing with + * the OP_ARCHV bit before we place it on the end of the + * provided list. + */ + gn->type |= OP_ARCHV; + (void) Lst_AtEnd (nodeLst, (ClientData)gn); + } + } + if (doSubst) { + free(memName); + } + + *cp = saveChar; + } + + /* + * If substituted libName, free it now, since we need it no longer. + */ + if (subLibName) { + free(libName); + } + + /* + * We promised the pointer would be set up at the next non-space, so + * we must advance cp there before setting *linePtr... (note that on + * entrance to the loop, cp is guaranteed to point at a ')') + */ + do { + cp++; + } while (*cp != '\0' && isspace ((unsigned char)*cp)); + + *linePtr = cp; + return (SUCCESS); +} + +/*- + *----------------------------------------------------------------------- + * ArchFindArchive -- + * See if the given archive is the one we are looking for. Called + * From ArchStatMember and ArchFindMember via Lst_Find. + * + * Results: + * 0 if it is, non-zero if it isn't. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static int +ArchFindArchive (ar, archName) + ClientData ar; /* Current list element */ + ClientData archName; /* Name we want */ +{ + return (strcmp ((char *) archName, ((Arch *) ar)->name)); +} + +/*- + *----------------------------------------------------------------------- + * ArchStatMember -- + * Locate a member of an archive, given the path of the archive and + * the path of the desired member. + * + * Results: + * A pointer to the current struct ar_hdr structure for the member. Note + * That no position is returned, so this is not useful for touching + * archive members. This is mostly because we have no assurances that + * The archive will remain constant after we read all the headers, so + * there's not much point in remembering the position... + * + * Side Effects: + * + *----------------------------------------------------------------------- + */ +static struct ar_hdr * +ArchStatMember (archive, member, hash) + char *archive; /* Path to the archive */ + char *member; /* Name of member. If it is a path, only the + * last component is used. */ + Boolean hash; /* TRUE if archive should be hashed if not + * already so. */ +{ + FILE * arch; /* Stream to archive */ + int size; /* Size of archive member */ + char *cp; /* Useful character pointer */ + char magic[SARMAG]; + LstNode ln; /* Lst member containing archive descriptor */ + Arch *ar; /* Archive descriptor */ + Hash_Entry *he; /* Entry containing member's description */ + struct ar_hdr arh; /* archive-member header for reading archive */ + char memName[MAXPATHLEN+1]; + /* Current member name while hashing. */ + + /* + * Because of space constraints and similar things, files are archived + * using their final path components, not the entire thing, so we need + * to point 'member' to the final component, if there is one, to make + * the comparisons easier... + */ + cp = strrchr (member, '/'); + if (cp != (char *) NULL) { + member = cp + 1; + } + + ln = Lst_Find (archives, (ClientData) archive, ArchFindArchive); + if (ln != NILLNODE) { + ar = (Arch *) Lst_Datum (ln); + + he = Hash_FindEntry (&ar->members, member); + + if (he != (Hash_Entry *) NULL) { + return ((struct ar_hdr *) Hash_GetValue (he)); + } else { + /* Try truncated name */ + char copy[AR_MAX_NAME_LEN+1]; + int len = strlen (member); + + if (len > AR_MAX_NAME_LEN) { + len = AR_MAX_NAME_LEN; + strncpy(copy, member, AR_MAX_NAME_LEN); + copy[AR_MAX_NAME_LEN] = '\0'; + } + if ((he = Hash_FindEntry (&ar->members, copy)) != NULL) + return ((struct ar_hdr *) Hash_GetValue (he)); + return ((struct ar_hdr *) NULL); + } + } + + if (!hash) { + /* + * Caller doesn't want the thing hashed, just use ArchFindMember + * to read the header for the member out and close down the stream + * again. Since the archive is not to be hashed, we assume there's + * no need to allocate extra room for the header we're returning, + * so just declare it static. + */ + static struct ar_hdr sarh; + + arch = ArchFindMember(archive, member, &sarh, "r"); + + if (arch == (FILE *)NULL) { + return ((struct ar_hdr *)NULL); + } else { + fclose(arch); + return (&sarh); + } + } + + /* + * We don't have this archive on the list yet, so we want to find out + * everything that's in it and cache it so we can get at it quickly. + */ + arch = fopen (archive, "r"); + if (arch == (FILE *) NULL) { + return ((struct ar_hdr *) NULL); + } + + /* + * We use the ARMAG string to make sure this is an archive we + * can handle... + */ + if ((fread (magic, SARMAG, 1, arch) != 1) || + (strncmp (magic, ARMAG, SARMAG) != 0)) { + fclose (arch); + return ((struct ar_hdr *) NULL); + } + + ar = (Arch *)emalloc (sizeof (Arch)); + ar->name = estrdup (archive); + ar->fnametab = NULL; + ar->fnamesize = 0; + Hash_InitTable (&ar->members, -1); + memName[AR_MAX_NAME_LEN] = '\0'; + + while (fread ((char *)&arh, sizeof (struct ar_hdr), 1, arch) == 1) { + if (strncmp ( arh.AR_FMAG, ARFMAG, sizeof (arh.AR_FMAG)) != 0) { + /* + * The header is bogus, so the archive is bad + * and there's no way we can recover... + */ + goto badarch; + } else { + /* + * We need to advance the stream's pointer to the start of the + * next header. Files are padded with newlines to an even-byte + * boundary, so we need to extract the size of the file from the + * 'size' field of the header and round it up during the seek. + */ + arh.AR_SIZE[sizeof(arh.AR_SIZE)-1] = '\0'; + size = (int) strtol(arh.AR_SIZE, NULL, 10); + + (void) strncpy (memName, arh.AR_NAME, sizeof(arh.AR_NAME)); + for (cp = &memName[AR_MAX_NAME_LEN]; *cp == ' '; cp--) { + continue; + } + cp[1] = '\0'; + +#ifdef SVR4ARCHIVES + /* + * svr4 names are slash terminated. Also svr4 extended AR format. + */ + if (memName[0] == '/') { + /* + * svr4 magic mode; handle it + */ + switch (ArchSVR4Entry(ar, memName, size, arch)) { + case -1: /* Invalid data */ + goto badarch; + case 0: /* List of files entry */ + continue; + default: /* Got the entry */ + break; + } + } + else { + if (cp[0] == '/') + cp[0] = '\0'; + } +#endif + +#ifdef AR_EFMT1 + /* + * BSD 4.4 extended AR format: #1/, with name as the + * first bytes of the file + */ + if (strncmp(memName, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 && + isdigit(memName[sizeof(AR_EFMT1) - 1])) { + + unsigned int elen = atoi(&memName[sizeof(AR_EFMT1)-1]); + + if (elen > MAXPATHLEN) + goto badarch; + if (fread (memName, elen, 1, arch) != 1) + goto badarch; + memName[elen] = '\0'; + fseek (arch, -elen, SEEK_CUR); + if (DEBUG(ARCH) || DEBUG(MAKE)) { + printf("ArchStat: Extended format entry for %s\n", memName); + } + } +#endif + + he = Hash_CreateEntry (&ar->members, memName, (Boolean *)NULL); + Hash_SetValue (he, (ClientData)emalloc (sizeof (struct ar_hdr))); + memcpy ((Address)Hash_GetValue (he), (Address)&arh, + sizeof (struct ar_hdr)); + } + fseek (arch, (size + 1) & ~1, SEEK_CUR); + } + + fclose (arch); + + (void) Lst_AtEnd (archives, (ClientData) ar); + + /* + * Now that the archive has been read and cached, we can look into + * the hash table to find the desired member's header. + */ + he = Hash_FindEntry (&ar->members, member); + + if (he != (Hash_Entry *) NULL) { + return ((struct ar_hdr *) Hash_GetValue (he)); + } else { + return ((struct ar_hdr *) NULL); + } + +badarch: + fclose (arch); + Hash_DeleteTable (&ar->members); + if (ar->fnametab) + free(ar->fnametab); + free ((Address)ar); + return ((struct ar_hdr *) NULL); +} + +#ifdef SVR4ARCHIVES +/*- + *----------------------------------------------------------------------- + * ArchSVR4Entry -- + * Parse an SVR4 style entry that begins with a slash. + * If it is "//", then load the table of filenames + * If it is "/", then try to substitute the long file name + * from offset of a table previously read. + * + * Results: + * -1: Bad data in archive + * 0: A table was loaded from the file + * 1: Name was successfully substituted from table + * 2: Name was not successfully substituted from table + * + * Side Effects: + * If a table is read, the file pointer is moved to the next archive + * member + * + *----------------------------------------------------------------------- + */ +static int +ArchSVR4Entry(ar, name, size, arch) + Arch *ar; + char *name; + size_t size; + FILE *arch; +{ +#define ARLONGNAMES1 "//" +#define ARLONGNAMES2 "/ARFILENAMES" + size_t entry; + char *ptr, *eptr; + + if (strncmp(name, ARLONGNAMES1, sizeof(ARLONGNAMES1) - 1) == 0 || + strncmp(name, ARLONGNAMES2, sizeof(ARLONGNAMES2) - 1) == 0) { + + if (ar->fnametab != NULL) { + if (DEBUG(ARCH)) { + printf("Attempted to redefine an SVR4 name table\n"); + } + return -1; + } + + /* + * This is a table of archive names, so we build one for + * ourselves + */ + ar->fnametab = emalloc(size); + ar->fnamesize = size; + + if (fread(ar->fnametab, size, 1, arch) != 1) { + if (DEBUG(ARCH)) { + printf("Reading an SVR4 name table failed\n"); + } + return -1; + } + eptr = ar->fnametab + size; + for (entry = 0, ptr = ar->fnametab; ptr < eptr; ptr++) + switch (*ptr) { + case '/': + entry++; + *ptr = '\0'; + break; + + case '\n': + break; + + default: + break; + } + if (DEBUG(ARCH)) { + printf("Found svr4 archive name table with %lu entries\n", + (u_long)entry); + } + return 0; + } + + if (name[1] == ' ' || name[1] == '\0') + return 2; + + entry = (size_t) strtol(&name[1], &eptr, 0); + if ((*eptr != ' ' && *eptr != '\0') || eptr == &name[1]) { + if (DEBUG(ARCH)) { + printf("Could not parse SVR4 name %s\n", name); + } + return 2; + } + if (entry >= ar->fnamesize) { + if (DEBUG(ARCH)) { + printf("SVR4 entry offset %s is greater than %lu\n", + name, (u_long)ar->fnamesize); + } + return 2; + } + + if (DEBUG(ARCH)) { + printf("Replaced %s with %s\n", name, &ar->fnametab[entry]); + } + + (void) strncpy(name, &ar->fnametab[entry], MAXPATHLEN); + name[MAXPATHLEN] = '\0'; + return 1; +} +#endif + + +/*- + *----------------------------------------------------------------------- + * ArchFindMember -- + * Locate a member of an archive, given the path of the archive and + * the path of the desired member. If the archive is to be modified, + * the mode should be "r+", if not, it should be "r". + * + * Results: + * An FILE *, opened for reading and writing, positioned at the + * start of the member's struct ar_hdr, or NULL if the member was + * nonexistent. The current struct ar_hdr for member. + * + * Side Effects: + * The passed struct ar_hdr structure is filled in. + * + *----------------------------------------------------------------------- + */ +static FILE * +ArchFindMember (archive, member, arhPtr, mode) + char *archive; /* Path to the archive */ + char *member; /* Name of member. If it is a path, only the + * last component is used. */ + struct ar_hdr *arhPtr; /* Pointer to header structure to be filled in */ + char *mode; /* The mode for opening the stream */ +{ + FILE * arch; /* Stream to archive */ + int size; /* Size of archive member */ + char *cp; /* Useful character pointer */ + char magic[SARMAG]; + int len, tlen; + + arch = fopen (archive, mode); + if (arch == (FILE *) NULL) { + return ((FILE *) NULL); + } + + /* + * We use the ARMAG string to make sure this is an archive we + * can handle... + */ + if ((fread (magic, SARMAG, 1, arch) != 1) || + (strncmp (magic, ARMAG, SARMAG) != 0)) { + fclose (arch); + return ((FILE *) NULL); + } + + /* + * Because of space constraints and similar things, files are archived + * using their final path components, not the entire thing, so we need + * to point 'member' to the final component, if there is one, to make + * the comparisons easier... + */ + cp = strrchr (member, '/'); + if (cp != (char *) NULL) { + member = cp + 1; + } + len = tlen = strlen (member); + if (len > sizeof (arhPtr->AR_NAME)) { + tlen = sizeof (arhPtr->AR_NAME); + } + + while (fread ((char *)arhPtr, sizeof (struct ar_hdr), 1, arch) == 1) { + if (strncmp(arhPtr->AR_FMAG, ARFMAG, sizeof (arhPtr->AR_FMAG) ) != 0) { + /* + * The header is bogus, so the archive is bad + * and there's no way we can recover... + */ + fclose (arch); + return ((FILE *) NULL); + } else if (strncmp (member, arhPtr->AR_NAME, tlen) == 0) { + /* + * If the member's name doesn't take up the entire 'name' field, + * we have to be careful of matching prefixes. Names are space- + * padded to the right, so if the character in 'name' at the end + * of the matched string is anything but a space, this isn't the + * member we sought. + */ + if (tlen != sizeof(arhPtr->AR_NAME) && arhPtr->AR_NAME[tlen] != ' '){ + goto skip; + } else { + /* + * To make life easier, we reposition the file at the start + * of the header we just read before we return the stream. + * In a more general situation, it might be better to leave + * the file at the actual member, rather than its header, but + * not here... + */ + fseek (arch, -sizeof(struct ar_hdr), SEEK_CUR); + return (arch); + } + } else +#ifdef AR_EFMT1 + /* + * BSD 4.4 extended AR format: #1/, with name as the + * first bytes of the file + */ + if (strncmp(arhPtr->AR_NAME, AR_EFMT1, + sizeof(AR_EFMT1) - 1) == 0 && + isdigit(arhPtr->AR_NAME[sizeof(AR_EFMT1) - 1])) { + + unsigned int elen = atoi(&arhPtr->AR_NAME[sizeof(AR_EFMT1)-1]); + char ename[MAXPATHLEN]; + + if (elen > MAXPATHLEN) { + fclose (arch); + return NULL; + } + if (fread (ename, elen, 1, arch) != 1) { + fclose (arch); + return NULL; + } + ename[elen] = '\0'; + if (DEBUG(ARCH) || DEBUG(MAKE)) { + printf("ArchFind: Extended format entry for %s\n", ename); + } + if (strncmp(ename, member, len) == 0) { + /* Found as extended name */ + fseek (arch, -sizeof(struct ar_hdr) - elen, SEEK_CUR); + return (arch); + } + fseek (arch, -elen, SEEK_CUR); + goto skip; + } else +#endif + { +skip: + /* + * This isn't the member we're after, so we need to advance the + * stream's pointer to the start of the next header. Files are + * padded with newlines to an even-byte boundary, so we need to + * extract the size of the file from the 'size' field of the + * header and round it up during the seek. + */ + arhPtr->AR_SIZE[sizeof(arhPtr->AR_SIZE)-1] = '\0'; + size = (int) strtol(arhPtr->AR_SIZE, NULL, 10); + fseek (arch, (size + 1) & ~1, SEEK_CUR); + } + } + + /* + * We've looked everywhere, but the member is not to be found. Close the + * archive and return NULL -- an error. + */ + fclose (arch); + return ((FILE *) NULL); +} + +/*- + *----------------------------------------------------------------------- + * Arch_Touch -- + * Touch a member of an archive. + * + * Results: + * The 'time' field of the member's header is updated. + * + * Side Effects: + * The modification time of the entire archive is also changed. + * For a library, this could necessitate the re-ranlib'ing of the + * whole thing. + * + *----------------------------------------------------------------------- + */ +void +Arch_Touch (gn) + GNode *gn; /* Node of member to touch */ +{ + FILE * arch; /* Stream open to archive, positioned properly */ + struct ar_hdr arh; /* Current header describing member */ + char *p1, *p2; + + arch = ArchFindMember(Var_Value (ARCHIVE, gn, &p1), + Var_Value (MEMBER, gn, &p2), + &arh, "r+"); + if (p1) + free(p1); + if (p2) + free(p2); + snprintf(arh.AR_DATE, sizeof(arh.AR_DATE), "%-12ld", (long) now); + + if (arch != (FILE *) NULL) { + (void)fwrite ((char *)&arh, sizeof (struct ar_hdr), 1, arch); + fclose (arch); + } +} + +/*- + *----------------------------------------------------------------------- + * Arch_TouchLib -- + * Given a node which represents a library, touch the thing, making + * sure that the table of contents also is touched. + * + * Results: + * None. + * + * Side Effects: + * Both the modification time of the library and of the RANLIBMAG + * member are set to 'now'. + * + *----------------------------------------------------------------------- + */ +void +Arch_TouchLib (gn) + GNode *gn; /* The node of the library to touch */ +{ +#ifdef RANLIBMAG + FILE * arch; /* Stream open to archive */ + struct ar_hdr arh; /* Header describing table of contents */ + struct utimbuf times; /* Times for utime() call */ + + arch = ArchFindMember (gn->path, RANLIBMAG, &arh, "r+"); + snprintf(arh.AR_DATE, sizeof(arh.AR_DATE), "%-12ld", (long) now); + + if (arch != (FILE *) NULL) { + (void)fwrite ((char *)&arh, sizeof (struct ar_hdr), 1, arch); + fclose (arch); + + times.actime = times.modtime = now; + utime(gn->path, ×); + } +#endif +} + +/*- + *----------------------------------------------------------------------- + * Arch_MTime -- + * Return the modification time of a member of an archive. + * + * Results: + * The modification time (seconds). + * + * Side Effects: + * The mtime field of the given node is filled in with the value + * returned by the function. + * + *----------------------------------------------------------------------- + */ +time_t +Arch_MTime (gn) + GNode *gn; /* Node describing archive member */ +{ + struct ar_hdr *arhPtr; /* Header of desired member */ + time_t modTime; /* Modification time as an integer */ + char *p1, *p2; + + arhPtr = ArchStatMember (Var_Value (ARCHIVE, gn, &p1), + Var_Value (MEMBER, gn, &p2), + TRUE); + if (p1) + free(p1); + if (p2) + free(p2); + + if (arhPtr != (struct ar_hdr *) NULL) { + modTime = (time_t) strtol(arhPtr->AR_DATE, NULL, 10); + } else { + modTime = 0; + } + + gn->mtime = modTime; + return (modTime); +} + +/*- + *----------------------------------------------------------------------- + * Arch_MemMTime -- + * Given a non-existent archive member's node, get its modification + * time from its archived form, if it exists. + * + * Results: + * The modification time. + * + * Side Effects: + * The mtime field is filled in. + * + *----------------------------------------------------------------------- + */ +time_t +Arch_MemMTime (gn) + GNode *gn; +{ + LstNode ln; + GNode *pgn; + char *nameStart, + *nameEnd; + + if (Lst_Open (gn->parents) != SUCCESS) { + gn->mtime = 0; + return (0); + } + while ((ln = Lst_Next (gn->parents)) != NILLNODE) { + pgn = (GNode *) Lst_Datum (ln); + + if (pgn->type & OP_ARCHV) { + /* + * If the parent is an archive specification and is being made + * and its member's name matches the name of the node we were + * given, record the modification time of the parent in the + * child. We keep searching its parents in case some other + * parent requires this child to exist... + */ + nameStart = strchr (pgn->name, '(') + 1; + nameEnd = strchr (nameStart, ')'); + + if ((pgn->flags & REMAKE) && + strncmp(nameStart, gn->name, nameEnd - nameStart) == 0) { + gn->mtime = Arch_MTime(pgn); + } + } else if (pgn->flags & REMAKE) { + /* + * Something which isn't a library depends on the existence of + * this target, so it needs to exist. + */ + gn->mtime = 0; + break; + } + } + + Lst_Close (gn->parents); + + return (gn->mtime); +} + +/*- + *----------------------------------------------------------------------- + * Arch_FindLib -- + * Search for a library along the given search path. + * + * Results: + * None. + * + * Side Effects: + * The node's 'path' field is set to the found path (including the + * actual file name, not -l...). If the system can handle the -L + * flag when linking (or we cannot find the library), we assume that + * the user has placed the .LIBRARIES variable in the final linking + * command (or the linker will know where to find it) and set the + * TARGET variable for this node to be the node's name. Otherwise, + * we set the TARGET variable to be the full path of the library, + * as returned by Dir_FindFile. + * + *----------------------------------------------------------------------- + */ +void +Arch_FindLib (gn, path) + GNode *gn; /* Node of library to find */ + Lst path; /* Search path */ +{ + char *libName; /* file name for archive */ + size_t sz = strlen(gn->name) + 6 - 2; + + libName = (char *)emalloc(sz); + snprintf(libName, sz, "lib%s.a", &gn->name[2]); + + gn->path = Dir_FindFile (libName, path); + + free (libName); + +#ifdef LIBRARIES + Var_Set (TARGET, gn->name, gn, 0); +#else + Var_Set (TARGET, gn->path == (char *) NULL ? gn->name : gn->path, gn, 0); +#endif /* LIBRARIES */ +} + +/*- + *----------------------------------------------------------------------- + * Arch_LibOODate -- + * Decide if a node with the OP_LIB attribute is out-of-date. Called + * from Make_OODate to make its life easier. + * + * There are several ways for a library to be out-of-date that are + * not available to ordinary files. In addition, there are ways + * that are open to regular files that are not available to + * libraries. A library that is only used as a source is never + * considered out-of-date by itself. This does not preclude the + * library's modification time from making its parent be out-of-date. + * A library will be considered out-of-date for any of these reasons, + * given that it is a target on a dependency line somewhere: + * Its modification time is less than that of one of its + * sources (gn->mtime < gn->cmtime). + * Its modification time is greater than the time at which the + * make began (i.e. it's been modified in the course + * of the make, probably by archiving). + * The modification time of one of its sources is greater than + * the one of its RANLIBMAG member (i.e. its table of contents + * is out-of-date). We don't compare of the archive time + * vs. TOC time because they can be too close. In my + * opinion we should not bother with the TOC at all since + * this is used by 'ar' rules that affect the data contents + * of the archive, not by ranlib rules, which affect the + * TOC. + * + * Results: + * TRUE if the library is out-of-date. FALSE otherwise. + * + * Side Effects: + * The library will be hashed if it hasn't been already. + * + *----------------------------------------------------------------------- + */ +Boolean +Arch_LibOODate (gn) + GNode *gn; /* The library's graph node */ +{ + Boolean oodate; + + if (gn->type & OP_PHONY) { + oodate = TRUE; + } else if (OP_NOP(gn->type) && Lst_IsEmpty(gn->children)) { + oodate = FALSE; + } else if ((gn->cmtime == 0) || (gn->mtime > now) || + (gn->mtime < gn->cmtime)) { + oodate = TRUE; + } else { +#ifdef RANLIBMAG + struct ar_hdr *arhPtr; /* Header for __.SYMDEF */ + int modTimeTOC; /* The table-of-contents's mod time */ + + arhPtr = ArchStatMember (gn->path, RANLIBMAG, FALSE); + + if (arhPtr != (struct ar_hdr *)NULL) { + modTimeTOC = (int) strtol(arhPtr->AR_DATE, NULL, 10); + + if (DEBUG(ARCH) || DEBUG(MAKE)) { + printf("%s modified %s...", RANLIBMAG, Targ_FmtTime(modTimeTOC)); + } + oodate = (gn->cmtime > modTimeTOC); + } else { + /* + * A library w/o a table of contents is out-of-date + */ + if (DEBUG(ARCH) || DEBUG(MAKE)) { + printf("No t.o.c...."); + } + oodate = TRUE; + } +#else + oodate = FALSE; +#endif + } + return (oodate); +} + +/*- + *----------------------------------------------------------------------- + * Arch_Init -- + * Initialize things for this module. + * + * Results: + * None. + * + * Side Effects: + * The 'archives' list is initialized. + * + *----------------------------------------------------------------------- + */ +void +Arch_Init () +{ + archives = Lst_Init (FALSE); +} + + + +/*- + *----------------------------------------------------------------------- + * Arch_End -- + * Cleanup things for this module. + * + * Results: + * None. + * + * Side Effects: + * The 'archives' list is freed + * + *----------------------------------------------------------------------- + */ +void +Arch_End () +{ +#ifdef CLEANUP + Lst_Destroy(archives, ArchFree); +#endif +} + +/*- + *----------------------------------------------------------------------- + * Arch_IsLib -- + * Check if the node is a library + * + * Results: + * True or False. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +int +Arch_IsLib(gn) + GNode *gn; +{ + static const char armag[] = "!\n"; + char buf[sizeof(armag)-1]; + int fd; + + if ((fd = open(gn->path, O_RDONLY)) == -1) + return FALSE; + + if (read(fd, buf, sizeof(buf)) != sizeof(buf)) { + (void) close(fd); + return FALSE; + } + + (void) close(fd); + + return memcmp(buf, armag, sizeof(buf)) == 0; +} diff --git a/bootstrap/bmake/bit.h b/bootstrap/bmake/bit.h new file mode 100644 index 00000000000..6753471bf70 --- /dev/null +++ b/bootstrap/bmake/bit.h @@ -0,0 +1,102 @@ +/* $NetBSD: bit.h,v 1.1.1.1 2004/03/11 13:04:01 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. + * Copyright (c) 1988, 1989 by Adam de Boor + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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: @(#)bit.h 5.3 (Berkeley) 6/1/90 + */ + +/* + * bit.h -- + * + * Definition of macros for setting and clearing bits in an array + * of integers. + * + * It is assumed that "int" is 32 bits wide. + */ + +#ifndef _BIT +#define _BIT + +#include "sprite.h" + +#define BIT_NUM_BITS_PER_INT 32 +#define BIT_NUM_BITS_PER_BYTE 8 + +#define Bit_NumInts(numBits) \ + (((numBits)+BIT_NUM_BITS_PER_INT -1)/BIT_NUM_BITS_PER_INT) + +#define Bit_NumBytes(numBits) \ + (Bit_NumInts(numBits) * sizeof(int)) + +#define Bit_Alloc(numBits, bitArrayPtr) \ + bitArrayPtr = (int *) emalloc((unsigned)Bit_NumBytes(numBits)); \ + Bit_Zero((numBits), (bitArrayPtr)) + +#define Bit_Free(bitArrayPtr) \ + free((char *)bitArrayPtr) + +#define Bit_Set(numBits, bitArrayPtr) \ + ((bitArrayPtr)[(numBits)/BIT_NUM_BITS_PER_INT] |= \ + (1 << ((numBits) % BIT_NUM_BITS_PER_INT))) + +#define Bit_IsSet(numBits, bitArrayPtr) \ + ((bitArrayPtr)[(numBits)/BIT_NUM_BITS_PER_INT] & \ + (1 << ((numBits) % BIT_NUM_BITS_PER_INT))) + +#define Bit_Clear(numBits, bitArrayPtr) \ + ((bitArrayPtr)[(numBits)/BIT_NUM_BITS_PER_INT] &= \ + ~(1 << ((numBits) % BIT_NUM_BITS_PER_INT))) + +#define Bit_IsClear(numBits, bitArrayPtr) \ + (!(Bit_IsSet((numBits), (bitArrayPtr)))) + +#define Bit_Copy(numBits, srcArrayPtr, destArrayPtr) \ + memmove((char *)(destArrayPtr), (char *)(srcArrayPtr), \ + Bit_NumBytes(numBits)) + +#define Bit_Zero(numBits, bitArrayPtr) \ + memset((char *)(bitArrayPtr), 0, Bit_NumBytes(numBits)) + +extern int Bit_FindFirstSet(); +extern int Bit_FindFirstClear(); +extern Boolean Bit_Intersect(); +extern Boolean Bit_Union(); +extern Boolean Bit_AnySet(); +extern int *Bit_Expand(); + +#endif /* _BIT */ diff --git a/bootstrap/bmake/buf.c b/bootstrap/bmake/buf.c new file mode 100644 index 00000000000..c2eb21db12b --- /dev/null +++ b/bootstrap/bmake/buf.c @@ -0,0 +1,308 @@ +/* $NetBSD: buf.c,v 1.1.1.1 2004/03/11 13:04:01 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. + * Copyright (c) 1988, 1989 by Adam de Boor + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: buf.c,v 1.1.1.1 2004/03/11 13:04:01 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)buf.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: buf.c,v 1.1.1.1 2004/03/11 13:04:01 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * buf.c -- + * Functions for automatically-expanded buffers. + */ + +#include "sprite.h" +#include "make.h" +#include "buf.h" + +#ifndef max +#define max(a,b) ((a) > (b) ? (a) : (b)) +#endif + +/* + * BufExpand -- + * Expand the given buffer to hold the given number of additional + * bytes. + * Makes sure there's room for an extra NULL byte at the end of the + * buffer in case it holds a string. + */ +#define BufExpand(bp,nb) \ + while (bp->left < (nb)+1) {\ + int newSize = (bp)->size * 2; \ + Byte *newBuf = (Byte *) erealloc((bp)->buffer, newSize); \ + \ + (bp)->inPtr = newBuf + ((bp)->inPtr - (bp)->buffer); \ + (bp)->outPtr = newBuf + ((bp)->outPtr - (bp)->buffer);\ + (bp)->buffer = newBuf;\ + (bp)->size = newSize;\ + (bp)->left = newSize - ((bp)->inPtr - (bp)->buffer);\ + } + +#define BUF_DEF_SIZE 256 /* Default buffer size */ + +/*- + *----------------------------------------------------------------------- + * Buf_OvAddByte -- + * Add a single byte to the buffer. left is zero or negative. + * + * Results: + * None. + * + * Side Effects: + * The buffer may be expanded. + * + *----------------------------------------------------------------------- + */ +void +Buf_OvAddByte (bp, byte) + register Buffer bp; + int byte; +{ + int nbytes = 1; + bp->left = 0; + BufExpand (bp, nbytes); + + *bp->inPtr++ = byte; + bp->left--; + + /* + * Null-terminate + */ + *bp->inPtr = 0; +} + +/*- + *----------------------------------------------------------------------- + * Buf_AddBytes -- + * Add a number of bytes to the buffer. + * + * Results: + * None. + * + * Side Effects: + * Guess what? + * + *----------------------------------------------------------------------- + */ +void +Buf_AddBytes (bp, numBytes, bytesPtr) + register Buffer bp; + int numBytes; + const Byte *bytesPtr; +{ + + BufExpand (bp, numBytes); + + memcpy (bp->inPtr, bytesPtr, numBytes); + bp->inPtr += numBytes; + bp->left -= numBytes; + + /* + * Null-terminate + */ + *bp->inPtr = 0; +} + +/*- + *----------------------------------------------------------------------- + * Buf_GetAll -- + * Get all the available data at once. + * + * Results: + * A pointer to the data and the number of bytes available. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +Byte * +Buf_GetAll (bp, numBytesPtr) + register Buffer bp; + int *numBytesPtr; +{ + + if (numBytesPtr != (int *)NULL) { + *numBytesPtr = bp->inPtr - bp->outPtr; + } + + return (bp->outPtr); +} + +/*- + *----------------------------------------------------------------------- + * Buf_Discard -- + * Throw away bytes in a buffer. + * + * Results: + * None. + * + * Side Effects: + * The bytes are discarded. + * + *----------------------------------------------------------------------- + */ +void +Buf_Discard (bp, numBytes) + register Buffer bp; + int numBytes; +{ + + if (bp->inPtr - bp->outPtr <= numBytes) { + bp->inPtr = bp->outPtr = bp->buffer; + bp->left = bp->size; + *bp->inPtr = 0; + } else { + bp->outPtr += numBytes; + } +} + +/*- + *----------------------------------------------------------------------- + * Buf_Size -- + * Returns the number of bytes in the given buffer. Doesn't include + * the null-terminating byte. + * + * Results: + * The number of bytes. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +int +Buf_Size (buf) + Buffer buf; +{ + return (buf->inPtr - buf->outPtr); +} + +/*- + *----------------------------------------------------------------------- + * Buf_Init -- + * Initialize a buffer. If no initial size is given, a reasonable + * default is used. + * + * Results: + * A buffer to be given to other functions in this library. + * + * Side Effects: + * The buffer is created, the space allocated and pointers + * initialized. + * + *----------------------------------------------------------------------- + */ +Buffer +Buf_Init (size) + int size; /* Initial size for the buffer */ +{ + Buffer bp; /* New Buffer */ + + bp = (Buffer)emalloc(sizeof(*bp)); + + if (size <= 0) { + size = BUF_DEF_SIZE; + } + bp->left = bp->size = size; + bp->buffer = (Byte *)emalloc(size); + bp->inPtr = bp->outPtr = bp->buffer; + *bp->inPtr = 0; + + return (bp); +} + +/*- + *----------------------------------------------------------------------- + * Buf_Destroy -- + * Nuke a buffer and all its resources. + * + * Results: + * None. + * + * Side Effects: + * The buffer is freed. + * + *----------------------------------------------------------------------- + */ +void +Buf_Destroy (buf, freeData) + Buffer buf; /* Buffer to destroy */ + Boolean freeData; /* TRUE if the data should be destroyed as well */ +{ + + if (freeData) { + free ((char *)buf->buffer); + } + free ((char *)buf); +} + +/*- + *----------------------------------------------------------------------- + * Buf_ReplaceLastByte -- + * Replace the last byte in a buffer. + * + * Results: + * None. + * + * Side Effects: + * If the buffer was empty intially, then a new byte will be added. + * Otherwise, the last byte is overwritten. + * + *----------------------------------------------------------------------- + */ +void +Buf_ReplaceLastByte (buf, byte) + Buffer buf; /* buffer to augment */ + int byte; /* byte to be written */ +{ + if (buf->inPtr == buf->outPtr) + Buf_AddByte(buf, byte); + else + *(buf->inPtr - 1) = byte; +} diff --git a/bootstrap/bmake/buf.h b/bootstrap/bmake/buf.h new file mode 100644 index 00000000000..b3462a38683 --- /dev/null +++ b/bootstrap/bmake/buf.h @@ -0,0 +1,79 @@ +/* $NetBSD: buf.h,v 1.1.1.1 2004/03/11 13:04:01 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. + * Copyright (c) 1988, 1989 by Adam de Boor + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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: @(#)buf.h 8.1 (Berkeley) 6/6/93 + */ + +/*- + * buf.h -- + * Header for users of the buf library. + */ + +#ifndef _BUF_H +#define _BUF_H + +#include "sprite.h" + +typedef char Byte; + +typedef struct Buffer { + int size; /* Current size of the buffer */ + int left; /* Space left (== size - (inPtr - buffer)) */ + Byte *buffer; /* The buffer itself */ + Byte *inPtr; /* Place to write to */ + Byte *outPtr; /* Place to read from */ +} *Buffer; + +/* Buf_AddByte adds a single byte to a buffer. */ +#define Buf_AddByte(bp, byte) \ + (void) (--(bp)->left <= 0 ? Buf_OvAddByte(bp, byte), 1 : \ + (*(bp)->inPtr++ = (byte), *(bp)->inPtr = 0), 1) + +#define BUF_ERROR 256 + +void Buf_OvAddByte __P((Buffer, int)); +void Buf_AddBytes __P((Buffer, int, const Byte *)); +Byte *Buf_GetAll __P((Buffer, int *)); +void Buf_Discard __P((Buffer, int)); +int Buf_Size __P((Buffer)); +Buffer Buf_Init __P((int)); +void Buf_Destroy __P((Buffer, Boolean)); +void Buf_ReplaceLastByte __P((Buffer, int)); + +#endif /* _BUF_H */ diff --git a/bootstrap/bmake/compat.c b/bootstrap/bmake/compat.c new file mode 100644 index 00000000000..5086742c2f4 --- /dev/null +++ b/bootstrap/bmake/compat.c @@ -0,0 +1,636 @@ +/* $NetBSD: compat.c,v 1.1.1.1 2004/03/11 13:04:01 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. + * Copyright (c) 1988, 1989 by Adam de Boor + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: compat.c,v 1.1.1.1 2004/03/11 13:04:01 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)compat.c 8.2 (Berkeley) 3/19/94"; +#else +__RCSID("$NetBSD: compat.c,v 1.1.1.1 2004/03/11 13:04:01 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +#if !defined(MAKE_BOOTSTRAP) && !defined(lint) +__IDSTRING(rcs_id,"$Id: compat.c,v 1.1.1.1 2004/03/11 13:04:01 grant Exp $"); +#endif + +/*- + * compat.c -- + * The routines in this file implement the full-compatibility + * mode of PMake. Most of the special functionality of PMake + * is available in this mode. Things not supported: + * - different shells. + * - friendly variable substitution. + * + * Interface: + * Compat_Run Initialize things for this module and recreate + * thems as need creatin' + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include +#include +#include "wait.h" +#include +#include +#include +#include +#include +#include +#include "make.h" +#include "hash.h" +#include "dir.h" +#include "job.h" + +/* + * The following array is used to make a fast determination of which + * characters are interpreted specially by the shell. If a command + * contains any of these characters, it is executed by the shell, not + * directly by us. + */ + +static char meta[256]; + +static GNode *curTarg = NILGNODE; +static GNode *ENDNode; +static void CompatInterrupt __P((int)); +static int CompatRunCommand __P((ClientData, ClientData)); +static int CompatMake __P((ClientData, ClientData)); + +/*- + *----------------------------------------------------------------------- + * CompatInterrupt -- + * Interrupt the creation of the current target and remove it if + * it ain't precious. + * + * Results: + * None. + * + * Side Effects: + * The target is removed and the process exits. If .INTERRUPT exists, + * its commands are run first WITH INTERRUPTS IGNORED.. + * + *----------------------------------------------------------------------- + */ +static void +CompatInterrupt (signo) + int signo; +{ + GNode *gn; + + if ((curTarg != NILGNODE) && !Targ_Precious (curTarg)) { + char *p1; + char *file = Var_Value (TARGET, curTarg, &p1); + + if (!noExecute && eunlink(file) != -1) { + Error("*** %s removed\n", file); + } + if (p1) + free(p1); + + /* + * Run .INTERRUPT only if hit with interrupt signal + */ + if (signo == SIGINT) { + gn = Targ_FindNode(".INTERRUPT", TARG_NOCREATE); + if (gn != NILGNODE) { + Lst_ForEach(gn->commands, CompatRunCommand, (ClientData)gn); + } + } + + } + exit (signo); +} + +/*- + *----------------------------------------------------------------------- + * CompatRunCommand -- + * Execute the next command for a target. If the command returns an + * error, the node's made field is set to ERROR and creation stops. + * + * Results: + * 0 if the command succeeded, 1 if an error occurred. + * + * Side Effects: + * The node's 'made' field may be set to ERROR. + * + *----------------------------------------------------------------------- + */ +static int +CompatRunCommand (cmdp, gnp) + ClientData cmdp; /* Command to execute */ + ClientData gnp; /* Node from which the command came */ +{ + char *cmdStart; /* Start of expanded command */ + char *cp, *bp; + Boolean silent, /* Don't print command */ + errCheck; /* Check errors */ + WAIT_T reason; /* Reason for child's death */ + int status; /* Description of child's death */ + int cpid; /* Child actually found */ + ReturnStatus stat; /* Status of fork */ + LstNode cmdNode; /* Node where current command is located */ + char **av; /* Argument vector for thing to exec */ + int argc; /* Number of arguments in av or 0 if not + * dynamically allocated */ + Boolean local; /* TRUE if command should be executed + * locally */ + char *cmd = (char *) cmdp; + GNode *gn = (GNode *) gnp; + + /* + * Avoid clobbered variable warnings by forcing the compiler + * to ``unregister'' variables + */ +#if __GNUC__ + (void) &av; + (void) &errCheck; +#endif + silent = gn->type & OP_SILENT; + errCheck = !(gn->type & OP_IGNORE); + + cmdNode = Lst_Member (gn->commands, (ClientData)cmd); + cmdStart = Var_Subst (NULL, cmd, gn, FALSE); + + /* + * brk_string will return an argv with a NULL in av[0], thus causing + * execvp to choke and die horribly. Besides, how can we execute a null + * command? In any case, we warn the user that the command expanded to + * nothing (is this the right thing to do?). + */ + + if (*cmdStart == '\0') { + free(cmdStart); + Error("%s expands to empty string", cmd); + return(0); + } else { + cmd = cmdStart; + } + Lst_Replace (cmdNode, (ClientData)cmdStart); + + if ((gn->type & OP_SAVE_CMDS) && (gn != ENDNode)) { + (void)Lst_AtEnd(ENDNode->commands, (ClientData)cmdStart); + return(0); + } else if (strcmp(cmdStart, "...") == 0) { + gn->type |= OP_SAVE_CMDS; + return(0); + } + + while ((*cmd == '@') || (*cmd == '-')) { + if (*cmd == '@') { + silent = TRUE; + } else { + errCheck = FALSE; + } + cmd++; + } + + while (isspace((unsigned char)*cmd)) + cmd++; + + /* + * Search for meta characters in the command. If there are no meta + * characters, there's no need to execute a shell to execute the + * command. + */ + for (cp = cmd; !meta[(unsigned char)*cp]; cp++) { + continue; + } + + /* + * Print the command before echoing if we're not supposed to be quiet for + * this one. We also print the command if -n given. + */ + if (!silent || NoExecute(gn)) { + printf ("%s\n", cmd); + fflush(stdout); + } + + /* + * If we're not supposed to execute any commands, this is as far as + * we go... + */ + if (NoExecute(gn)) { + return (0); + } + + if (*cp != '\0') { + /* + * If *cp isn't the null character, we hit a "meta" character and + * need to pass the command off to the shell. We give the shell the + * -e flag as well as -c if it's supposed to exit when it hits an + * error. + */ + static char *shargv[4] = { "/bin/sh" }; + + if (DEBUG(SHELL)) + shargv[1] = (errCheck ? "-exc" : "-xc"); + else + shargv[1] = (errCheck ? "-ec" : "-c"); + shargv[2] = cmd; + shargv[3] = (char *)NULL; + av = shargv; + argc = 0; + bp = NULL; + } else { + /* + * No meta-characters, so no need to exec a shell. Break the command + * into words to form an argument vector we can execute. + */ + av = brk_string(cmd, &argc, TRUE, &bp); + } + + local = TRUE; + + /* + * Fork and execute the single command. If the fork fails, we abort. + */ + cpid = vfork(); + if (cpid < 0) { + Fatal("Could not fork"); + } + if (cpid == 0) { + Check_Cwd(av); + if (local) + (void)execvp(av[0], av); + else + (void)execv(av[0], av); + execError(av[0]); + _exit(1); + } + if (bp) { + free(av); + free(bp); + } + free(cmdStart); + Lst_Replace (cmdNode, (ClientData) NULL); + + /* + * The child is off and running. Now all we can do is wait... + */ + while (1) { + + while ((stat = wait(&reason)) != cpid) { + if (stat == -1 && errno != EINTR) { + break; + } + } + + if (stat > -1) { + if (WIFSTOPPED(reason)) { + status = WSTOPSIG(reason); /* stopped */ + } else if (WIFEXITED(reason)) { + status = WEXITSTATUS(reason); /* exited */ + if (status != 0) { + printf ("*** Error code %d", status); + } + } else { + status = WTERMSIG(reason); /* signaled */ + printf ("*** Signal %d", status); + } + + + if (!WIFEXITED(reason) || (status != 0)) { + if (errCheck) { + gn->made = ERROR; + if (keepgoing) { + /* + * Abort the current target, but let others + * continue. + */ + printf (" (continuing)\n"); + } + } else { + /* + * Continue executing commands for this target. + * If we return 0, this will happen... + */ + printf (" (ignored)\n"); + status = 0; + } + } + break; + } else { + Fatal ("error in wait: %d: %s", stat, strerror(errno)); + /*NOTREACHED*/ + } + } + + return (status); +} + +/*- + *----------------------------------------------------------------------- + * CompatMake -- + * Make a target. + * + * Results: + * 0 + * + * Side Effects: + * If an error is detected and not being ignored, the process exits. + * + *----------------------------------------------------------------------- + */ +static int +CompatMake (gnp, pgnp) + ClientData gnp; /* The node to make */ + ClientData pgnp; /* Parent to abort if necessary */ +{ + GNode *gn = (GNode *) gnp; + GNode *pgn = (GNode *) pgnp; + + if (pgn->type & OP_MADE) { + (void) Dir_MTime(gn); + gn->made = UPTODATE; + } + + if (gn->made == UNMADE) { + /* + * First mark ourselves to be made, then apply whatever transformations + * the suffix module thinks are necessary. Once that's done, we can + * descend and make all our children. If any of them has an error + * but the -k flag was given, our 'make' field will be set FALSE again. + * This is our signal to not attempt to do anything but abort our + * parent as well. + */ + gn->flags |= REMAKE; + gn->made = BEINGMADE; + Suff_FindDeps (gn); + Lst_ForEach (gn->children, CompatMake, (ClientData)gn); + if ((gn->flags & REMAKE) == 0) { + gn->made = ABORTED; + pgn->flags &= ~REMAKE; + goto cohorts; + } + + if (Lst_Member (gn->iParents, pgn) != NILLNODE) { + char *p1; + Var_Set (IMPSRC, Var_Value(TARGET, gn, &p1), pgn, 0); + if (p1) + free(p1); + } + + /* + * All the children were made ok. Now cmtime contains the modification + * time of the newest child, we need to find out if we exist and when + * we were modified last. The criteria for datedness are defined by the + * Make_OODate function. + */ + if (DEBUG(MAKE)) { + printf("Examining %s...", gn->name); + } + if (! Make_OODate(gn)) { + gn->made = UPTODATE; + if (DEBUG(MAKE)) { + printf("up-to-date.\n"); + } + goto cohorts; + } else if (DEBUG(MAKE)) { + printf("out-of-date.\n"); + } + + /* + * If the user is just seeing if something is out-of-date, exit now + * to tell him/her "yes". + */ + if (queryFlag) { + exit (1); + } + + /* + * We need to be re-made. We also have to make sure we've got a $? + * variable. To be nice, we also define the $> variable using + * Make_DoAllVar(). + */ + Make_DoAllVar(gn); + + /* + * Alter our type to tell if errors should be ignored or things + * should not be printed so CompatRunCommand knows what to do. + */ + if (Targ_Ignore (gn)) { + gn->type |= OP_IGNORE; + } + if (Targ_Silent (gn)) { + gn->type |= OP_SILENT; + } + + if (Job_CheckCommands (gn, Fatal)) { + /* + * Our commands are ok, but we still have to worry about the -t + * flag... + */ + if (!touchFlag || (gn->type & OP_MAKE)) { + curTarg = gn; + Lst_ForEach (gn->commands, CompatRunCommand, (ClientData)gn); + curTarg = NILGNODE; + } else { + Job_Touch (gn, gn->type & OP_SILENT); + } + } else { + gn->made = ERROR; + } + + if (gn->made != ERROR) { + /* + * If the node was made successfully, mark it so, update + * its modification time and timestamp all its parents. Note + * that for .ZEROTIME targets, the timestamping isn't done. + * This is to keep its state from affecting that of its parent. + */ + gn->made = MADE; + pgn->flags |= Make_Recheck(gn) == 0 ? FORCE : 0; + if (!(gn->type & OP_EXEC)) { + pgn->flags |= CHILDMADE; + Make_TimeStamp(pgn, gn); + } + } else if (keepgoing) { + pgn->flags &= ~REMAKE; + } else { + PrintOnError("\n\nStop."); + exit (1); + } + } else if (gn->made == ERROR) { + /* + * Already had an error when making this beastie. Tell the parent + * to abort. + */ + pgn->flags &= ~REMAKE; + } else { + if (Lst_Member (gn->iParents, pgn) != NILLNODE) { + char *p1; + Var_Set (IMPSRC, Var_Value(TARGET, gn, &p1), pgn, 0); + if (p1) + free(p1); + } + switch(gn->made) { + case BEINGMADE: + Error("Graph cycles through %s\n", gn->name); + gn->made = ERROR; + pgn->flags &= ~REMAKE; + break; + case MADE: + if ((gn->type & OP_EXEC) == 0) { + pgn->flags |= CHILDMADE; + Make_TimeStamp(pgn, gn); + } + break; + case UPTODATE: + if ((gn->type & OP_EXEC) == 0) { + Make_TimeStamp(pgn, gn); + } + break; + default: + break; + } + } + +cohorts: + Lst_ForEach (gn->cohorts, CompatMake, pgnp); + return (0); +} + +/*- + *----------------------------------------------------------------------- + * Compat_Run -- + * Initialize this mode and start making. + * + * Results: + * None. + * + * Side Effects: + * Guess what? + * + *----------------------------------------------------------------------- + */ +void +Compat_Run(targs) + Lst targs; /* List of target nodes to re-create */ +{ + char *cp; /* Pointer to string of shell meta-characters */ + GNode *gn = NULL;/* Current root target */ + int errors; /* Number of targets not remade due to errors */ + + if (signal(SIGINT, SIG_IGN) != SIG_IGN) { + signal(SIGINT, CompatInterrupt); + } + if (signal(SIGTERM, SIG_IGN) != SIG_IGN) { + signal(SIGTERM, CompatInterrupt); + } + if (signal(SIGHUP, SIG_IGN) != SIG_IGN) { + signal(SIGHUP, CompatInterrupt); + } + if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) { + signal(SIGQUIT, CompatInterrupt); + } + + for (cp = "#=|^(){};&<>*?[]:$`\\\n"; *cp != '\0'; cp++) { + meta[(unsigned char) *cp] = 1; + } + /* + * The null character serves as a sentinel in the string. + */ + meta[0] = 1; + + ENDNode = Targ_FindNode(".END", TARG_CREATE); + /* + * If the user has defined a .BEGIN target, execute the commands attached + * to it. + */ + if (!queryFlag) { + gn = Targ_FindNode(".BEGIN", TARG_NOCREATE); + if (gn != NILGNODE) { + Lst_ForEach(gn->commands, CompatRunCommand, (ClientData)gn); + if (gn->made == ERROR) { + PrintOnError("\n\nStop."); + exit(1); + } + } + } + + /* + * Expand .USE nodes right now, because they can modify the structure + * of the tree. + */ + Lst_Destroy(Make_ExpandUse(targs), NOFREE); + + /* + * For each entry in the list of targets to create, call CompatMake on + * it to create the thing. CompatMake will leave the 'made' field of gn + * in one of several states: + * UPTODATE gn was already up-to-date + * MADE gn was recreated successfully + * ERROR An error occurred while gn was being created + * ABORTED gn was not remade because one of its inferiors + * could not be made due to errors. + */ + errors = 0; + while (!Lst_IsEmpty (targs)) { + gn = (GNode *) Lst_DeQueue (targs); + CompatMake (gn, gn); + + if (gn->made == UPTODATE) { + printf ("`%s' is up to date.\n", gn->name); + } else if (gn->made == ABORTED) { + printf ("`%s' not remade because of errors.\n", gn->name); + errors += 1; + } + } + + /* + * If the user has defined a .END target, run its commands. + */ + if (errors == 0) { + Lst_ForEach(ENDNode->commands, CompatRunCommand, (ClientData)gn); + if (gn->made == ERROR) { + PrintOnError("\n\nStop."); + exit(1); + } + } +} diff --git a/bootstrap/bmake/cond.c b/bootstrap/bmake/cond.c new file mode 100644 index 00000000000..8fdb30ca07b --- /dev/null +++ b/bootstrap/bmake/cond.c @@ -0,0 +1,1352 @@ +/* $NetBSD: cond.c,v 1.1.1.1 2004/03/11 13:04:01 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. + * Copyright (c) 1988, 1989 by Adam de Boor + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: cond.c,v 1.1.1.1 2004/03/11 13:04:01 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)cond.c 8.2 (Berkeley) 1/2/94"; +#else +__RCSID("$NetBSD: cond.c,v 1.1.1.1 2004/03/11 13:04:01 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * cond.c -- + * Functions to handle conditionals in a makefile. + * + * Interface: + * Cond_Eval Evaluate the conditional in the passed line. + * + */ + +#include +#include +#include "make.h" +#include "hash.h" +#include "dir.h" +#include "buf.h" + +/* + * The parsing of conditional expressions is based on this grammar: + * E -> F || E + * E -> F + * F -> T && F + * F -> T + * T -> defined(variable) + * T -> make(target) + * T -> exists(file) + * T -> empty(varspec) + * T -> target(name) + * T -> commands(name) + * T -> symbol + * T -> $(varspec) op value + * T -> $(varspec) == "string" + * T -> $(varspec) != "string" + * T -> ( E ) + * T -> ! T + * op -> == | != | > | < | >= | <= + * + * 'symbol' is some other symbol to which the default function (condDefProc) + * is applied. + * + * Tokens are scanned from the 'condExpr' string. The scanner (CondToken) + * will return And for '&' and '&&', Or for '|' and '||', Not for '!', + * LParen for '(', RParen for ')' and will evaluate the other terminal + * symbols, using either the default function or the function given in the + * terminal, and return the result as either True or False. + * + * All Non-Terminal functions (CondE, CondF and CondT) return Err on error. + */ +typedef enum { + And, Or, Not, True, False, LParen, RParen, EndOfFile, None, Err +} Token; + +/*- + * Structures to handle elegantly the different forms of #if's. The + * last two fields are stored in condInvert and condDefProc, respectively. + */ +static void CondPushBack __P((Token)); +static int CondGetArg __P((char **, char **, char *, Boolean)); +static Boolean CondDoDefined __P((int, char *)); +static int CondStrMatch __P((ClientData, ClientData)); +static Boolean CondDoMake __P((int, char *)); +static Boolean CondDoExists __P((int, char *)); +static Boolean CondDoTarget __P((int, char *)); +static Boolean CondDoCommands __P((int, char *)); +static Boolean CondCvtArg __P((char *, double *)); +static Token CondToken __P((Boolean)); +static Token CondT __P((Boolean)); +static Token CondF __P((Boolean)); +static Token CondE __P((Boolean)); + +static struct If { + char *form; /* Form of if */ + int formlen; /* Length of form */ + Boolean doNot; /* TRUE if default function should be negated */ + Boolean (*defProc) __P((int, char *)); /* Default function to apply */ +} ifs[] = { + { "ifdef", 5, FALSE, CondDoDefined }, + { "ifndef", 6, TRUE, CondDoDefined }, + { "ifmake", 6, FALSE, CondDoMake }, + { "ifnmake", 7, TRUE, CondDoMake }, + { "if", 2, FALSE, CondDoDefined }, + { NULL, 0, FALSE, NULL } +}; + +static Boolean condInvert; /* Invert the default function */ +static Boolean (*condDefProc) /* Default function to apply */ + __P((int, char *)); +static char *condExpr; /* The expression to parse */ +static Token condPushBack=None; /* Single push-back token used in + * parsing */ + +#define MAXIF 30 /* greatest depth of #if'ing */ + +static Boolean condStack[MAXIF]; /* Stack of conditionals's values */ +static int condTop = MAXIF; /* Top-most conditional */ +static int skipIfLevel=0; /* Depth of skipped conditionals */ +static Boolean skipLine = FALSE; /* Whether the parse module is skipping + * lines */ + +/*- + *----------------------------------------------------------------------- + * CondPushBack -- + * Push back the most recent token read. We only need one level of + * this, so the thing is just stored in 'condPushback'. + * + * Results: + * None. + * + * Side Effects: + * condPushback is overwritten. + * + *----------------------------------------------------------------------- + */ +static void +CondPushBack (t) + Token t; /* Token to push back into the "stream" */ +{ + condPushBack = t; +} + +/*- + *----------------------------------------------------------------------- + * CondGetArg -- + * Find the argument of a built-in function. + * + * Results: + * The length of the argument and the address of the argument. + * + * Side Effects: + * The pointer is set to point to the closing parenthesis of the + * function call. + * + *----------------------------------------------------------------------- + */ +static int +CondGetArg (linePtr, argPtr, func, parens) + char **linePtr; + char **argPtr; + char *func; + Boolean parens; /* TRUE if arg should be bounded by parens */ +{ + register char *cp; + int argLen; + register Buffer buf; + + cp = *linePtr; + if (parens) { + while (*cp != '(' && *cp != '\0') { + cp++; + } + if (*cp == '(') { + cp++; + } + } + + if (*cp == '\0') { + /* + * No arguments whatsoever. Because 'make' and 'defined' aren't really + * "reserved words", we don't print a message. I think this is better + * than hitting the user with a warning message every time s/he uses + * the word 'make' or 'defined' at the beginning of a symbol... + */ + *argPtr = cp; + return (0); + } + + while (*cp == ' ' || *cp == '\t') { + cp++; + } + + /* + * Create a buffer for the argument and start it out at 16 characters + * long. Why 16? Why not? + */ + buf = Buf_Init(16); + + while ((strchr(" \t)&|", *cp) == (char *)NULL) && (*cp != '\0')) { + if (*cp == '$') { + /* + * Parse the variable spec and install it as part of the argument + * if it's valid. We tell Var_Parse to complain on an undefined + * variable, so we don't do it too. Nor do we return an error, + * though perhaps we should... + */ + char *cp2; + int len; + Boolean doFree; + + cp2 = Var_Parse(cp, VAR_CMD, TRUE, &len, &doFree); + + Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2); + if (doFree) { + free(cp2); + } + cp += len; + } else { + Buf_AddByte(buf, (Byte)*cp); + cp++; + } + } + + Buf_AddByte(buf, (Byte)'\0'); + *argPtr = (char *)Buf_GetAll(buf, &argLen); + Buf_Destroy(buf, FALSE); + + while (*cp == ' ' || *cp == '\t') { + cp++; + } + if (parens && *cp != ')') { + Parse_Error (PARSE_WARNING, "Missing closing parenthesis for %s()", + func); + return (0); + } else if (parens) { + /* + * Advance pointer past close parenthesis. + */ + cp++; + } + + *linePtr = cp; + return (argLen); +} + +/*- + *----------------------------------------------------------------------- + * CondDoDefined -- + * Handle the 'defined' function for conditionals. + * + * Results: + * TRUE if the given variable is defined. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static Boolean +CondDoDefined (argLen, arg) + int argLen; + char *arg; +{ + char savec = arg[argLen]; + char *p1; + Boolean result; + + arg[argLen] = '\0'; + if (Var_Value (arg, VAR_CMD, &p1) != (char *)NULL) { + result = TRUE; + } else { + result = FALSE; + } + if (p1) + free(p1); + arg[argLen] = savec; + return (result); +} + +/*- + *----------------------------------------------------------------------- + * CondStrMatch -- + * Front-end for Str_Match so it returns 0 on match and non-zero + * on mismatch. Callback function for CondDoMake via Lst_Find + * + * Results: + * 0 if string matches pattern + * + * Side Effects: + * None + * + *----------------------------------------------------------------------- + */ +static int +CondStrMatch(string, pattern) + ClientData string; + ClientData pattern; +{ + return(!Str_Match((char *) string,(char *) pattern)); +} + +/*- + *----------------------------------------------------------------------- + * CondDoMake -- + * Handle the 'make' function for conditionals. + * + * Results: + * TRUE if the given target is being made. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static Boolean +CondDoMake (argLen, arg) + int argLen; + char *arg; +{ + char savec = arg[argLen]; + Boolean result; + + arg[argLen] = '\0'; + if (Lst_Find (create, (ClientData)arg, CondStrMatch) == NILLNODE) { + result = FALSE; + } else { + result = TRUE; + } + arg[argLen] = savec; + return (result); +} + +/*- + *----------------------------------------------------------------------- + * CondDoExists -- + * See if the given file exists. + * + * Results: + * TRUE if the file exists and FALSE if it does not. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static Boolean +CondDoExists (argLen, arg) + int argLen; + char *arg; +{ + char savec = arg[argLen]; + Boolean result; + char *path; + + arg[argLen] = '\0'; + path = Dir_FindFile(arg, dirSearchPath); + if (path != (char *)NULL) { + result = TRUE; + free(path); + } else { + result = FALSE; + } + arg[argLen] = savec; + return (result); +} + +/*- + *----------------------------------------------------------------------- + * CondDoTarget -- + * See if the given node exists and is an actual target. + * + * Results: + * TRUE if the node exists as a target and FALSE if it does not. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static Boolean +CondDoTarget (argLen, arg) + int argLen; + char *arg; +{ + char savec = arg[argLen]; + Boolean result; + GNode *gn; + + arg[argLen] = '\0'; + gn = Targ_FindNode(arg, TARG_NOCREATE); + if ((gn != NILGNODE) && !OP_NOP(gn->type)) { + result = TRUE; + } else { + result = FALSE; + } + arg[argLen] = savec; + return (result); +} + +/*- + *----------------------------------------------------------------------- + * CondDoCommands -- + * See if the given node exists and is an actual target with commands + * associated with it. + * + * Results: + * TRUE if the node exists as a target and has commands associated with + * it and FALSE if it does not. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static Boolean +CondDoCommands (argLen, arg) + int argLen; + char *arg; +{ + char savec = arg[argLen]; + Boolean result; + GNode *gn; + + arg[argLen] = '\0'; + gn = Targ_FindNode(arg, TARG_NOCREATE); + if ((gn != NILGNODE) && !OP_NOP(gn->type) && !Lst_IsEmpty(gn->commands)) { + result = TRUE; + } else { + result = FALSE; + } + arg[argLen] = savec; + return (result); +} + +/*- + *----------------------------------------------------------------------- + * CondCvtArg -- + * Convert the given number into a double. If the number begins + * with 0x, it is interpreted as a hexadecimal integer + * and converted to a double from there. All other strings just have + * strtod called on them. + * + * Results: + * Sets 'value' to double value of string. + * Returns true if the string was a valid number, false o.w. + * + * Side Effects: + * Can change 'value' even if string is not a valid number. + * + * + *----------------------------------------------------------------------- + */ +static Boolean +CondCvtArg(str, value) + register char *str; + double *value; +{ + if ((*str == '0') && (str[1] == 'x')) { + register long i; + + for (str += 2, i = 0; *str; str++) { + int x; + if (isdigit((unsigned char) *str)) + x = *str - '0'; + else if (isxdigit((unsigned char) *str)) + x = 10 + *str - isupper((unsigned char) *str) ? 'A' : 'a'; + else + return FALSE; + i = (i << 4) + x; + } + *value = (double) i; + return TRUE; + } + else { + char *eptr; + *value = strtod(str, &eptr); + return *eptr == '\0'; + } +} + +/*- + *----------------------------------------------------------------------- + * CondToken -- + * Return the next token from the input. + * + * Results: + * A Token for the next lexical token in the stream. + * + * Side Effects: + * condPushback will be set back to None if it is used. + * + *----------------------------------------------------------------------- + */ +static Token +CondToken(doEval) + Boolean doEval; +{ + Token t; + + if (condPushBack == None) { + while (*condExpr == ' ' || *condExpr == '\t') { + condExpr++; + } + switch (*condExpr) { + case '(': + t = LParen; + condExpr++; + break; + case ')': + t = RParen; + condExpr++; + break; + case '|': + if (condExpr[1] == '|') { + condExpr++; + } + condExpr++; + t = Or; + break; + case '&': + if (condExpr[1] == '&') { + condExpr++; + } + condExpr++; + t = And; + break; + case '!': + t = Not; + condExpr++; + break; + case '\n': + case '\0': + t = EndOfFile; + break; + case '$': { + char *lhs; + char *rhs; + char *op; + int varSpecLen; + Boolean doFree; + + /* + * Parse the variable spec and skip over it, saving its + * value in lhs. + */ + t = Err; + lhs = Var_Parse(condExpr, VAR_CMD, doEval,&varSpecLen,&doFree); + if (lhs == var_Error) { + /* + * Even if !doEval, we still report syntax errors, which + * is what getting var_Error back with !doEval means. + */ + return(Err); + } + condExpr += varSpecLen; + + if (!isspace((unsigned char) *condExpr) && + strchr("!=><", *condExpr) == NULL) { + Buffer buf; + char *cp; + + buf = Buf_Init(0); + + for (cp = lhs; *cp; cp++) + Buf_AddByte(buf, (Byte)*cp); + + if (doFree) + free(lhs); + + for (;*condExpr && !isspace((unsigned char) *condExpr); + condExpr++) + Buf_AddByte(buf, (Byte)*condExpr); + + Buf_AddByte(buf, (Byte)'\0'); + lhs = (char *)Buf_GetAll(buf, &varSpecLen); + Buf_Destroy(buf, FALSE); + + doFree = TRUE; + } + + /* + * Skip whitespace to get to the operator + */ + while (isspace((unsigned char) *condExpr)) + condExpr++; + + /* + * Make sure the operator is a valid one. If it isn't a + * known relational operator, pretend we got a + * != 0 comparison. + */ + op = condExpr; + switch (*condExpr) { + case '!': + case '=': + case '<': + case '>': + if (condExpr[1] == '=') { + condExpr += 2; + } else { + condExpr += 1; + } + break; + default: + op = "!="; + rhs = "0"; + + goto do_compare; + } + while (isspace((unsigned char) *condExpr)) { + condExpr++; + } + if (*condExpr == '\0') { + Parse_Error(PARSE_WARNING, + "Missing right-hand-side of operator"); + goto error; + } + rhs = condExpr; +do_compare: + if (*rhs == '"') { + /* + * Doing a string comparison. Only allow == and != for + * operators. + */ + char *string; + char *cp, *cp2; + int qt; + Buffer buf; + +do_string_compare: + if (((*op != '!') && (*op != '=')) || (op[1] != '=')) { + Parse_Error(PARSE_WARNING, + "String comparison operator should be either == or !="); + goto error; + } + + buf = Buf_Init(0); + qt = *rhs == '"' ? 1 : 0; + + for (cp = &rhs[qt]; + ((qt && (*cp != '"')) || + (!qt && strchr(" \t)", *cp) == NULL)) && + (*cp != '\0'); cp++) { + if ((*cp == '\\') && (cp[1] != '\0')) { + /* + * Backslash escapes things -- skip over next + * character, if it exists. + */ + cp++; + Buf_AddByte(buf, (Byte)*cp); + } else if (*cp == '$') { + int len; + Boolean freeIt; + + cp2 = Var_Parse(cp, VAR_CMD, doEval,&len, &freeIt); + if (cp2 != var_Error) { + Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2); + if (freeIt) { + free(cp2); + } + cp += len - 1; + } else { + Buf_AddByte(buf, (Byte)*cp); + } + } else { + Buf_AddByte(buf, (Byte)*cp); + } + } + + Buf_AddByte(buf, (Byte)0); + + string = (char *)Buf_GetAll(buf, (int *)0); + Buf_Destroy(buf, FALSE); + + if (DEBUG(COND)) { + printf("lhs = \"%s\", rhs = \"%s\", op = %.2s\n", + lhs, string, op); + } + /* + * Null-terminate rhs and perform the comparison. + * t is set to the result. + */ + if (*op == '=') { + t = strcmp(lhs, string) ? False : True; + } else { + t = strcmp(lhs, string) ? True : False; + } + free(string); + if (rhs == condExpr) { + if (!qt && *cp == ')') + condExpr = cp; + else + condExpr = cp + 1; + } + } else { + /* + * rhs is either a float or an integer. Convert both the + * lhs and the rhs to a double and compare the two. + */ + double left, right; + char *string; + + if (!CondCvtArg(lhs, &left)) + goto do_string_compare; + if (*rhs == '$') { + int len; + Boolean freeIt; + + string = Var_Parse(rhs, VAR_CMD, doEval,&len,&freeIt); + if (string == var_Error) { + right = 0.0; + } else { + if (!CondCvtArg(string, &right)) { + if (freeIt) + free(string); + goto do_string_compare; + } + if (freeIt) + free(string); + if (rhs == condExpr) + condExpr += len; + } + } else { + if (!CondCvtArg(rhs, &right)) + goto do_string_compare; + if (rhs == condExpr) { + /* + * Skip over the right-hand side + */ + while(!isspace((unsigned char) *condExpr) && + (*condExpr != '\0')) { + condExpr++; + } + } + } + + if (DEBUG(COND)) { + printf("left = %f, right = %f, op = %.2s\n", left, + right, op); + } + switch(op[0]) { + case '!': + if (op[1] != '=') { + Parse_Error(PARSE_WARNING, + "Unknown operator"); + goto error; + } + t = (left != right ? True : False); + break; + case '=': + if (op[1] != '=') { + Parse_Error(PARSE_WARNING, + "Unknown operator"); + goto error; + } + t = (left == right ? True : False); + break; + case '<': + if (op[1] == '=') { + t = (left <= right ? True : False); + } else { + t = (left < right ? True : False); + } + break; + case '>': + if (op[1] == '=') { + t = (left >= right ? True : False); + } else { + t = (left > right ? True : False); + } + break; + } + } +error: + if (doFree) + free(lhs); + break; + } + default: { + Boolean (*evalProc) __P((int, char *)); + Boolean invert = FALSE; + char *arg; + int arglen; + + if (strncmp (condExpr, "defined", 7) == 0) { + /* + * Use CondDoDefined to evaluate the argument and + * CondGetArg to extract the argument from the 'function + * call'. + */ + evalProc = CondDoDefined; + condExpr += 7; + arglen = CondGetArg (&condExpr, &arg, "defined", TRUE); + if (arglen == 0) { + condExpr -= 7; + goto use_default; + } + } else if (strncmp (condExpr, "make", 4) == 0) { + /* + * Use CondDoMake to evaluate the argument and + * CondGetArg to extract the argument from the 'function + * call'. + */ + evalProc = CondDoMake; + condExpr += 4; + arglen = CondGetArg (&condExpr, &arg, "make", TRUE); + if (arglen == 0) { + condExpr -= 4; + goto use_default; + } + } else if (strncmp (condExpr, "exists", 6) == 0) { + /* + * Use CondDoExists to evaluate the argument and + * CondGetArg to extract the argument from the + * 'function call'. + */ + evalProc = CondDoExists; + condExpr += 6; + arglen = CondGetArg(&condExpr, &arg, "exists", TRUE); + if (arglen == 0) { + condExpr -= 6; + goto use_default; + } + } else if (strncmp(condExpr, "empty", 5) == 0) { + /* + * Use Var_Parse to parse the spec in parens and return + * True if the resulting string is empty. + */ + int length; + Boolean doFree; + char *val; + + condExpr += 5; + + for (arglen = 0; + condExpr[arglen] != '(' && condExpr[arglen] != '\0'; + arglen += 1) + continue; + + if (condExpr[arglen] != '\0') { + val = Var_Parse(&condExpr[arglen - 1], VAR_CMD, + doEval, &length, &doFree); + if (val == var_Error) { + t = Err; + } else { + /* + * A variable is empty when it just contains + * spaces... 4/15/92, christos + */ + char *p; + for (p = val; *p && isspace((unsigned char)*p); p++) + continue; + t = (*p == '\0') ? True : False; + } + if (doFree) { + free(val); + } + /* + * Advance condExpr to beyond the closing ). Note that + * we subtract one from arglen + length b/c length + * is calculated from condExpr[arglen - 1]. + */ + condExpr += arglen + length - 1; + } else { + condExpr -= 5; + goto use_default; + } + break; + } else if (strncmp (condExpr, "target", 6) == 0) { + /* + * Use CondDoTarget to evaluate the argument and + * CondGetArg to extract the argument from the + * 'function call'. + */ + evalProc = CondDoTarget; + condExpr += 6; + arglen = CondGetArg(&condExpr, &arg, "target", TRUE); + if (arglen == 0) { + condExpr -= 6; + goto use_default; + } + } else if (strncmp (condExpr, "commands", 8) == 0) { + /* + * Use CondDoCommands to evaluate the argument and + * CondGetArg to extract the argument from the + * 'function call'. + */ + evalProc = CondDoCommands; + condExpr += 8; + arglen = CondGetArg(&condExpr, &arg, "commands", TRUE); + if (arglen == 0) { + condExpr -= 8; + goto use_default; + } + } else { + /* + * The symbol is itself the argument to the default + * function. We advance condExpr to the end of the symbol + * by hand (the next whitespace, closing paren or + * binary operator) and set to invert the evaluation + * function if condInvert is TRUE. + */ + use_default: + invert = condInvert; + evalProc = condDefProc; + arglen = CondGetArg(&condExpr, &arg, "", FALSE); + } + + /* + * Evaluate the argument using the set function. If invert + * is TRUE, we invert the sense of the function. + */ + t = (!doEval || (* evalProc) (arglen, arg) ? + (invert ? False : True) : + (invert ? True : False)); + free(arg); + break; + } + } + } else { + t = condPushBack; + condPushBack = None; + } + return (t); +} + +/*- + *----------------------------------------------------------------------- + * CondT -- + * Parse a single term in the expression. This consists of a terminal + * symbol or Not and a terminal symbol (not including the binary + * operators): + * T -> defined(variable) | make(target) | exists(file) | symbol + * T -> ! T | ( E ) + * + * Results: + * True, False or Err. + * + * Side Effects: + * Tokens are consumed. + * + *----------------------------------------------------------------------- + */ +static Token +CondT(doEval) + Boolean doEval; +{ + Token t; + + t = CondToken(doEval); + + if (t == EndOfFile) { + /* + * If we reached the end of the expression, the expression + * is malformed... + */ + t = Err; + } else if (t == LParen) { + /* + * T -> ( E ) + */ + t = CondE(doEval); + if (t != Err) { + if (CondToken(doEval) != RParen) { + t = Err; + } + } + } else if (t == Not) { + t = CondT(doEval); + if (t == True) { + t = False; + } else if (t == False) { + t = True; + } + } + return (t); +} + +/*- + *----------------------------------------------------------------------- + * CondF -- + * Parse a conjunctive factor (nice name, wot?) + * F -> T && F | T + * + * Results: + * True, False or Err + * + * Side Effects: + * Tokens are consumed. + * + *----------------------------------------------------------------------- + */ +static Token +CondF(doEval) + Boolean doEval; +{ + Token l, o; + + l = CondT(doEval); + if (l != Err) { + o = CondToken(doEval); + + if (o == And) { + /* + * F -> T && F + * + * If T is False, the whole thing will be False, but we have to + * parse the r.h.s. anyway (to throw it away). + * If T is True, the result is the r.h.s., be it an Err or no. + */ + if (l == True) { + l = CondF(doEval); + } else { + (void) CondF(FALSE); + } + } else { + /* + * F -> T + */ + CondPushBack (o); + } + } + return (l); +} + +/*- + *----------------------------------------------------------------------- + * CondE -- + * Main expression production. + * E -> F || E | F + * + * Results: + * True, False or Err. + * + * Side Effects: + * Tokens are, of course, consumed. + * + *----------------------------------------------------------------------- + */ +static Token +CondE(doEval) + Boolean doEval; +{ + Token l, o; + + l = CondF(doEval); + if (l != Err) { + o = CondToken(doEval); + + if (o == Or) { + /* + * E -> F || E + * + * A similar thing occurs for ||, except that here we make sure + * the l.h.s. is False before we bother to evaluate the r.h.s. + * Once again, if l is False, the result is the r.h.s. and once + * again if l is True, we parse the r.h.s. to throw it away. + */ + if (l == False) { + l = CondE(doEval); + } else { + (void) CondE(FALSE); + } + } else { + /* + * E -> F + */ + CondPushBack (o); + } + } + return (l); +} + +/*- + *----------------------------------------------------------------------- + * Cond_EvalExpression -- + * Evaluate an expression in the passed line. The expression + * consists of &&, ||, !, make(target), defined(variable) + * and parenthetical groupings thereof. + * + * Results: + * COND_PARSE if the condition was valid grammatically + * COND_INVALID if not a valid conditional. + * + * (*value) is set to the boolean value of the condition + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +int +Cond_EvalExpression(dosetup, line, value, eprint) + int dosetup; + char *line; + Boolean *value; + int eprint; +{ + if (dosetup) { + condDefProc = CondDoDefined; + condInvert = 0; + } + + while (*line == ' ' || *line == '\t') + line++; + + condExpr = line; + condPushBack = None; + + switch (CondE(TRUE)) { + case True: + if (CondToken(TRUE) == EndOfFile) { + *value = TRUE; + break; + } + goto err; + /*FALLTHRU*/ + case False: + if (CondToken(TRUE) == EndOfFile) { + *value = FALSE; + break; + } + /*FALLTHRU*/ + case Err: +err: + if (eprint) + Parse_Error (PARSE_FATAL, "Malformed conditional (%s)", + line); + return (COND_INVALID); + default: + break; + } + + return COND_PARSE; +} + + +/*- + *----------------------------------------------------------------------- + * Cond_Eval -- + * Evaluate the conditional in the passed line. The line + * looks like this: + * # + * where is any of if, ifmake, ifnmake, ifdef, + * ifndef, elif, elifmake, elifnmake, elifdef, elifndef + * and consists of &&, ||, !, make(target), defined(variable) + * and parenthetical groupings thereof. + * + * Results: + * COND_PARSE if should parse lines after the conditional + * COND_SKIP if should skip lines after the conditional + * COND_INVALID if not a valid conditional. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +int +Cond_Eval (line) + char *line; /* Line to parse */ +{ + struct If *ifp; + Boolean isElse; + Boolean value = FALSE; + int level; /* Level at which to report errors. */ + + level = PARSE_FATAL; + + for (line++; *line == ' ' || *line == '\t'; line++) { + continue; + } + + /* + * Find what type of if we're dealing with. The result is left + * in ifp and isElse is set TRUE if it's an elif line. + */ + if (line[0] == 'e' && line[1] == 'l') { + line += 2; + isElse = TRUE; + } else if (strncmp (line, "endif", 5) == 0) { + /* + * End of a conditional section. If skipIfLevel is non-zero, that + * conditional was skipped, so lines following it should also be + * skipped. Hence, we return COND_SKIP. Otherwise, the conditional + * was read so succeeding lines should be parsed (think about it...) + * so we return COND_PARSE, unless this endif isn't paired with + * a decent if. + */ + if (skipIfLevel != 0) { + skipIfLevel -= 1; + return (COND_SKIP); + } else { + if (condTop == MAXIF) { + Parse_Error (level, "if-less endif"); + return (COND_INVALID); + } else { + skipLine = FALSE; + condTop += 1; + return (COND_PARSE); + } + } + } else { + isElse = FALSE; + } + + /* + * Figure out what sort of conditional it is -- what its default + * function is, etc. -- by looking in the table of valid "ifs" + */ + for (ifp = ifs; ifp->form != (char *)0; ifp++) { + if (strncmp (ifp->form, line, ifp->formlen) == 0) { + break; + } + } + + if (ifp->form == (char *) 0) { + /* + * Nothing fit. If the first word on the line is actually + * "else", it's a valid conditional whose value is the inverse + * of the previous if we parsed. + */ + if (isElse && (line[0] == 's') && (line[1] == 'e')) { + if (condTop == MAXIF) { + Parse_Error (level, "if-less else"); + return (COND_INVALID); + } else if (skipIfLevel == 0) { + value = !condStack[condTop]; + } else { + return (COND_SKIP); + } + } else { + /* + * Not a valid conditional type. No error... + */ + return (COND_INVALID); + } + } else { + if (isElse) { + if (condTop == MAXIF) { + Parse_Error (level, "if-less elif"); + return (COND_INVALID); + } else if (skipIfLevel != 0) { + /* + * If skipping this conditional, just ignore the whole thing. + * If we don't, the user might be employing a variable that's + * undefined, for which there's an enclosing ifdef that + * we're skipping... + */ + return(COND_SKIP); + } + } else if (skipLine) { + /* + * Don't even try to evaluate a conditional that's not an else if + * we're skipping things... + */ + skipIfLevel += 1; + return(COND_SKIP); + } + + /* + * Initialize file-global variables for parsing + */ + condDefProc = ifp->defProc; + condInvert = ifp->doNot; + + line += ifp->formlen; + if (Cond_EvalExpression(0, line, &value, 1) == COND_INVALID) + return COND_INVALID; + } + if (!isElse) { + condTop -= 1; + } else if ((skipIfLevel != 0) || condStack[condTop]) { + /* + * If this is an else-type conditional, it should only take effect + * if its corresponding if was evaluated and FALSE. If its if was + * TRUE or skipped, we return COND_SKIP (and start skipping in case + * we weren't already), leaving the stack unmolested so later elif's + * don't screw up... + */ + skipLine = TRUE; + return (COND_SKIP); + } + + if (condTop < 0) { + /* + * This is the one case where we can definitely proclaim a fatal + * error. If we don't, we're hosed. + */ + Parse_Error (PARSE_FATAL, "Too many nested if's. %d max.", MAXIF); + return (COND_INVALID); + } else { + condStack[condTop] = value; + skipLine = !value; + return (value ? COND_PARSE : COND_SKIP); + } +} + + + +/*- + *----------------------------------------------------------------------- + * Cond_End -- + * Make sure everything's clean at the end of a makefile. + * + * Results: + * None. + * + * Side Effects: + * Parse_Error will be called if open conditionals are around. + * + *----------------------------------------------------------------------- + */ +void +Cond_End() +{ + if (condTop != MAXIF) { + Parse_Error(PARSE_FATAL, "%d open conditional%s", MAXIF-condTop, + MAXIF-condTop == 1 ? "" : "s"); + } + condTop = MAXIF; +} diff --git a/bootstrap/bmake/config.h.in b/bootstrap/bmake/config.h.in new file mode 100644 index 00000000000..cf322768617 --- /dev/null +++ b/bootstrap/bmake/config.h.in @@ -0,0 +1,182 @@ +/* config.h.in. Generated automatically from configure.in by autoheader. */ + +/* Define if on AIX 3. + System headers sometimes define this. + We just want to avoid a redefinition error message. */ +#ifndef _ALL_SOURCE +#undef _ALL_SOURCE +#endif + +/* Define to empty if the keyword does not work. */ +#undef const + +/* Define if you don't have vprintf but do have _doprnt. */ +#undef HAVE_DOPRNT + +/* Define if your struct stat has st_rdev. */ +#undef HAVE_ST_RDEV + +/* Define if you have that is POSIX.1 compatible. */ +#undef HAVE_SYS_WAIT_H + +/* Define if you have . */ +#undef HAVE_VFORK_H + +/* Define if you have the vprintf function. */ +#undef HAVE_VPRINTF + +/* Define if you have the wait3 system call. */ +#undef HAVE_WAIT3 + +/* Define if on MINIX. */ +#undef _MINIX + +/* Define to `long' if doesn't define. */ +#undef off_t + +/* Define to `int' if doesn't define. */ +#undef pid_t + +/* Define if the system does not provide POSIX.1 features except + with this defined. */ +#undef _POSIX_1_SOURCE + +/* Define if you need to in order for stat and other things to work. */ +#undef _POSIX_SOURCE + +/* Define as the return type of signal handlers (int or void). */ +#undef RETSIGTYPE + +/* Define to `unsigned' if doesn't define. */ +#undef size_t + +/* Define if the `S_IS*' macros in do not work properly. */ +#undef STAT_MACROS_BROKEN + +/* Define if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define if `sys_siglist' is declared by . */ +#undef SYS_SIGLIST_DECLARED + +/* Define if you can safely include both and . */ +#undef TIME_WITH_SYS_TIME + +/* Define if your declares struct tm. */ +#undef TM_IN_SYS_TIME + +/* Define vfork as fork if vfork does not work. */ +#undef vfork + +/* Define if your processor stores words with the most significant + byte first (like Motorola and SPARC, unlike Intel and VAX). */ +#undef WORDS_BIGENDIAN + +/* Define if you have the getcwd function. */ +#undef HAVE_GETCWD + +/* Define if you have the getenv function. */ +#undef HAVE_GETENV + +/* Define if you have the getopt function. */ +#undef HAVE_GETOPT + +/* Define if you have the getwd function. */ +#undef HAVE_GETWD + +/* Define if you have the putenv function. */ +#undef HAVE_PUTENV + +/* Define if you have the select function. */ +#undef HAVE_SELECT + +/* Define if you have the setenv function. */ +#undef HAVE_SETENV + +/* Define if you have the sigaction function. */ +#undef HAVE_SIGACTION + +/* Define if you have the sigvec function. */ +#undef HAVE_SIGVEC + +/* Define if you have the snprintf function. */ +#undef HAVE_SNPRINTF + +/* Define if you have the strdup function. */ +#undef HAVE_STRDUP + +/* Define if you have the strerror function. */ +#undef HAVE_STRERROR + +/* Define if you have the strftime function. */ +#undef HAVE_STRFTIME + +/* Define if you have the strtod function. */ +#undef HAVE_STRTOD + +/* Define if you have the strtol function. */ +#undef HAVE_STRTOL + +/* Define if you have the vsnprintf function. */ +#undef HAVE_VSNPRINTF + +/* Define if you have the wait3 function. */ +#undef HAVE_WAIT3 + +/* Define if you have the wait4 function. */ +#undef HAVE_WAIT4 + +/* Define if you have the waitpid function. */ +#undef HAVE_WAITPID + +/* Define if you have the header file. */ +#undef HAVE_AR_H + +/* Define if you have the header file. */ +#undef HAVE_DIRENT_H + +/* Define if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define if you have the header file. */ +#undef HAVE_NDIR_H + +/* Define if you have the header file. */ +#undef HAVE_PATHS_H + +/* Define if you have the header file. */ +#undef HAVE_POLL_H + +/* Define if you have the header file. */ +#undef HAVE_RANLIB_H + +/* Define if you have the header file. */ +#undef HAVE_STRING_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_DIR_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_NDIR_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_SELECT_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_UIO_H + +/* Define if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define if you have the header file. */ +#undef HAVE_UTIME_H + +/* Define if your compiler has __attribute__ */ +#undef HAVE___ATTRIBUTE__ + +#ifndef HAVE___ATTRIBUTE__ +# define __attribute__(x) +#endif diff --git a/bootstrap/bmake/configure b/bootstrap/bmake/configure new file mode 100755 index 00000000000..df76015317f --- /dev/null +++ b/bootstrap/bmake/configure @@ -0,0 +1,2966 @@ +#! /bin/sh + +# Guess values for system-dependent variables and create Makefiles. +# Generated automatically using autoconf version 2.13 +# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. + +# Defaults: +ac_help= +ac_default_prefix=/usr/local +# Any additions from configure.in: +ac_help="$ac_help + --with-machine=MACHINE explicitly set MACHINE" +ac_help="$ac_help + --with-force-machine=MACHINE set FORCE_MACHINE" +ac_help="$ac_help + --with-machine_arch=MACHINE_ARCH explicitly set MACHINE_ARCH" +ac_help="$ac_help + --with-default-sys-path=PATH:DIR:LIST use an explicit _PATH_DEFSYSPATH" +ac_help="$ac_help + --with-prefix-sys-path=PATH:DIR:LIST prefix _PATH_PREFIX_SYSPATH" +ac_help="$ac_help + --with-path-objdirprefix=PATH override _PATH_OBJDIRPREFIX" +ac_help="$ac_help + --disable-pwd-override disable \$PWD overriding getcwd()" +ac_help="$ac_help + --disable-check-make-chdir disable make trying to guess + when it should automatically cd \${.CURDIR}" + +# Initialize some variables set by options. +# The variables have the same names as the options, with +# dashes changed to underlines. +build=NONE +cache_file=./config.cache +exec_prefix=NONE +host=NONE +no_create= +nonopt=NONE +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +target=NONE +verbose= +x_includes=NONE +x_libraries=NONE +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +# Initialize some other variables. +subdirs= +MFLAGS= MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} +# Maximum number of lines to put in a shell here document. +ac_max_here_lines=12 + +ac_prev= +for ac_option +do + + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + case "$ac_option" in + -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) ac_optarg= ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case "$ac_option" in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir="$ac_optarg" ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build="$ac_optarg" ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file="$ac_optarg" ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir="$ac_optarg" ;; + + -disable-* | --disable-*) + ac_feature=`echo $ac_option|sed -e 's/-*disable-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + eval "enable_${ac_feature}=no" ;; + + -enable-* | --enable-*) + ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "enable_${ac_feature}='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix="$ac_optarg" ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he) + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat << EOF +Usage: configure [options] [host] +Options: [defaults in brackets after descriptions] +Configuration: + --cache-file=FILE cache test results in FILE + --help print this message + --no-create do not create output files + --quiet, --silent do not print \`checking...' messages + --version print the version of autoconf that created configure +Directory and file names: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [same as prefix] + --bindir=DIR user executables in DIR [EPREFIX/bin] + --sbindir=DIR system admin executables in DIR [EPREFIX/sbin] + --libexecdir=DIR program executables in DIR [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data in DIR + [PREFIX/share] + --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data in DIR + [PREFIX/com] + --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var] + --libdir=DIR object code libraries in DIR [EPREFIX/lib] + --includedir=DIR C header files in DIR [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include] + --infodir=DIR info documentation in DIR [PREFIX/info] + --mandir=DIR man documentation in DIR [PREFIX/man] + --srcdir=DIR find the sources in DIR [configure dir or ..] + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM + run sed PROGRAM on installed program names +EOF + cat << EOF +Host type: + --build=BUILD configure for building on BUILD [BUILD=HOST] + --host=HOST configure for HOST [guessed] + --target=TARGET configure for TARGET [TARGET=HOST] +Features and packages: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --x-includes=DIR X include files are in DIR + --x-libraries=DIR X library files are in DIR +EOF + if test -n "$ac_help"; then + echo "--enable and --with options recognized:$ac_help" + fi + exit 0 ;; + + -host | --host | --hos | --ho) + ac_prev=host ;; + -host=* | --host=* | --hos=* | --ho=*) + host="$ac_optarg" ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir="$ac_optarg" ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir="$ac_optarg" ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir="$ac_optarg" ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir="$ac_optarg" ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir="$ac_optarg" ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir="$ac_optarg" ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir="$ac_optarg" ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix="$ac_optarg" ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix="$ac_optarg" ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix="$ac_optarg" ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name="$ac_optarg" ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir="$ac_optarg" ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir="$ac_optarg" ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site="$ac_optarg" ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir="$ac_optarg" ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir="$ac_optarg" ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target="$ac_optarg" ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers) + echo "configure generated by autoconf version 2.13" + exit 0 ;; + + -with-* | --with-*) + ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "with_${ac_package}='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`echo $ac_option|sed -e 's/-*without-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + eval "with_${ac_package}=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes="$ac_optarg" ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries="$ac_optarg" ;; + + -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; } + ;; + + *) + if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then + echo "configure: warning: $ac_option: invalid host type" 1>&2 + fi + if test "x$nonopt" != xNONE; then + { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } + fi + nonopt="$ac_option" + ;; + + esac +done + +if test -n "$ac_prev"; then + { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; } +fi + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +# File descriptor usage: +# 0 standard input +# 1 file creation +# 2 errors and warnings +# 3 some systems may open it to /dev/tty +# 4 used on the Kubota Titan +# 6 checking for... messages and results +# 5 compiler messages saved in config.log +if test "$silent" = yes; then + exec 6>/dev/null +else + exec 6>&1 +fi +exec 5>./config.log + +echo "\ +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. +" 1>&5 + +# Strip out --no-create and --no-recursion so they do not pile up. +# Also quote any args containing shell metacharacters. +ac_configure_args= +for ac_arg +do + case "$ac_arg" in + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) ;; + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*) + ac_configure_args="$ac_configure_args '$ac_arg'" ;; + *) ac_configure_args="$ac_configure_args $ac_arg" ;; + esac +done + +# NLS nuisances. +# Only set these to C if already set. These must not be set unconditionally +# because not all systems understand e.g. LANG=C (notably SCO). +# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'! +# Non-C LC_CTYPE values break the ctype check. +if test "${LANG+set}" = set; then LANG=C; export LANG; fi +if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi +if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi +if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo > confdefs.h + +# 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. +ac_unique_file=makefile.boot.in + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_prog=$0 + ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'` + test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; } + else + { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; } + fi +fi +srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'` + +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + echo "loading site script $ac_site_file" + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + echo "loading cache $cache_file" + . $cache_file +else + echo "creating cache $cache_file" + > $cache_file +fi + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +ac_exeext= +ac_objext=o +if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then + # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. + if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then + ac_n= ac_c=' +' ac_t=' ' + else + ac_n=-n ac_c= ac_t= + fi +else + ac_n= ac_c='\c' ac_t= +fi + + + + +# Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:547: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="gcc" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:577: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_prog_rejected=no + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + break + fi + done + IFS="$ac_save_ifs" +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# -gt 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + set dummy "$ac_dir/$ac_word" "$@" + shift + ac_cv_prog_CC="$@" + fi +fi +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + if test -z "$CC"; then + case "`uname -s`" in + *win32* | *WIN32*) + # Extract the first word of "cl", so it can be a program name with args. +set dummy cl; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:628: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="cl" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + ;; + esac + fi + test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; } +fi + +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 +echo "configure:660: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +cat > conftest.$ac_ext << EOF + +#line 671 "configure" +#include "confdefs.h" + +main(){return(0);} +EOF +if { (eval echo configure:676: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + ac_cv_prog_cc_works=yes + # If we can't run a trivial program, we are probably using a cross compiler. + if (./conftest; exit) 2>/dev/null; then + ac_cv_prog_cc_cross=no + else + ac_cv_prog_cc_cross=yes + fi +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_prog_cc_works=no +fi +rm -fr conftest* +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +echo "$ac_t""$ac_cv_prog_cc_works" 1>&6 +if test $ac_cv_prog_cc_works = no; then + { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } +fi +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 +echo "configure:702: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 +cross_compiling=$ac_cv_prog_cc_cross + +echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 +echo "configure:707: checking whether we are using GNU C" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.c <&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then + ac_cv_prog_gcc=yes +else + ac_cv_prog_gcc=no +fi +fi + +echo "$ac_t""$ac_cv_prog_gcc" 1>&6 + +if test $ac_cv_prog_gcc = yes; then + GCC=yes +else + GCC= +fi + +ac_test_CFLAGS="${CFLAGS+set}" +ac_save_CFLAGS="$CFLAGS" +CFLAGS= +echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 +echo "configure:735: checking whether ${CC-cc} accepts -g" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo 'void f(){}' > conftest.c +if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then + ac_cv_prog_cc_g=yes +else + ac_cv_prog_cc_g=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_prog_cc_g" 1>&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS="$ac_save_CFLAGS" +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi + +echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 +echo "configure:767: checking how to run the C preprocessor" >&5 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then +if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + # This must be in double quotes, not single quotes, because CPP may get + # substituted into the Makefile and "${CC-cc}" will confuse make. + CPP="${CC-cc} -E" + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:788: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -E -traditional-cpp" + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:805: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -nologo -E" + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:822: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP=/lib/cpp +fi +rm -f conftest* +fi +rm -f conftest* +fi +rm -f conftest* + ac_cv_prog_CPP="$CPP" +fi + CPP="$ac_cv_prog_CPP" +else + ac_cv_prog_CPP="$CPP" +fi +echo "$ac_t""$CPP" 1>&6 + +if test $ac_cv_prog_gcc = yes; then + echo $ac_n "checking whether ${CC-cc} needs -traditional""... $ac_c" 1>&6 +echo "configure:848: checking whether ${CC-cc} needs -traditional" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc_traditional'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_pattern="Autoconf.*'x'" + cat > conftest.$ac_ext < +Autoconf TIOCGETP +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "$ac_pattern" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_prog_gcc_traditional=yes +else + rm -rf conftest* + ac_cv_prog_gcc_traditional=no +fi +rm -f conftest* + + + if test $ac_cv_prog_gcc_traditional = no; then + cat > conftest.$ac_ext < +Autoconf TCGETA +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "$ac_pattern" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_prog_gcc_traditional=yes +fi +rm -f conftest* + + fi +fi + +echo "$ac_t""$ac_cv_prog_gcc_traditional" 1>&6 + if test $ac_cv_prog_gcc_traditional = yes; then + CC="$CC -traditional" + fi +fi + +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; } +fi +ac_config_guess=$ac_aux_dir/config.guess +ac_config_sub=$ac_aux_dir/config.sub +ac_configure=$ac_aux_dir/configure # This should be Cygnus configure. + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# ./install, which can be erroneously created by make from ./install.sh. +echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 +echo "configure:924: checking for a BSD compatible install" >&5 +if test -z "$INSTALL"; then +if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS=":" + for ac_dir in $PATH; do + # Account for people who put trailing slashes in PATH elements. + case "$ac_dir/" in + /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + if test -f $ac_dir/$ac_prog; then + if test $ac_prog = install && + grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + else + ac_cv_path_install="$ac_dir/$ac_prog -c" + break 2 + fi + fi + done + ;; + esac + done + IFS="$ac_save_IFS" + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL="$ac_cv_path_install" + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL="$ac_install_sh" + fi +fi +echo "$ac_t""$INSTALL" 1>&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +echo $ac_n "checking for AIX""... $ac_c" 1>&6 +echo "configure:977: checking for AIX" >&5 +cat > conftest.$ac_ext <&5 | + egrep "yes" >/dev/null 2>&1; then + rm -rf conftest* + echo "$ac_t""yes" 1>&6; cat >> confdefs.h <<\EOF +#define _ALL_SOURCE 1 +EOF + +else + rm -rf conftest* + echo "$ac_t""no" 1>&6 +fi +rm -f conftest* + + +ac_safe=`echo "minix/config.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for minix/config.h""... $ac_c" 1>&6 +echo "configure:1002: checking for minix/config.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1012: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + MINIX=yes +else + echo "$ac_t""no" 1>&6 +MINIX= +fi + +if test "$MINIX" = yes; then + cat >> confdefs.h <<\EOF +#define _POSIX_SOURCE 1 +EOF + + cat >> confdefs.h <<\EOF +#define _POSIX_1_SOURCE 2 +EOF + + cat >> confdefs.h <<\EOF +#define _MINIX 1 +EOF + +fi + +echo $ac_n "checking for POSIXized ISC""... $ac_c" 1>&6 +echo "configure:1050: checking for POSIXized ISC" >&5 +if test -d /etc/conf/kconfig.d && + grep _POSIX_VERSION /usr/include/sys/unistd.h >/dev/null 2>&1 +then + echo "$ac_t""yes" 1>&6 + ISC=yes # If later tests want to check for ISC. + cat >> confdefs.h <<\EOF +#define _POSIX_SOURCE 1 +EOF + + if test "$GCC" = yes; then + CC="$CC -posix" + else + CC="$CC -Xp" + fi +else + echo "$ac_t""no" 1>&6 + ISC= +fi + + + +echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6 +echo "configure:1073: checking for ANSI C header files" >&5 +if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +#include +#include +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1086: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + ac_cv_header_stdc=yes +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. +cat > conftest.$ac_ext < +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "memchr" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. +cat > conftest.$ac_ext < +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "free" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. +if test "$cross_compiling" = yes; then + : +else + cat > conftest.$ac_ext < +#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 +if { (eval echo configure:1153: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + : +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_header_stdc=no +fi +rm -fr conftest* +fi + +fi +fi + +echo "$ac_t""$ac_cv_header_stdc" 1>&6 +if test $ac_cv_header_stdc = yes; then + cat >> confdefs.h <<\EOF +#define STDC_HEADERS 1 +EOF + +fi + +echo $ac_n "checking for sys/wait.h that is POSIX.1 compatible""... $ac_c" 1>&6 +echo "configure:1177: checking for sys/wait.h that is POSIX.1 compatible" >&5 +if eval "test \"`echo '$''{'ac_cv_header_sys_wait_h'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +#ifndef WEXITSTATUS +#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) +#endif +#ifndef WIFEXITED +#define WIFEXITED(stat_val) (((stat_val) & 255) == 0) +#endif +int main() { +int s; +wait (&s); +s = WIFEXITED (s) ? WEXITSTATUS (s) : 1; +; return 0; } +EOF +if { (eval echo configure:1198: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_header_sys_wait_h=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_header_sys_wait_h=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_header_sys_wait_h" 1>&6 +if test $ac_cv_header_sys_wait_h = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_SYS_WAIT_H 1 +EOF + +fi + +ac_header_dirent=no +for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr that defines DIR""... $ac_c" 1>&6 +echo "configure:1223: checking for $ac_hdr that defines DIR" >&5 +if eval "test \"`echo '$''{'ac_cv_header_dirent_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include <$ac_hdr> +int main() { +DIR *dirp = 0; +; return 0; } +EOF +if { (eval echo configure:1236: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + eval "ac_cv_header_dirent_$ac_safe=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_dirent_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_dirent_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done +# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix. +if test $ac_header_dirent = dirent.h; then +echo $ac_n "checking for opendir in -ldir""... $ac_c" 1>&6 +echo "configure:1261: checking for opendir in -ldir" >&5 +ac_lib_var=`echo dir'_'opendir | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-ldir $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + LIBS="$LIBS -ldir" +else + echo "$ac_t""no" 1>&6 +fi + +else +echo $ac_n "checking for opendir in -lx""... $ac_c" 1>&6 +echo "configure:1302: checking for opendir in -lx" >&5 +ac_lib_var=`echo x'_'opendir | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lx $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + LIBS="$LIBS -lx" +else + echo "$ac_t""no" 1>&6 +fi + +fi + +for ac_hdr in ar.h ranlib.h fcntl.h paths.h sys/select.h sys/time.h unistd.h string.h utime.h poll.h sys/uio.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:1347: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1357: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + + +ac_safe=`echo "sys/cdefs.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for sys/cdefs.h""... $ac_c" 1>&6 +echo "configure:1386: checking for sys/cdefs.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1396: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + echo $ac_n "checking whether sys/cdefs.h is compatible... $ac_c" 2>&6 +cat > conftest.$ac_ext < +#ifdef __RCSID +yes +#endif + +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "yes" >/dev/null 2>&1; then + rm -rf conftest* + echo yes 2>&6 +else + rm -rf conftest* + echo no 2>&6; CPPFLAGS="${CPPFLAGS} -I\${srcdir}/missing -DNEED_HOST_CDEFS_H" +fi +rm -f conftest* + +else + echo "$ac_t""no" 1>&6 +CPPFLAGS="${CPPFLAGS} -I\${srcdir}/missing" +fi + + + +echo $ac_n "checking for __attribute__""... $ac_c" 1>&6 +echo "configure:1440: checking for __attribute__" >&5 +if eval "test \"`echo '$''{'ac_cv___attribute__'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + +cat > conftest.$ac_ext < + +int main() { + +static void foo(void) __attribute__ ((noreturn)); + +static void +foo(void) +{ + exit(1); +} + +; return 0; } +EOF +if { (eval echo configure:1463: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv___attribute__=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv___attribute__=no +fi +rm -f conftest* +fi + +if test "$ac_cv___attribute__" = "yes"; then + cat >> confdefs.h <<\EOF +#define HAVE___ATTRIBUTE__ 1 +EOF + +fi +echo "$ac_t""$ac_cv___attribute__" 1>&6 + +echo $ac_n "checking whether byte ordering is bigendian""... $ac_c" 1>&6 +echo "configure:1484: checking whether byte ordering is bigendian" >&5 +if eval "test \"`echo '$''{'ac_cv_c_bigendian'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_cv_c_bigendian=unknown +# See if sys/param.h defines the BYTE_ORDER macro. +cat > conftest.$ac_ext < +#include +int main() { + +#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN + bogus endian macros +#endif +; return 0; } +EOF +if { (eval echo configure:1502: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + # It does; now see whether it defined to BIG_ENDIAN or not. +cat > conftest.$ac_ext < +#include +int main() { + +#if BYTE_ORDER != BIG_ENDIAN + not big endian +#endif +; return 0; } +EOF +if { (eval echo configure:1517: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_c_bigendian=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_c_bigendian=no +fi +rm -f conftest* +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -f conftest* +if test $ac_cv_c_bigendian = unknown; then +if test "$cross_compiling" = yes; then + { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } +else + cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + ac_cv_c_bigendian=no +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_c_bigendian=yes +fi +rm -fr conftest* +fi + +fi +fi + +echo "$ac_t""$ac_cv_c_bigendian" 1>&6 +if test $ac_cv_c_bigendian = yes; then + cat >> confdefs.h <<\EOF +#define WORDS_BIGENDIAN 1 +EOF + +fi + +echo $ac_n "checking for working const""... $ac_c" 1>&6 +echo "configure:1574: checking for working const" >&5 +if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <j = 5; +} +{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; +} + +; return 0; } +EOF +if { (eval echo configure:1628: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_c_const=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_c_const=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_c_const" 1>&6 +if test $ac_cv_c_const = no; then + cat >> confdefs.h <<\EOF +#define const +EOF + +fi + +echo $ac_n "checking for off_t""... $ac_c" 1>&6 +echo "configure:1649: checking for off_t" >&5 +if eval "test \"`echo '$''{'ac_cv_type_off_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#if STDC_HEADERS +#include +#include +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "(^|[^a-zA-Z_0-9])off_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_off_t=yes +else + rm -rf conftest* + ac_cv_type_off_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_off_t" 1>&6 +if test $ac_cv_type_off_t = no; then + cat >> confdefs.h <<\EOF +#define off_t long +EOF + +fi + +echo $ac_n "checking for pid_t""... $ac_c" 1>&6 +echo "configure:1682: checking for pid_t" >&5 +if eval "test \"`echo '$''{'ac_cv_type_pid_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#if STDC_HEADERS +#include +#include +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "(^|[^a-zA-Z_0-9])pid_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_pid_t=yes +else + rm -rf conftest* + ac_cv_type_pid_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_pid_t" 1>&6 +if test $ac_cv_type_pid_t = no; then + cat >> confdefs.h <<\EOF +#define pid_t int +EOF + +fi + +echo $ac_n "checking for size_t""... $ac_c" 1>&6 +echo "configure:1715: checking for size_t" >&5 +if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#if STDC_HEADERS +#include +#include +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "(^|[^a-zA-Z_0-9])size_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_size_t=yes +else + rm -rf conftest* + ac_cv_type_size_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_size_t" 1>&6 +if test $ac_cv_type_size_t = no; then + cat >> confdefs.h <<\EOF +#define size_t unsigned +EOF + +fi + +echo $ac_n "checking for sys_siglist declaration in signal.h or unistd.h""... $ac_c" 1>&6 +echo "configure:1748: checking for sys_siglist declaration in signal.h or unistd.h" >&5 +if eval "test \"`echo '$''{'ac_cv_decl_sys_siglist'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +/* NetBSD declares sys_siglist in unistd.h. */ +#ifdef HAVE_UNISTD_H +#include +#endif +int main() { +char *msg = *(sys_siglist + 1); +; return 0; } +EOF +if { (eval echo configure:1765: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_decl_sys_siglist=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_decl_sys_siglist=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_decl_sys_siglist" 1>&6 +if test $ac_cv_decl_sys_siglist = yes; then + cat >> confdefs.h <<\EOF +#define SYS_SIGLIST_DECLARED 1 +EOF + +fi + +echo $ac_n "checking whether time.h and sys/time.h may both be included""... $ac_c" 1>&6 +echo "configure:1786: checking whether time.h and sys/time.h may both be included" >&5 +if eval "test \"`echo '$''{'ac_cv_header_time'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +#include +int main() { +struct tm *tp; +; return 0; } +EOF +if { (eval echo configure:1800: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_header_time=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_header_time=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_header_time" 1>&6 +if test $ac_cv_header_time = yes; then + cat >> confdefs.h <<\EOF +#define TIME_WITH_SYS_TIME 1 +EOF + +fi + +echo $ac_n "checking whether struct tm is in sys/time.h or time.h""... $ac_c" 1>&6 +echo "configure:1821: checking whether struct tm is in sys/time.h or time.h" >&5 +if eval "test \"`echo '$''{'ac_cv_struct_tm'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +int main() { +struct tm *tp; tp->tm_sec; +; return 0; } +EOF +if { (eval echo configure:1834: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_struct_tm=time.h +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_struct_tm=sys/time.h +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_struct_tm" 1>&6 +if test $ac_cv_struct_tm = sys/time.h; then + cat >> confdefs.h <<\EOF +#define TM_IN_SYS_TIME 1 +EOF + +fi + + +echo $ac_n "checking return type of signal handlers""... $ac_c" 1>&6 +echo "configure:1856: checking return type of signal handlers" >&5 +if eval "test \"`echo '$''{'ac_cv_type_signal'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +#ifdef signal +#undef signal +#endif +#ifdef __cplusplus +extern "C" void (*signal (int, void (*)(int)))(int); +#else +void (*signal ()) (); +#endif + +int main() { +int i; +; return 0; } +EOF +if { (eval echo configure:1878: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_type_signal=void +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_type_signal=int +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_type_signal" 1>&6 +cat >> confdefs.h <&6 +echo "configure:1898: checking for vfork.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1908: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<\EOF +#define HAVE_VFORK_H 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi + +echo $ac_n "checking for working vfork""... $ac_c" 1>&6 +echo "configure:1933: checking for working vfork" >&5 +if eval "test \"`echo '$''{'ac_cv_func_vfork_works'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + echo $ac_n "checking for vfork""... $ac_c" 1>&6 +echo "configure:1939: checking for vfork" >&5 +if eval "test \"`echo '$''{'ac_cv_func_vfork'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char vfork(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_vfork) || defined (__stub___vfork) +choke me +#else +vfork(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1967: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_vfork=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_vfork=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'vfork`\" = yes"; then + echo "$ac_t""yes" 1>&6 + : +else + echo "$ac_t""no" 1>&6 +fi + +ac_cv_func_vfork_works=$ac_cv_func_vfork +else + cat > conftest.$ac_ext < +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_VFORK_H +#include +#endif +/* On some sparc systems, changes by the child to local and incoming + argument registers are propagated back to the parent. + The compiler is told about this with #include , + but some compilers (e.g. gcc -O) don't grok . + Test for this by using a static variable whose address + is put into a register that is clobbered by the vfork. */ +static +#ifdef __cplusplus +sparc_address_test (int arg) +#else +sparc_address_test (arg) int arg; +#endif +{ + static pid_t child; + if (!child) { + child = vfork (); + if (child < 0) { + perror ("vfork"); + _exit(2); + } + if (!child) { + arg = getpid(); + write(-1, "", 0); + _exit (arg); + } + } +} +main() { + pid_t parent = getpid (); + pid_t child; + + sparc_address_test (); + + child = vfork (); + + if (child == 0) { + /* Here is another test for sparc vfork register problems. + This test uses lots of local variables, at least + as many local variables as main has allocated so far + including compiler temporaries. 4 locals are enough for + gcc 1.40.3 on a Solaris 4.1.3 sparc, but we use 8 to be safe. + A buggy compiler should reuse the register of parent + for one of the local variables, since it will think that + parent can't possibly be used any more in this routine. + Assigning to the local variable will thus munge parent + in the parent process. */ + pid_t + p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(), + p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid(); + /* Convince the compiler that p..p7 are live; otherwise, it might + use the same hardware register for all 8 local variables. */ + if (p != p1 || p != p2 || p != p3 || p != p4 + || p != p5 || p != p6 || p != p7) + _exit(1); + + /* On some systems (e.g. IRIX 3.3), + vfork doesn't separate parent from child file descriptors. + If the child closes a descriptor before it execs or exits, + this munges the parent's descriptor as well. + Test for this by closing stdout in the child. */ + _exit(close(fileno(stdout)) != 0); + } else { + int status; + struct stat st; + + while (wait(&status) != child) + ; + exit( + /* Was there some problem with vforking? */ + child < 0 + + /* Did the child fail? (This shouldn't happen.) */ + || status + + /* Did the vfork/compiler bug occur? */ + || parent != getpid() + + /* Did the file descriptor bug occur? */ + || fstat(fileno(stdout), &st) != 0 + ); + } +} +EOF +if { (eval echo configure:2084: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + ac_cv_func_vfork_works=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_func_vfork_works=no +fi +rm -fr conftest* +fi + +fi + +echo "$ac_t""$ac_cv_func_vfork_works" 1>&6 +if test $ac_cv_func_vfork_works = no; then + cat >> confdefs.h <<\EOF +#define vfork fork +EOF + +fi + +echo $ac_n "checking for vprintf""... $ac_c" 1>&6 +echo "configure:2107: checking for vprintf" >&5 +if eval "test \"`echo '$''{'ac_cv_func_vprintf'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char vprintf(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_vprintf) || defined (__stub___vprintf) +choke me +#else +vprintf(); +#endif + +; return 0; } +EOF +if { (eval echo configure:2135: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_vprintf=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_vprintf=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'vprintf`\" = yes"; then + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<\EOF +#define HAVE_VPRINTF 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi + +if test "$ac_cv_func_vprintf" != yes; then +echo $ac_n "checking for _doprnt""... $ac_c" 1>&6 +echo "configure:2159: checking for _doprnt" >&5 +if eval "test \"`echo '$''{'ac_cv_func__doprnt'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char _doprnt(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub__doprnt) || defined (__stub____doprnt) +choke me +#else +_doprnt(); +#endif + +; return 0; } +EOF +if { (eval echo configure:2187: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func__doprnt=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func__doprnt=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'_doprnt`\" = yes"; then + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<\EOF +#define HAVE_DOPRNT 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi + +fi + +echo $ac_n "checking for wait3 that fills in rusage""... $ac_c" 1>&6 +echo "configure:2212: checking for wait3 that fills in rusage" >&5 +if eval "test \"`echo '$''{'ac_cv_func_wait3_rusage'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_wait3_rusage=no +else + cat > conftest.$ac_ext < +#include +#include +#include +/* HP-UX has wait3 but does not fill in rusage at all. */ +main() { + struct rusage r; + int i; + /* Use a field that we can force nonzero -- + voluntary context switches. + For systems like NeXT and OSF/1 that don't set it, + also use the system CPU time. And page faults (I/O) for Linux. */ + r.ru_nvcsw = 0; + r.ru_stime.tv_sec = 0; + r.ru_stime.tv_usec = 0; + r.ru_majflt = r.ru_minflt = 0; + switch (fork()) { + case 0: /* Child. */ + sleep(1); /* Give up the CPU. */ + _exit(0); + case -1: _exit(0); /* What can we do? */ + default: /* Parent. */ + wait3(&i, 0, &r); + sleep(2); /* Avoid "text file busy" from rm on fast HP-UX machines. */ + exit(r.ru_nvcsw == 0 && r.ru_majflt == 0 && r.ru_minflt == 0 + && r.ru_stime.tv_sec == 0 && r.ru_stime.tv_usec == 0); + } +} +EOF +if { (eval echo configure:2251: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + ac_cv_func_wait3_rusage=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_func_wait3_rusage=no +fi +rm -fr conftest* +fi + +fi + +echo "$ac_t""$ac_cv_func_wait3_rusage" 1>&6 +if test $ac_cv_func_wait3_rusage = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_WAIT3 1 +EOF + +fi + +for ac_func in getcwd getwd getopt putenv select strdup strerror strtod strtol setenv getenv vsnprintf snprintf strftime sigaction sigvec waitpid wait4 wait3 +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:2276: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:2304: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <&6 +fi +done + +for ac_func in getenv +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:2331: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:2359: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <&6 +LIBOBJS="$LIBOBJS ${ac_func}.${ac_objext}" +fi +done + + +echo $ac_n "checking whether stat file-mode macros are broken""... $ac_c" 1>&6 +echo "configure:2386: checking whether stat file-mode macros are broken" >&5 +if eval "test \"`echo '$''{'ac_cv_header_stat_broken'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include + +#if defined(S_ISBLK) && defined(S_IFDIR) +# if S_ISBLK (S_IFDIR) +You lose. +# endif +#endif + +#if defined(S_ISBLK) && defined(S_IFCHR) +# if S_ISBLK (S_IFCHR) +You lose. +# endif +#endif + +#if defined(S_ISLNK) && defined(S_IFREG) +# if S_ISLNK (S_IFREG) +You lose. +# endif +#endif + +#if defined(S_ISSOCK) && defined(S_IFREG) +# if S_ISSOCK (S_IFREG) +You lose. +# endif +#endif + +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "You lose" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_header_stat_broken=yes +else + rm -rf conftest* + ac_cv_header_stat_broken=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_header_stat_broken" 1>&6 +if test $ac_cv_header_stat_broken = yes; then + cat >> confdefs.h <<\EOF +#define STAT_MACROS_BROKEN 1 +EOF + +fi + +echo $ac_n "checking for st_rdev in struct stat""... $ac_c" 1>&6 +echo "configure:2442: checking for st_rdev in struct stat" >&5 +if eval "test \"`echo '$''{'ac_cv_struct_st_rdev'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +int main() { +struct stat s; s.st_rdev; +; return 0; } +EOF +if { (eval echo configure:2455: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_struct_st_rdev=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_struct_st_rdev=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_struct_st_rdev" 1>&6 +if test $ac_cv_struct_st_rdev = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_ST_RDEV 1 +EOF + +fi + + +if egrep "__EXTENSIONS__" /usr/include/signal.h >/dev/null 2>&1; then + CPPFLAGS="$CPPFLAGS -D__EXTENSIONS__" +fi + +echo $ac_n "Checking for MACHINE & MACHINE_ARCH... $ac_c" 2>&6 +cat > conftest.$ac_ext < +#ifdef MACHINE +machine=MACHINE +#endif +#ifdef MACHINE_ARCH +machine_arch=MACHINE_ARCH +#endif +EOF + +default_machine=`(eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep machine= | tr -d ' "'` +rm -rf conftest* +if test "$default_machine"; then + eval "$default_machine" +fi +machine=${machine:-`$srcdir/machine.sh`} +machine_arch=${machine_arch:-`$srcdir/machine.sh arch`} +echo "defaults: MACHINE=$machine, MACHINE_ARCH=$machine_arch" 1>&6 +# Check whether --with-machine or --without-machine was given. +if test "${with_machine+set}" = set; then + withval="$with_machine" + case "${withval}" in +yes) { echo "configure: error: bad value ${withval} given for bmake MACHINE" 1>&2; exit 1; } ;; +no) ;; +generic) machine=`$srcdir/machine.sh`;; +*) machine=$with_machine;; +esac +fi + +force_machine= +# Check whether --with-force_machine or --without-force_machine was given. +if test "${with_force_machine+set}" = set; then + withval="$with_force_machine" + case "${withval}" in +yes) force_machine=FORCE_;; +no) ;; +*) force_machine=FORCE_; machine=$with_force_machine;; +esac +fi + +# Check whether --with-machine_arch or --without-machine_arch was given. +if test "${with_machine_arch+set}" = set; then + withval="$with_machine_arch" + case "${withval}" in +yes) { echo "configure: error: bad value ${withval} given for bmake MACHINE_ARCH" 1>&2; exit 1; } ;; +no) ;; +*) machine_arch=$with_machine_arch;; +esac +fi + +echo "Using: ${force_machine}MACHINE=$machine, MACHINE_ARCH=$machine_arch" 1>&6 +# Check whether --with-default-sys-path or --without-default-sys-path was given. +if test "${with_default_sys_path+set}" = set; then + withval="$with_default_sys_path" + case "${withval}" in +yes) { echo "configure: error: bad value ${withval} given for bmake _PATH_DEFSYSPATH" 1>&2; exit 1; } ;; +no) ;; +*) CPPFLAGS="$CPPFLAGS \"-D_PATH_DEFSYSPATH=\\\"$with_default_sys_path\\\"\"" ;; +esac +fi + +# Check whether --with-prefix-sys-path or --without-prefix-sys-path was given. +if test "${with_prefix_sys_path+set}" = set; then + withval="$with_prefix_sys_path" + case "${withval}" in +yes) { echo "configure: error: bad value ${withval} given for bmake _PATH_PREFIX_SYSPATH" 1>&2; exit 1; } ;; +no) ;; +*) CPPFLAGS="$CPPFLAGS \"-D_PATH_PREFIX_SYSPATH=\\\"$with_prefix_sys_path\\\"\"" ;; +esac +fi + +# Check whether --with-path-objdirprefix or --without-path-objdirprefix was given. +if test "${with_path_objdirprefix+set}" = set; then + withval="$with_path_objdirprefix" + case "${withval}" in +yes) { echo "configure: error: bad value ${withval} given for bmake _PATH_OBJDIRPREFIX" 1>&2; exit 1; } ;; +no) CPPFLAGS="$CPPFLAGS -DNO_PATH_OBJDIRPREFIX" ;; +*) CPPFLAGS="$CPPFLAGS \"-D_PATH_OBJDIRPREFIX=\\\"$with_path-objdir\\\"\"" ;; +esac +fi + +# Check whether --enable-pwd-override or --disable-pwd-override was given. +if test "${enable_pwd_override+set}" = set; then + enableval="$enable_pwd_override" + case "${enableval}" in +yes) ;; +no) CPPFLAGS="$CPPFLAGS -DNO_PWD_OVERRIDE" ;; +*) { echo "configure: error: bad value ${enableval} given for pwd-override option" 1>&2; exit 1; } ;; +esac +fi + +# Check whether --enable-check-make-chdir or --disable-check-make-chdir was given. +if test "${enable_check_make_chdir+set}" = set; then + enableval="$enable_check_make_chdir" + case "${enableval}" in +yes) ;; +no) CPPFLAGS="$CPPFLAGS -DNO_CHECK_MAKE_CHDIR" ;; +*) { echo "configure: error: bad value ${enableval} given for check-make-chdir option" 1>&2; exit 1; } ;; +esac +fi + + + + +trap '' 1 2 15 +cat > confcache <<\EOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs. It is not useful on other systems. +# If it contains results you don't want to keep, you may remove or edit it. +# +# By default, configure uses ./config.cache as the cache file, +# creating it if it does not exist already. You can give configure +# the --cache-file=FILE option to use a different cache file; that is +# what configure does when it calls configure scripts in +# subdirectories, so they share the cache. +# Giving --cache-file=/dev/null disables caching, for debugging configure. +# config.status only pays attention to the cache file if you give it the +# --recheck option to rerun configure. +# +EOF +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +(set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote substitution + # turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + -e "s/'/'\\\\''/g" \ + -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p' + ;; + esac >> confcache +if cmp -s $cache_file confcache; then + : +else + if test -w $cache_file; then + echo "updating cache $cache_file" + cat confcache > $cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Any assignment to VPATH causes Sun make to only execute +# the first set of double-colon rules, so remove it if not needed. +# If there is a colon in the path, we need to keep it. +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d' +fi + +trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 + +DEFS=-DHAVE_CONFIG_H + +# Without the "./", some shells look in PATH for config.status. +: ${CONFIG_STATUS=./config.status} + +echo creating $CONFIG_STATUS +rm -f $CONFIG_STATUS +cat > $CONFIG_STATUS </dev/null | sed 1q`: +# +# $0 $ac_configure_args +# +# Compiler output produced by configure, useful for debugging +# configure, is in ./config.log if it exists. + +ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]" +for ac_option +do + case "\$ac_option" in + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion" + exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;; + -version | --version | --versio | --versi | --vers | --ver | --ve | --v) + echo "$CONFIG_STATUS generated by autoconf version 2.13" + exit 0 ;; + -help | --help | --hel | --he | --h) + echo "\$ac_cs_usage"; exit 0 ;; + *) echo "\$ac_cs_usage"; exit 1 ;; + esac +done + +ac_given_srcdir=$srcdir +ac_given_INSTALL="$INSTALL" + +trap 'rm -fr `echo "Makefile makefile.boot lst.lib/makefile.boot config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 +EOF +cat >> $CONFIG_STATUS < conftest.subs <<\\CEOF +$ac_vpsub +$extrasub +s%@SHELL@%$SHELL%g +s%@CFLAGS@%$CFLAGS%g +s%@CPPFLAGS@%$CPPFLAGS%g +s%@CXXFLAGS@%$CXXFLAGS%g +s%@FFLAGS@%$FFLAGS%g +s%@DEFS@%$DEFS%g +s%@LDFLAGS@%$LDFLAGS%g +s%@LIBS@%$LIBS%g +s%@exec_prefix@%$exec_prefix%g +s%@prefix@%$prefix%g +s%@program_transform_name@%$program_transform_name%g +s%@bindir@%$bindir%g +s%@sbindir@%$sbindir%g +s%@libexecdir@%$libexecdir%g +s%@datadir@%$datadir%g +s%@sysconfdir@%$sysconfdir%g +s%@sharedstatedir@%$sharedstatedir%g +s%@localstatedir@%$localstatedir%g +s%@libdir@%$libdir%g +s%@includedir@%$includedir%g +s%@oldincludedir@%$oldincludedir%g +s%@infodir@%$infodir%g +s%@mandir@%$mandir%g +s%@CC@%$CC%g +s%@CPP@%$CPP%g +s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g +s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g +s%@INSTALL_DATA@%$INSTALL_DATA%g +s%@ac_exe_suffix@%$ac_exe_suffix%g +s%@LIBOBJS@%$LIBOBJS%g +s%@machine@%$machine%g +s%@force_machine@%$force_machine%g +s%@machine_arch@%$machine_arch%g + +CEOF +EOF + +cat >> $CONFIG_STATUS <<\EOF + +# Split the substitutions into bite-sized pieces for seds with +# small command number limits, like on Digital OSF/1 and HP-UX. +ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script. +ac_file=1 # Number of current file. +ac_beg=1 # First line for current file. +ac_end=$ac_max_sed_cmds # Line after last line for current file. +ac_more_lines=: +ac_sed_cmds="" +while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file + else + sed "${ac_end}q" conftest.subs > conftest.s$ac_file + fi + if test ! -s conftest.s$ac_file; then + ac_more_lines=false + rm -f conftest.s$ac_file + else + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f conftest.s$ac_file" + else + ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file" + fi + ac_file=`expr $ac_file + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_cmds` + fi +done +if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat +fi +EOF + +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF +for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories. + + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`" + # A "../" for each directory in $ac_dir_suffix. + ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'` + else + ac_dir_suffix= ac_dots= + fi + + case "$ac_given_srcdir" in + .) srcdir=. + if test -z "$ac_dots"; then top_srcdir=. + else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;; + /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;; + *) # Relative path. + srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix" + top_srcdir="$ac_dots$ac_given_srcdir" ;; + esac + + case "$ac_given_INSTALL" in + [/$]*) INSTALL="$ac_given_INSTALL" ;; + *) INSTALL="$ac_dots$ac_given_INSTALL" ;; + esac + + echo creating "$ac_file" + rm -f "$ac_file" + configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure." + case "$ac_file" in + *Makefile*) ac_comsub="1i\\ +# $configure_input" ;; + *) ac_comsub= ;; + esac + + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + sed -e "$ac_comsub +s%@configure_input@%$configure_input%g +s%@srcdir@%$srcdir%g +s%@top_srcdir@%$top_srcdir%g +s%@INSTALL@%$INSTALL%g +" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file +fi; done +rm -f conftest.s* + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='\([ ][ ]*\)[^ ]*%\1#\2' +ac_dC='\3' +ac_dD='%g' +# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE". +ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='\([ ]\)%\1#\2define\3' +ac_uC=' ' +ac_uD='\4%g' +# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_eB='$%\1#\2define\3' +ac_eC=' ' +ac_eD='%g' + +if test "${CONFIG_HEADERS+set}" != set; then +EOF +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF +fi +for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + echo creating $ac_file + + rm -f conftest.frag conftest.in conftest.out + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + cat $ac_file_inputs > conftest.in + +EOF + +# Transform confdefs.h into a sed script conftest.vals that substitutes +# the proper values into config.h.in to produce config.h. And first: +# Protect against being on the right side of a sed subst in config.status. +# Protect against being in an unquoted here document in config.status. +rm -f conftest.vals +cat > conftest.hdr <<\EOF +s/[\\&%]/\\&/g +s%[\\$`]%\\&%g +s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp +s%ac_d%ac_u%gp +s%ac_u%ac_e%gp +EOF +sed -n -f conftest.hdr confdefs.h > conftest.vals +rm -f conftest.hdr + +# This sed command replaces #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +cat >> conftest.vals <<\EOF +s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */% +EOF + +# Break up conftest.vals because some shells have a limit on +# the size of here documents, and old seds have small limits too. + +rm -f conftest.tail +while : +do + ac_lines=`grep -c . conftest.vals` + # grep -c gives empty output for an empty file on some AIX systems. + if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi + # Write a limited-size here document to conftest.frag. + echo ' cat > conftest.frag <> $CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS + echo 'CEOF + sed -f conftest.frag conftest.in > conftest.out + rm -f conftest.in + mv conftest.out conftest.in +' >> $CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail + rm -f conftest.vals + mv conftest.tail conftest.vals +done +rm -f conftest.vals + +cat >> $CONFIG_STATUS <<\EOF + rm -f conftest.frag conftest.h + echo "/* $ac_file. Generated automatically by configure. */" > conftest.h + cat conftest.in >> conftest.h + rm -f conftest.in + if cmp -s $ac_file conftest.h 2>/dev/null; then + echo "$ac_file is unchanged" + rm -f conftest.h + else + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + fi + rm -f $ac_file + mv conftest.h $ac_file + fi +fi; done + +EOF +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF + +exit 0 +EOF +chmod +x $CONFIG_STATUS +rm -fr confdefs* $ac_clean_files +test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1 + + +cat <&6 +AC_EGREP_CPP(yes, +[#include +#ifdef __RCSID +yes +#endif +], +echo yes 2>&6, +echo no 2>&6; CPPFLAGS="${CPPFLAGS} -I\${srcdir}/missing -DNEED_HOST_CDEFS_H"), +CPPFLAGS="${CPPFLAGS} -I\${srcdir}/missing") + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C___ATTRIBUTE__ +AC_C_BIGENDIAN +AC_C_CONST +AC_TYPE_OFF_T +AC_TYPE_PID_T +AC_TYPE_SIZE_T +AC_DECL_SYS_SIGLIST +AC_HEADER_TIME +AC_STRUCT_TM + +dnl Checks for library functions. +AC_TYPE_SIGNAL +AC_FUNC_VFORK +AC_FUNC_VPRINTF +AC_FUNC_WAIT3 +AC_CHECK_FUNCS(getcwd getwd getopt putenv select strdup strerror strtod strtol setenv getenv vsnprintf snprintf strftime sigaction sigvec waitpid wait4 wait3) +dnl AC_REPLACE_FUNCS(setenv getenv) +AC_REPLACE_FUNCS(getenv) +dnl +dnl Structures +dnl +AC_HEADER_STAT +AC_STRUCT_ST_RDEV +dnl +dnl +dnl +dnl Solaris's signal.h only privides sigset_t etc if one of +dnl _EXTENSIONS_ _POSIX_C_SOURCE or _XOPEN_SOURCE are defined. +dnl The later two seem to cause more problems than they solve so if we +dnl see _EXTENSIONS_ we use it. +dnl Note we _don't_ want AC_EGREP_HEADER as we don't want CPP involved. +dnl +AC_EGREP(__EXTENSIONS__, /usr/include/signal.h, +CPPFLAGS="$CPPFLAGS -D__EXTENSIONS__",) +dnl +dnl AC_* don't quite cut it. +dnl +echo $ac_n "Checking for MACHINE & MACHINE_ARCH... $ac_c" 2>&6 +cat > conftest.$ac_ext < +#ifdef MACHINE +machine=MACHINE +#endif +#ifdef MACHINE_ARCH +machine_arch=MACHINE_ARCH +#endif +EOF + +default_machine=`(eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep machine= | tr -d ' "'` +rm -rf conftest* +if test "$default_machine"; then + eval "$default_machine" +fi +machine=${machine:-`$srcdir/machine.sh`} +machine_arch=${machine_arch:-`$srcdir/machine.sh arch`} +echo "defaults: MACHINE=$machine, MACHINE_ARCH=$machine_arch" 1>&6 +dnl +dnl now allow overrides +dnl +AC_ARG_WITH(machine, +[ --with-machine=MACHINE explicitly set MACHINE], +[case "${withval}" in +yes) AC_MSG_ERROR(bad value ${withval} given for bmake MACHINE) ;; +no) ;; +generic) machine=`$srcdir/machine.sh`;; +*) machine=$with_machine;; +esac]) +force_machine= +AC_ARG_WITH(force_machine, +[ --with-force-machine=MACHINE set FORCE_MACHINE], +[case "${withval}" in +yes) force_machine=FORCE_;; +no) ;; +*) force_machine=FORCE_; machine=$with_force_machine;; +esac]) +dnl +AC_ARG_WITH(machine_arch, +[ --with-machine_arch=MACHINE_ARCH explicitly set MACHINE_ARCH], +[case "${withval}" in +yes) AC_MSG_ERROR(bad value ${withval} given for bmake MACHINE_ARCH) ;; +no) ;; +*) machine_arch=$with_machine_arch;; +esac]) +dnl +dnl Tell them what we ended up with +dnl +echo "Using: ${force_machine}MACHINE=$machine, MACHINE_ARCH=$machine_arch" 1>&6 +dnl +dnl Allow folk to control _PATH_DEFSYSPATH +dnl +AC_ARG_WITH(default-sys-path, +[ --with-default-sys-path=PATH:DIR:LIST use an explicit _PATH_DEFSYSPATH], +[case "${withval}" in +yes) AC_MSG_ERROR(bad value ${withval} given for bmake _PATH_DEFSYSPATH) ;; +no) ;; +*) CPPFLAGS="$CPPFLAGS \"-D_PATH_DEFSYSPATH=\\\"$with_default_sys_path\\\"\"" ;; +esac]) +dnl +dnl Or just to prefix it +dnl +AC_ARG_WITH(prefix-sys-path, +[ --with-prefix-sys-path=PATH:DIR:LIST prefix _PATH_PREFIX_SYSPATH], +[case "${withval}" in +yes) AC_MSG_ERROR(bad value ${withval} given for bmake _PATH_PREFIX_SYSPATH) ;; +no) ;; +*) CPPFLAGS="$CPPFLAGS \"-D_PATH_PREFIX_SYSPATH=\\\"$with_prefix_sys_path\\\"\"" ;; +esac]) +dnl +dnl Some folk don't like this one +dnl +AC_ARG_WITH(path-objdirprefix, +[ --with-path-objdirprefix=PATH override _PATH_OBJDIRPREFIX], +[case "${withval}" in +yes) AC_MSG_ERROR(bad value ${withval} given for bmake _PATH_OBJDIRPREFIX) ;; +no) CPPFLAGS="$CPPFLAGS -DNO_PATH_OBJDIRPREFIX" ;; +*) CPPFLAGS="$CPPFLAGS \"-D_PATH_OBJDIRPREFIX=\\\"$with_path-objdir\\\"\"" ;; +esac]) +dnl +dnl And this can be handy to do with out. +dnl +AC_ARG_ENABLE(pwd-override, +[ --disable-pwd-override disable \$PWD overriding getcwd()], +[case "${enableval}" in +yes) ;; +no) CPPFLAGS="$CPPFLAGS -DNO_PWD_OVERRIDE" ;; +*) AC_MSG_ERROR(bad value ${enableval} given for pwd-override option) ;; +esac]) +dnl +dnl Just for grins +dnl +AC_ARG_ENABLE(check-make-chdir, +[ --disable-check-make-chdir disable make trying to guess + when it should automatically cd \${.CURDIR}], +[case "${enableval}" in +yes) ;; +no) CPPFLAGS="$CPPFLAGS -DNO_CHECK_MAKE_CHDIR" ;; +*) AC_MSG_ERROR(bad value ${enableval} given for check-make-chdir option) ;; +esac]) +AC_SUBST(machine) +AC_SUBST(force_machine) +AC_SUBST(machine_arch) +AC_OUTPUT(Makefile makefile.boot lst.lib/makefile.boot) + +cat < +#ifndef lint +#if 0 +static char sccsid[] = "@(#)dir.c 8.2 (Berkeley) 1/2/94"; +#else +__RCSID("$NetBSD: dir.c,v 1.1.1.1 2004/03/11 13:04:07 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +#if !defined(MAKE_BOOTSTRAP) && !defined(lint) +__IDSTRING(rcs_id,"$Id: dir.c,v 1.1.1.1 2004/03/11 13:04:07 grant Exp $"); +#endif + +/*- + * dir.c -- + * Directory searching using wildcards and/or normal names... + * Used both for source wildcarding in the Makefile and for finding + * implicit sources. + * + * The interface for this module is: + * Dir_Init Initialize the module. + * + * Dir_End Cleanup the module. + * + * Dir_HasWildcards Returns TRUE if the name given it needs to + * be wildcard-expanded. + * + * Dir_Expand Given a pattern and a path, return a Lst of names + * which match the pattern on the search path. + * + * Dir_FindFile Searches for a file on a given search path. + * If it exists, the entire path is returned. + * Otherwise NULL is returned. + * + * Dir_MTime Return the modification time of a node. The file + * is searched for along the default search path. + * The path and mtime fields of the node are filled + * in. + * + * Dir_AddDir Add a directory to a search path. + * + * Dir_MakeFlags Given a search path and a command flag, create + * a string with each of the directories in the path + * preceded by the command flag and all of them + * separated by a space. + * + * Dir_Destroy Destroy an element of a search path. Frees up all + * things that can be freed for the element as long + * as the element is no longer referenced by any other + * search path. + * Dir_ClearPath Resets a search path to the empty list. + * + * For debugging: + * Dir_PrintDirectories Print stats about the directory cache. + */ + +#include +#include +#include +#include +#include +#include "make.h" +#include "hash.h" +#include "dir.h" + +/* + * A search path consists of a Lst of Path structures. A Path structure + * has in it the name of the directory and a hash table of all the files + * in the directory. This is used to cut down on the number of system + * calls necessary to find implicit dependents and their like. Since + * these searches are made before any actions are taken, we need not + * worry about the directory changing due to creation commands. If this + * hampers the style of some makefiles, they must be changed. + * + * A list of all previously-read directories is kept in the + * openDirectories Lst. This list is checked first before a directory + * is opened. + * + * The need for the caching of whole directories is brought about by + * the multi-level transformation code in suff.c, which tends to search + * for far more files than regular make does. In the initial + * implementation, the amount of time spent performing "stat" calls was + * truly astronomical. The problem with hashing at the start is, + * of course, that pmake doesn't then detect changes to these directories + * during the course of the make. Three possibilities suggest themselves: + * + * 1) just use stat to test for a file's existence. As mentioned + * above, this is very inefficient due to the number of checks + * engendered by the multi-level transformation code. + * 2) use readdir() and company to search the directories, keeping + * them open between checks. I have tried this and while it + * didn't slow down the process too much, it could severely + * affect the amount of parallelism available as each directory + * open would take another file descriptor out of play for + * handling I/O for another job. Given that it is only recently + * that UNIX OS's have taken to allowing more than 20 or 32 + * file descriptors for a process, this doesn't seem acceptable + * to me. + * 3) record the mtime of the directory in the Path structure and + * verify the directory hasn't changed since the contents were + * hashed. This will catch the creation or deletion of files, + * but not the updating of files. However, since it is the + * creation and deletion that is the problem, this could be + * a good thing to do. Unfortunately, if the directory (say ".") + * were fairly large and changed fairly frequently, the constant + * rehashing could seriously degrade performance. It might be + * good in such cases to keep track of the number of rehashes + * and if the number goes over a (small) limit, resort to using + * stat in its place. + * + * An additional thing to consider is that pmake is used primarily + * to create C programs and until recently pcc-based compilers refused + * to allow you to specify where the resulting object file should be + * placed. This forced all objects to be created in the current + * directory. This isn't meant as a full excuse, just an explanation of + * some of the reasons for the caching used here. + * + * One more note: the location of a target's file is only performed + * on the downward traversal of the graph and then only for terminal + * nodes in the graph. This could be construed as wrong in some cases, + * but prevents inadvertent modification of files when the "installed" + * directory for a file is provided in the search path. + * + * Another data structure maintained by this module is an mtime + * cache used when the searching of cached directories fails to find + * a file. In the past, Dir_FindFile would simply perform an access() + * call in such a case to determine if the file could be found using + * just the name given. When this hit, however, all that was gained + * was the knowledge that the file existed. Given that an access() is + * essentially a stat() without the copyout() call, and that the same + * filesystem overhead would have to be incurred in Dir_MTime, it made + * sense to replace the access() with a stat() and record the mtime + * in a cache for when Dir_MTime was actually called. + */ + +Lst dirSearchPath; /* main search path */ + +static Lst openDirectories; /* the list of all open directories */ + +/* + * Variables for gathering statistics on the efficiency of the hashing + * mechanism. + */ +static int hits, /* Found in directory cache */ + misses, /* Sad, but not evil misses */ + nearmisses, /* Found under search path */ + bigmisses; /* Sought by itself */ + +static Path *dot; /* contents of current directory */ +static Path *cur; /* contents of current directory, if not dot */ +static Path *dotLast; /* a fake path entry indicating we need to + * look for . last */ +static Hash_Table mtimes; /* Results of doing a last-resort stat in + * Dir_FindFile -- if we have to go to the + * system to find the file, we might as well + * have its mtime on record. XXX: If this is done + * way early, there's a chance other rules will + * have already updated the file, in which case + * we'll update it again. Generally, there won't + * be two rules to update a single file, so this + * should be ok, but... */ + + +static int DirFindName __P((ClientData, ClientData)); +static int DirMatchFiles __P((char *, Path *, Lst)); +static void DirExpandCurly __P((char *, char *, Lst, Lst)); +static void DirExpandInt __P((char *, Lst, Lst)); +static int DirPrintWord __P((ClientData, ClientData)); +static int DirPrintDir __P((ClientData, ClientData)); +static char *DirLookup __P((Path *, char *, char *, Boolean)); +static char *DirLookupSubdir __P((Path *, char *)); +static char *DirFindDot __P((Boolean, char *, char *)); + +/*- + *----------------------------------------------------------------------- + * Dir_Init -- + * initialize things for this module + * + * Results: + * none + * + * Side Effects: + * some directories may be opened. + *----------------------------------------------------------------------- + */ +void +Dir_Init (cdname) + const char *cdname; +{ + dirSearchPath = Lst_Init (FALSE); + openDirectories = Lst_Init (FALSE); + Hash_InitTable(&mtimes, 0); + + /* + * Since the Path structure is placed on both openDirectories and + * the path we give Dir_AddDir (which in this case is openDirectories), + * we need to remove "." from openDirectories and what better time to + * do it than when we have to fetch the thing anyway? + */ + dot = Dir_AddDir (NULL, "."); + if (dot == NULL) { + Error("Cannot open `.' (%s)", strerror(errno)); + exit(1); + } + + /* + * We always need to have dot around, so we increment its reference count + * to make sure it's not destroyed. + */ + dot->refCount += 1; + + if (cdname != NULL) { + /* + * Our build directory is not the same as our source directory. + * Keep this one around too. + */ + cur = Dir_AddDir (NULL, cdname); + cur->refCount += 1; + } + + dotLast = (Path *) emalloc (sizeof (Path)); + dotLast->refCount = 1; + dotLast->hits = 0; + dotLast->name = estrdup(".DOTLAST"); + Hash_InitTable (&dotLast->files, -1); +} + +/*- + *----------------------------------------------------------------------- + * Dir_End -- + * cleanup things for this module + * + * Results: + * none + * + * Side Effects: + * none + *----------------------------------------------------------------------- + */ +void +Dir_End() +{ +#ifdef CLEANUP + if (cur) { + cur->refCount -= 1; + Dir_Destroy((ClientData) cur); + } + dot->refCount -= 1; + dotLast->refCount -= 1; + Dir_Destroy((ClientData) dotLast); + Dir_Destroy((ClientData) dot); + Dir_ClearPath(dirSearchPath); + Lst_Destroy(dirSearchPath, NOFREE); + Dir_ClearPath(openDirectories); + Lst_Destroy(openDirectories, NOFREE); + Hash_DeleteTable(&mtimes); +#endif +} + +/*- + *----------------------------------------------------------------------- + * DirFindName -- + * See if the Path structure describes the same directory as the + * given one by comparing their names. Called from Dir_AddDir via + * Lst_Find when searching the list of open directories. + * + * Results: + * 0 if it is the same. Non-zero otherwise + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +static int +DirFindName (p, dname) + ClientData p; /* Current name */ + ClientData dname; /* Desired name */ +{ + return (strcmp (((Path *)p)->name, (char *) dname)); +} + +/*- + *----------------------------------------------------------------------- + * Dir_HasWildcards -- + * see if the given name has any wildcard characters in it + * be careful not to expand unmatching brackets or braces. + * XXX: This code is not 100% correct. ([^]] fails etc.) + * I really don't think that make(1) should be expanding + * patterns, because then you have to set a mechanism for + * escaping the expansion! + * + * Results: + * returns TRUE if the word should be expanded, FALSE otherwise + * + * Side Effects: + * none + *----------------------------------------------------------------------- + */ +Boolean +Dir_HasWildcards (name) + char *name; /* name to check */ +{ + register char *cp; + int wild = 0, brace = 0, bracket = 0; + + for (cp = name; *cp; cp++) { + switch(*cp) { + case '{': + brace++; + wild = 1; + break; + case '}': + brace--; + break; + case '[': + bracket++; + wild = 1; + break; + case ']': + bracket--; + break; + case '?': + case '*': + wild = 1; + break; + default: + break; + } + } + return wild && bracket == 0 && brace == 0; +} + +/*- + *----------------------------------------------------------------------- + * DirMatchFiles -- + * Given a pattern and a Path structure, see if any files + * match the pattern and add their names to the 'expansions' list if + * any do. This is incomplete -- it doesn't take care of patterns like + * src / *src / *.c properly (just *.c on any of the directories), but it + * will do for now. + * + * Results: + * Always returns 0 + * + * Side Effects: + * File names are added to the expansions lst. The directory will be + * fully hashed when this is done. + *----------------------------------------------------------------------- + */ +static int +DirMatchFiles (pattern, p, expansions) + char *pattern; /* Pattern to look for */ + Path *p; /* Directory to search */ + Lst expansions; /* Place to store the results */ +{ + Hash_Search search; /* Index into the directory's table */ + Hash_Entry *entry; /* Current entry in the table */ + Boolean isDot; /* TRUE if the directory being searched is . */ + + isDot = (*p->name == '.' && p->name[1] == '\0'); + + for (entry = Hash_EnumFirst(&p->files, &search); + entry != (Hash_Entry *)NULL; + entry = Hash_EnumNext(&search)) + { + /* + * See if the file matches the given pattern. Note we follow the UNIX + * convention that dot files will only be found if the pattern + * begins with a dot (note also that as a side effect of the hashing + * scheme, .* won't match . or .. since they aren't hashed). + */ + if (Str_Match(entry->name, pattern) && + ((entry->name[0] != '.') || + (pattern[0] == '.'))) + { + (void)Lst_AtEnd(expansions, + (isDot ? estrdup(entry->name) : + str_concat(p->name, entry->name, + STR_ADDSLASH))); + } + } + return (0); +} + +/*- + *----------------------------------------------------------------------- + * DirExpandCurly -- + * Expand curly braces like the C shell. Does this recursively. + * Note the special case: if after the piece of the curly brace is + * done there are no wildcard characters in the result, the result is + * placed on the list WITHOUT CHECKING FOR ITS EXISTENCE. + * + * Results: + * None. + * + * Side Effects: + * The given list is filled with the expansions... + * + *----------------------------------------------------------------------- + */ +static void +DirExpandCurly(word, brace, path, expansions) + char *word; /* Entire word to expand */ + char *brace; /* First curly brace in it */ + Lst path; /* Search path to use */ + Lst expansions; /* Place to store the expansions */ +{ + char *end; /* Character after the closing brace */ + char *cp; /* Current position in brace clause */ + char *start; /* Start of current piece of brace clause */ + int bracelevel; /* Number of braces we've seen. If we see a + * right brace when this is 0, we've hit the + * end of the clause. */ + char *file; /* Current expansion */ + int otherLen; /* The length of the other pieces of the + * expansion (chars before and after the + * clause in 'word') */ + char *cp2; /* Pointer for checking for wildcards in + * expansion before calling Dir_Expand */ + + start = brace+1; + + /* + * Find the end of the brace clause first, being wary of nested brace + * clauses. + */ + for (end = start, bracelevel = 0; *end != '\0'; end++) { + if (*end == '{') { + bracelevel++; + } else if ((*end == '}') && (bracelevel-- == 0)) { + break; + } + } + if (*end == '\0') { + Error("Unterminated {} clause \"%s\"", start); + return; + } else { + end++; + } + otherLen = brace - word + strlen(end); + + for (cp = start; cp < end; cp++) { + /* + * Find the end of this piece of the clause. + */ + bracelevel = 0; + while (*cp != ',') { + if (*cp == '{') { + bracelevel++; + } else if ((*cp == '}') && (bracelevel-- <= 0)) { + break; + } + cp++; + } + /* + * Allocate room for the combination and install the three pieces. + */ + file = emalloc(otherLen + cp - start + 1); + if (brace != word) { + strncpy(file, word, brace-word); + } + if (cp != start) { + strncpy(&file[brace-word], start, cp-start); + } + strcpy(&file[(brace-word)+(cp-start)], end); + + /* + * See if the result has any wildcards in it. If we find one, call + * Dir_Expand right away, telling it to place the result on our list + * of expansions. + */ + for (cp2 = file; *cp2 != '\0'; cp2++) { + switch(*cp2) { + case '*': + case '?': + case '{': + case '[': + Dir_Expand(file, path, expansions); + goto next; + } + } + if (*cp2 == '\0') { + /* + * Hit the end w/o finding any wildcards, so stick the expansion + * on the end of the list. + */ + (void)Lst_AtEnd(expansions, file); + } else { + next: + free(file); + } + start = cp+1; + } +} + + +/*- + *----------------------------------------------------------------------- + * DirExpandInt -- + * Internal expand routine. Passes through the directories in the + * path one by one, calling DirMatchFiles for each. NOTE: This still + * doesn't handle patterns in directories... + * + * Results: + * None. + * + * Side Effects: + * Things are added to the expansions list. + * + *----------------------------------------------------------------------- + */ +static void +DirExpandInt(word, path, expansions) + char *word; /* Word to expand */ + Lst path; /* Path on which to look */ + Lst expansions; /* Place to store the result */ +{ + LstNode ln; /* Current node */ + Path *p; /* Directory in the node */ + + if (Lst_Open(path) == SUCCESS) { + while ((ln = Lst_Next(path)) != NILLNODE) { + p = (Path *)Lst_Datum(ln); + DirMatchFiles(word, p, expansions); + } + Lst_Close(path); + } +} + +/*- + *----------------------------------------------------------------------- + * DirPrintWord -- + * Print a word in the list of expansions. Callback for Dir_Expand + * when DEBUG(DIR), via Lst_ForEach. + * + * Results: + * === 0 + * + * Side Effects: + * The passed word is printed, followed by a space. + * + *----------------------------------------------------------------------- + */ +static int +DirPrintWord(word, dummy) + ClientData word; + ClientData dummy; +{ + printf("%s ", (char *) word); + + return(dummy ? 0 : 0); +} + +/*- + *----------------------------------------------------------------------- + * Dir_Expand -- + * Expand the given word into a list of words by globbing it looking + * in the directories on the given search path. + * + * Results: + * A list of words consisting of the files which exist along the search + * path matching the given pattern. + * + * Side Effects: + * Directories may be opened. Who knows? + *----------------------------------------------------------------------- + */ +void +Dir_Expand (word, path, expansions) + char *word; /* the word to expand */ + Lst path; /* the list of directories in which to find + * the resulting files */ + Lst expansions; /* the list on which to place the results */ +{ + char *cp; + + if (DEBUG(DIR)) { + printf("expanding \"%s\"...", word); + } + + cp = strchr(word, '{'); + if (cp) { + DirExpandCurly(word, cp, path, expansions); + } else { + cp = strchr(word, '/'); + if (cp) { + /* + * The thing has a directory component -- find the first wildcard + * in the string. + */ + for (cp = word; *cp; cp++) { + if (*cp == '?' || *cp == '[' || *cp == '*' || *cp == '{') { + break; + } + } + if (*cp == '{') { + /* + * This one will be fun. + */ + DirExpandCurly(word, cp, path, expansions); + return; + } else if (*cp != '\0') { + /* + * Back up to the start of the component + */ + char *dirpath; + + while (cp > word && *cp != '/') { + cp--; + } + if (cp != word) { + char sc; + /* + * If the glob isn't in the first component, try and find + * all the components up to the one with a wildcard. + */ + sc = cp[1]; + cp[1] = '\0'; + dirpath = Dir_FindFile(word, path); + cp[1] = sc; + /* + * dirpath is null if can't find the leading component + * XXX: Dir_FindFile won't find internal components. + * i.e. if the path contains ../Etc/Object and we're + * looking for Etc, it won't be found. Ah well. + * Probably not important. + */ + if (dirpath != (char *)NULL) { + char *dp = &dirpath[strlen(dirpath) - 1]; + if (*dp == '/') + *dp = '\0'; + path = Lst_Init(FALSE); + (void) Dir_AddDir(path, dirpath); + DirExpandInt(cp+1, path, expansions); + Lst_Destroy(path, NOFREE); + } + } else { + /* + * Start the search from the local directory + */ + DirExpandInt(word, path, expansions); + } + } else { + /* + * Return the file -- this should never happen. + */ + DirExpandInt(word, path, expansions); + } + } else { + /* + * First the files in dot + */ + DirMatchFiles(word, dot, expansions); + + /* + * Then the files in every other directory on the path. + */ + DirExpandInt(word, path, expansions); + } + } + if (DEBUG(DIR)) { + Lst_ForEach(expansions, DirPrintWord, (ClientData) 0); + fputc('\n', stdout); + } +} + +/*- + *----------------------------------------------------------------------- + * DirLookup -- + * Find if the file with the given name exists in the given path. + * + * Results: + * The path to the file, the empty string or NULL. If the file is + * the empty string, the search should be terminated. + * This path is guaranteed to be in a + * different part of memory than name and so may be safely free'd. + * + * Side Effects: + * None. + *----------------------------------------------------------------------- + */ +static char * +DirLookup(p, name, cp, hasSlash) + Path *p; + char *name; + char *cp; + Boolean hasSlash; +{ + char *p1; /* pointer into p->name */ + char *p2; /* pointer into name */ + char *file; /* the current filename to check */ + + if (DEBUG(DIR)) { + printf("%s...", p->name); + } + if (Hash_FindEntry (&p->files, cp) != (Hash_Entry *)NULL) { + if (DEBUG(DIR)) { + printf("here..."); + } + if (hasSlash) { + /* + * If the name had a slash, its initial components and p's + * final components must match. This is false if a mismatch + * is encountered before all of the initial components + * have been checked (p2 > name at the end of the loop), or + * we matched only part of one of the components of p + * along with all the rest of them (*p1 != '/'). + */ + p1 = p->name + strlen (p->name) - 1; + p2 = cp - 2; + while (p2 >= name && p1 >= p->name && *p1 == *p2) { + p1 -= 1; p2 -= 1; + } + if (p2 >= name || (p1 >= p->name && *p1 != '/')) { + if (DEBUG(DIR)) { + printf("component mismatch -- continuing..."); + } + return NULL; + } + } + file = str_concat (p->name, cp, STR_ADDSLASH); + if (DEBUG(DIR)) { + printf("returning %s\n", file); + } + p->hits += 1; + hits += 1; + return file; + } else if (hasSlash) { + /* + * If the file has a leading path component and that component + * exactly matches the entire name of the current search + * directory, we assume the file doesn't exist and return NULL. + */ + for (p1 = p->name, p2 = name; *p1 && *p1 == *p2; p1++, p2++) { + continue; + } + if (*p1 == '\0' && p2 == cp - 1) { + if (DEBUG(DIR)) { + printf("must be here but isn't -- returing\n"); + } + return ""; + } + } + return NULL; +} + + +/*- + *----------------------------------------------------------------------- + * DirLookupSubdir -- + * Find if the file with the given name exists in the given path. + * + * Results: + * The path to the file or NULL. This path is guaranteed to be in a + * different part of memory than name and so may be safely free'd. + * + * Side Effects: + * If the file is found, it is added in the modification times hash + * table. + *----------------------------------------------------------------------- + */ +static char * +DirLookupSubdir(p, name) + Path *p; + char *name; +{ + struct stat stb; /* Buffer for stat, if necessary */ + Hash_Entry *entry; /* Entry for mtimes table */ + char *file; /* the current filename to check */ + + if (p != dot) { + file = str_concat (p->name, name, STR_ADDSLASH); + } else { + /* + * Checking in dot -- DON'T put a leading ./ on the thing. + */ + file = estrdup(name); + } + + if (DEBUG(DIR)) { + printf("checking %s...", file); + } + + if (stat (file, &stb) == 0) { + if (DEBUG(DIR)) { + printf("got it.\n"); + } + + /* + * Save the modification time so if it's needed, we don't have + * to fetch it again. + */ + if (DEBUG(DIR)) { + printf("Caching %s for %s\n", Targ_FmtTime(stb.st_mtime), + file); + } + entry = Hash_CreateEntry(&mtimes, (char *) file, + (Boolean *)NULL); + Hash_SetValue(entry, (long)stb.st_mtime); + nearmisses += 1; + return (file); + } + free (file); + return NULL; +} + +/*- + *----------------------------------------------------------------------- + * DirFindDot -- + * Find the file given on "." or curdir + * + * Results: + * The path to the file or NULL. This path is guaranteed to be in a + * different part of memory than name and so may be safely free'd. + * + * Side Effects: + * Hit counts change + *----------------------------------------------------------------------- + */ +static char * +DirFindDot(hasSlash, name, cp) + Boolean hasSlash; + char *name; + char *cp; +{ + char *file; + + if ((!hasSlash || (cp - name == 2 && *name == '.'))) { + if (Hash_FindEntry (&dot->files, cp) != (Hash_Entry *)NULL) { + if (DEBUG(DIR)) { + printf("in '.'\n"); + } + hits += 1; + dot->hits += 1; + return (estrdup (name)); + } + if (cur && + Hash_FindEntry (&cur->files, cp) != (Hash_Entry *)NULL) { + if (DEBUG(DIR)) { + printf("in ${.CURDIR} = %s\n", cur->name); + } + hits += 1; + cur->hits += 1; + return str_concat (cur->name, cp, STR_ADDSLASH); + } + } + + + if (cur && (file = DirLookup(cur, name, cp, hasSlash)) != NULL) { + if (*file) + return file; + else + return NULL; + } + return NULL; +} + +/*- + *----------------------------------------------------------------------- + * Dir_FindFile -- + * Find the file with the given name along the given search path. + * + * Results: + * The path to the file or NULL. This path is guaranteed to be in a + * different part of memory than name and so may be safely free'd. + * + * Side Effects: + * If the file is found in a directory which is not on the path + * already (either 'name' is absolute or it is a relative path + * [ dir1/.../dirn/file ] which exists below one of the directories + * already on the search path), its directory is added to the end + * of the path on the assumption that there will be more files in + * that directory later on. Sometimes this is true. Sometimes not. + *----------------------------------------------------------------------- + */ +char * +Dir_FindFile (name, path) + char *name; /* the file to find */ + Lst path; /* the Lst of directories to search */ +{ + LstNode ln; /* a list element */ + register char *file; /* the current filename to check */ + register Path *p; /* current path member */ + register char *cp; /* index of first slash, if any */ + Boolean lastDot = FALSE; /* true we should search dot last */ + Boolean hasSlash; /* true if 'name' contains a / */ + struct stat stb; /* Buffer for stat, if necessary */ + Hash_Entry *entry; /* Entry for mtimes table */ + + /* + * Find the final component of the name and note whether it has a + * slash in it (the name, I mean) + */ + cp = strrchr (name, '/'); + if (cp) { + hasSlash = TRUE; + cp += 1; + } else { + hasSlash = FALSE; + cp = name; + } + + if (DEBUG(DIR)) { + printf("Searching for %s...", name); + } + + if (Lst_Open (path) == FAILURE) { + if (DEBUG(DIR)) { + printf("couldn't open path, file not found\n"); + } + misses += 1; + return ((char *) NULL); + } + + if ((ln = Lst_First (path)) != NILLNODE) { + p = (Path *) Lst_Datum (ln); + if (p == dotLast) + lastDot = TRUE; + if (DEBUG(DIR)) { + printf("[dot last]..."); + } + } + + /* + * No matter what, we always look for the file in the current directory + * before anywhere else and we *do not* add the ./ to it if it exists. + * This is so there are no conflicts between what the user specifies + * (fish.c) and what pmake finds (./fish.c). + * Unless we found the magic DOTLAST path... + */ + if (!lastDot && (file = DirFindDot(hasSlash, name, cp)) != NULL) + return file; + + + /* + * We look through all the directories on the path seeking one which + * contains the final component of the given name and whose final + * component(s) match the name's initial component(s). If such a beast + * is found, we concatenate the directory name and the final component + * and return the resulting string. If we don't find any such thing, + * we go on to phase two... + */ + while ((ln = Lst_Next (path)) != NILLNODE) { + p = (Path *) Lst_Datum (ln); + if (p == dotLast) + continue; + if ((file = DirLookup(p, name, cp, hasSlash)) != NULL) { + Lst_Close (path); + if (*file) + return file; + else + return NULL; + } + } + + if (lastDot && (file = DirFindDot(hasSlash, name, cp)) != NULL) + return file; + + /* + * We didn't find the file on any existing members of the directory. + * If the name doesn't contain a slash, that means it doesn't exist. + * If it *does* contain a slash, however, there is still hope: it + * could be in a subdirectory of one of the members of the search + * path. (eg. /usr/include and sys/types.h. The above search would + * fail to turn up types.h in /usr/include, but it *is* in + * /usr/include/sys/types.h) If we find such a beast, we assume there + * will be more (what else can we assume?) and add all but the last + * component of the resulting name onto the search path (at the + * end). This phase is only performed if the file is *not* absolute. + */ + if (!hasSlash) { + if (DEBUG(DIR)) { + printf("failed.\n"); + } + misses += 1; + return ((char *) NULL); + } + + if (*name != '/') { + Boolean checkedDot = FALSE; + + if (DEBUG(DIR)) { + printf("failed. Trying subdirectories..."); + } + + if (!lastDot && cur && (file = DirLookupSubdir(cur, name)) != NULL) + return file; + + (void) Lst_Open (path); + while ((ln = Lst_Next (path)) != NILLNODE) { + p = (Path *) Lst_Datum (ln); + if (p == dotLast) + continue; + if (p == dot) + checkedDot = TRUE; + if ((file = DirLookupSubdir(p, name)) != NULL) { + Lst_Close (path); + return file; + } + } + + if (lastDot && cur && (file = DirLookupSubdir(cur, name)) != NULL) + return file; + + if (DEBUG(DIR)) { + printf("failed. "); + } + Lst_Close (path); + + if (checkedDot) { + /* + * Already checked by the given name, since . was in the path, + * so no point in proceeding... + */ + if (DEBUG(DIR)) { + printf("Checked . already, returning NULL\n"); + } + return(NULL); + } + } + + /* + * Didn't find it that way, either. Sigh. Phase 3. Add its directory + * onto the search path in any case, just in case, then look for the + * thing in the hash table. If we find it, grand. We return a new + * copy of the name. Otherwise we sadly return a NULL pointer. Sigh. + * Note that if the directory holding the file doesn't exist, this will + * do an extra search of the final directory on the path. Unless something + * weird happens, this search won't succeed and life will be groovy. + * + * Sigh. We cannot add the directory onto the search path because + * of this amusing case: + * $(INSTALLDIR)/$(FILE): $(FILE) + * + * $(FILE) exists in $(INSTALLDIR) but not in the current one. + * When searching for $(FILE), we will find it in $(INSTALLDIR) + * b/c we added it here. This is not good... + */ +#ifdef notdef + cp[-1] = '\0'; + (void) Dir_AddDir (path, name); + cp[-1] = '/'; + + bigmisses += 1; + ln = Lst_Last (path); + if (ln == NILLNODE) { + return ((char *) NULL); + } else { + p = (Path *) Lst_Datum (ln); + } + + if (Hash_FindEntry (&p->files, cp) != (Hash_Entry *)NULL) { + return (estrdup (name)); + } else { + return ((char *) NULL); + } +#else /* !notdef */ + if (DEBUG(DIR)) { + printf("Looking for \"%s\"...", name); + } + + bigmisses += 1; + entry = Hash_FindEntry(&mtimes, name); + if (entry != (Hash_Entry *)NULL) { + if (DEBUG(DIR)) { + printf("got it (in mtime cache)\n"); + } + return(estrdup(name)); + } else if (stat (name, &stb) == 0) { + entry = Hash_CreateEntry(&mtimes, name, (Boolean *)NULL); + if (DEBUG(DIR)) { + printf("Caching %s for %s\n", Targ_FmtTime(stb.st_mtime), + name); + } + Hash_SetValue(entry, (long)stb.st_mtime); + return (estrdup (name)); + } else { + if (DEBUG(DIR)) { + printf("failed. Returning NULL\n"); + } + return ((char *)NULL); + } +#endif /* notdef */ +} + +/*- + *----------------------------------------------------------------------- + * Dir_MTime -- + * Find the modification time of the file described by gn along the + * search path dirSearchPath. + * + * Results: + * The modification time or 0 if it doesn't exist + * + * Side Effects: + * The modification time is placed in the node's mtime slot. + * If the node didn't have a path entry before, and Dir_FindFile + * found one for it, the full name is placed in the path slot. + *----------------------------------------------------------------------- + */ +int +Dir_MTime (gn) + GNode *gn; /* the file whose modification time is + * desired */ +{ + char *fullName; /* the full pathname of name */ + struct stat stb; /* buffer for finding the mod time */ + Hash_Entry *entry; + + if (gn->type & OP_ARCHV) { + return Arch_MTime (gn); + } else if (gn->path == (char *)NULL) { + if (gn->type & (OP_PHONY|OP_NOPATH)) + fullName = NULL; + else + fullName = Dir_FindFile (gn->name, dirSearchPath); + } else { + fullName = gn->path; + } + + if (fullName == (char *)NULL) { + fullName = estrdup(gn->name); + } + + entry = Hash_FindEntry(&mtimes, fullName); + if (entry != (Hash_Entry *)NULL) { + /* + * Only do this once -- the second time folks are checking to + * see if the file was actually updated, so we need to actually go + * to the file system. + */ + if (DEBUG(DIR)) { + printf("Using cached time %s for %s\n", + Targ_FmtTime((time_t)(long)Hash_GetValue(entry)), fullName); + } + stb.st_mtime = (time_t)(long)Hash_GetValue(entry); + Hash_DeleteEntry(&mtimes, entry); + } else if (stat (fullName, &stb) < 0) { + if (gn->type & OP_MEMBER) { + if (fullName != gn->path) + free(fullName); + return Arch_MemMTime (gn); + } else { + stb.st_mtime = 0; + } + } + if (fullName && gn->path == (char *)NULL) { + gn->path = fullName; + } + + gn->mtime = stb.st_mtime; + return (gn->mtime); +} + +/*- + *----------------------------------------------------------------------- + * Dir_AddDir -- + * Add the given name to the end of the given path. The order of + * the arguments is backwards so ParseDoDependency can do a + * Lst_ForEach of its list of paths... + * + * Results: + * none + * + * Side Effects: + * A structure is added to the list and the directory is + * read and hashed. + *----------------------------------------------------------------------- + */ +Path * +Dir_AddDir (path, name) + Lst path; /* the path to which the directory should be + * added */ + const char *name; /* the name of the directory to add */ +{ + LstNode ln; /* node in case Path structure is found */ + register Path *p = NULL; /* pointer to new Path structure */ + DIR *d; /* for reading directory */ + register struct dirent *dp; /* entry in directory */ + + if (strcmp(name, ".DOTLAST") == 0) { + ln = Lst_Find (path, (ClientData)name, DirFindName); + if (ln != NILLNODE) + return (Path *) Lst_Datum(ln); + else { + dotLast->refCount += 1; + (void)Lst_AtFront(path, (ClientData)dotLast); + } + } + + ln = Lst_Find (openDirectories, (ClientData)name, DirFindName); + if (ln != NILLNODE) { + p = (Path *)Lst_Datum (ln); + if (Lst_Member(path, (ClientData)p) == NILLNODE) { + p->refCount += 1; + (void)Lst_AtEnd (path, (ClientData)p); + } + } else { + if (DEBUG(DIR)) { + printf("Caching %s...", name); + fflush(stdout); + } + + if ((d = opendir (name)) != (DIR *) NULL) { + p = (Path *) emalloc (sizeof (Path)); + p->name = estrdup (name); + p->hits = 0; + p->refCount = 1; + Hash_InitTable (&p->files, -1); + + /* + * Skip the first two entries -- these will *always* be . and .. + */ + (void)readdir(d); + (void)readdir(d); + + while ((dp = readdir (d)) != (struct dirent *) NULL) { +#if defined(sun) && defined(d_ino) /* d_ino is a sunos4 #define for d_fileno */ + /* + * The sun directory library doesn't check for a 0 inode + * (0-inode slots just take up space), so we have to do + * it ourselves. + */ + if (dp->d_fileno == 0) { + continue; + } +#endif /* sun && d_ino */ + (void)Hash_CreateEntry(&p->files, dp->d_name, (Boolean *)NULL); + } + (void) closedir (d); + (void)Lst_AtEnd (openDirectories, (ClientData)p); + if (path != NULL) + (void)Lst_AtEnd (path, (ClientData)p); + } + if (DEBUG(DIR)) { + printf("done\n"); + } + } + return p; +} + +/*- + *----------------------------------------------------------------------- + * Dir_CopyDir -- + * Callback function for duplicating a search path via Lst_Duplicate. + * Ups the reference count for the directory. + * + * Results: + * Returns the Path it was given. + * + * Side Effects: + * The refCount of the path is incremented. + * + *----------------------------------------------------------------------- + */ +ClientData +Dir_CopyDir(p) + ClientData p; +{ + ((Path *) p)->refCount += 1; + + return ((ClientData)p); +} + +/*- + *----------------------------------------------------------------------- + * Dir_MakeFlags -- + * Make a string by taking all the directories in the given search + * path and preceding them by the given flag. Used by the suffix + * module to create variables for compilers based on suffix search + * paths. + * + * Results: + * The string mentioned above. Note that there is no space between + * the given flag and each directory. The empty string is returned if + * Things don't go well. + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +char * +Dir_MakeFlags (flag, path) + char *flag; /* flag which should precede each directory */ + Lst path; /* list of directories */ +{ + char *str; /* the string which will be returned */ + char *tstr; /* the current directory preceded by 'flag' */ + LstNode ln; /* the node of the current directory */ + Path *p; /* the structure describing the current directory */ + + str = estrdup (""); + + if (Lst_Open (path) == SUCCESS) { + while ((ln = Lst_Next (path)) != NILLNODE) { + p = (Path *) Lst_Datum (ln); + tstr = str_concat (flag, p->name, 0); + str = str_concat (str, tstr, STR_ADDSPACE | STR_DOFREE); + } + Lst_Close (path); + } + + return (str); +} + +/*- + *----------------------------------------------------------------------- + * Dir_Destroy -- + * Nuke a directory descriptor, if possible. Callback procedure + * for the suffixes module when destroying a search path. + * + * Results: + * None. + * + * Side Effects: + * If no other path references this directory (refCount == 0), + * the Path and all its data are freed. + * + *----------------------------------------------------------------------- + */ +void +Dir_Destroy (pp) + ClientData pp; /* The directory descriptor to nuke */ +{ + Path *p = (Path *) pp; + p->refCount -= 1; + + if (p->refCount == 0) { + LstNode ln; + + ln = Lst_Member (openDirectories, (ClientData)p); + (void) Lst_Remove (openDirectories, ln); + + Hash_DeleteTable (&p->files); + free((Address)p->name); + free((Address)p); + } +} + +/*- + *----------------------------------------------------------------------- + * Dir_ClearPath -- + * Clear out all elements of the given search path. This is different + * from destroying the list, notice. + * + * Results: + * None. + * + * Side Effects: + * The path is set to the empty list. + * + *----------------------------------------------------------------------- + */ +void +Dir_ClearPath(path) + Lst path; /* Path to clear */ +{ + Path *p; + while (!Lst_IsEmpty(path)) { + p = (Path *)Lst_DeQueue(path); + Dir_Destroy((ClientData) p); + } +} + + +/*- + *----------------------------------------------------------------------- + * Dir_Concat -- + * Concatenate two paths, adding the second to the end of the first. + * Makes sure to avoid duplicates. + * + * Results: + * None + * + * Side Effects: + * Reference counts for added dirs are upped. + * + *----------------------------------------------------------------------- + */ +void +Dir_Concat(path1, path2) + Lst path1; /* Dest */ + Lst path2; /* Source */ +{ + LstNode ln; + Path *p; + + for (ln = Lst_First(path2); ln != NILLNODE; ln = Lst_Succ(ln)) { + p = (Path *)Lst_Datum(ln); + if (Lst_Member(path1, (ClientData)p) == NILLNODE) { + p->refCount += 1; + (void)Lst_AtEnd(path1, (ClientData)p); + } + } +} + +/********** DEBUG INFO **********/ +void +Dir_PrintDirectories() +{ + LstNode ln; + Path *p; + + printf ("#*** Directory Cache:\n"); + printf ("# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n", + hits, misses, nearmisses, bigmisses, + (hits+bigmisses+nearmisses ? + hits * 100 / (hits + bigmisses + nearmisses) : 0)); + printf ("# %-20s referenced\thits\n", "directory"); + if (Lst_Open (openDirectories) == SUCCESS) { + while ((ln = Lst_Next (openDirectories)) != NILLNODE) { + p = (Path *) Lst_Datum (ln); + printf ("# %-20s %10d\t%4d\n", p->name, p->refCount, p->hits); + } + Lst_Close (openDirectories); + } +} + +static int DirPrintDir (p, dummy) + ClientData p; + ClientData dummy; +{ + printf ("%s ", ((Path *) p)->name); + return (dummy ? 0 : 0); +} + +void +Dir_PrintPath (path) + Lst path; +{ + Lst_ForEach (path, DirPrintDir, (ClientData)0); +} diff --git a/bootstrap/bmake/dir.h b/bootstrap/bmake/dir.h new file mode 100644 index 00000000000..000476005fd --- /dev/null +++ b/bootstrap/bmake/dir.h @@ -0,0 +1,72 @@ +/* $NetBSD: dir.h,v 1.1.1.1 2004/03/11 13:04:07 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. + * Copyright (c) 1988, 1989 by Adam de Boor + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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: @(#)dir.h 8.1 (Berkeley) 6/6/93 + */ + +/* dir.h -- + */ + +#ifndef _DIR +#define _DIR + +typedef struct Path { + char *name; /* Name of directory */ + int refCount; /* Number of paths with this directory */ + int hits; /* the number of times a file in this + * directory has been found */ + Hash_Table files; /* Hash table of files in directory */ +} Path; + +void Dir_Init __P((const char *)); +void Dir_End __P((void)); +Boolean Dir_HasWildcards __P((char *)); +void Dir_Expand __P((char *, Lst, Lst)); +char *Dir_FindFile __P((char *, Lst)); +int Dir_MTime __P((GNode *)); +Path *Dir_AddDir __P((Lst, const char *)); +char *Dir_MakeFlags __P((char *, Lst)); +void Dir_ClearPath __P((Lst)); +void Dir_Concat __P((Lst, Lst)); +void Dir_PrintDirectories __P((void)); +void Dir_PrintPath __P((Lst)); +void Dir_Destroy __P((ClientData)); +ClientData Dir_CopyDir __P((ClientData)); + +#endif /* _DIR */ diff --git a/bootstrap/bmake/find_lib.sh b/bootstrap/bmake/find_lib.sh new file mode 100755 index 00000000000..3c2e4af2f25 --- /dev/null +++ b/bootstrap/bmake/find_lib.sh @@ -0,0 +1,13 @@ +: +re=$1; shift + +for lib in $* +do + found=`nm $lib | egrep "$re"` + case "$found" in + "") ;; + *) echo "$lib: $found";; + esac +done + + diff --git a/bootstrap/bmake/for.c b/bootstrap/bmake/for.c new file mode 100644 index 00000000000..c31fe7764dd --- /dev/null +++ b/bootstrap/bmake/for.c @@ -0,0 +1,390 @@ +/* $NetBSD: for.c,v 1.1.1.1 2004/03/11 13:04:07 grant Exp $ */ + +/* + * 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. + */ + +#ifdef MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: for.c,v 1.1.1.1 2004/03/11 13:04:07 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)for.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: for.c,v 1.1.1.1 2004/03/11 13:04:07 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * for.c -- + * Functions to handle loops in a makefile. + * + * Interface: + * For_Eval Evaluate the loop in the passed line. + * For_Run Run accumulated loop + * + */ + +#include +#include +#include "make.h" +#include "hash.h" +#include "dir.h" +#include "buf.h" + +/* + * For statements are of the form: + * + * .for in + * ... + * .endfor + * + * The trick is to look for the matching end inside for for loop + * To do that, we count the current nesting level of the for loops. + * and the .endfor statements, accumulating all the statements between + * the initial .for loop and the matching .endfor; + * then we evaluate the for loop for each variable in the varlist. + * + * Note that any nested fors are just passed through; they get handled + * recursively in For_Eval when we're expanding the enclosing for in + * For_Run. + */ + +static int forLevel = 0; /* Nesting level */ + +/* + * State of a for loop. + */ +typedef struct _For { + Buffer buf; /* Body of loop */ + char **vars; /* Iteration variables */ + int nvars; /* # of iteration vars */ + Lst lst; /* List of items */ +} For; + +static For accumFor; /* Loop being accumulated */ + +static void ForAddVar __P((const char *, size_t)); + + + + +/*- + *----------------------------------------------------------------------- + * ForAddVar -- + * Add an iteration variable to the currently accumulating for. + * + * Results: none + * Side effects: no additional side effects. + *----------------------------------------------------------------------- + */ +static void +ForAddVar(data, len) + const char *data; + size_t len; +{ + Buffer buf; + int varlen; + + buf = Buf_Init(0); + Buf_AddBytes(buf, len, (Byte *) data); + + accumFor.nvars++; + accumFor.vars = erealloc(accumFor.vars, accumFor.nvars*sizeof(char *)); + + accumFor.vars[accumFor.nvars-1] = (char *) Buf_GetAll(buf, &varlen); + + Buf_Destroy(buf, FALSE); +} + +/*- + *----------------------------------------------------------------------- + * For_Eval -- + * Evaluate the for loop in the passed line. The line + * looks like this: + * .for in + * + * Results: + * TRUE: We found a for loop, or we are inside a for loop + * FALSE: We did not find a for loop, or we found the end of the for + * for loop. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +int +For_Eval (line) + char *line; /* Line to parse */ +{ + char *ptr = line, *sub, *in, *wrd; + int level; /* Level at which to report errors. */ + + level = PARSE_FATAL; + + + if (forLevel == 0) { + Buffer buf; + int varlen; + + for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++) + continue; + /* + * If we are not in a for loop quickly determine if the statement is + * a for. + */ + if (ptr[0] != 'f' || ptr[1] != 'o' || ptr[2] != 'r' || + !isspace((unsigned char) ptr[3])) + return FALSE; + ptr += 3; + + /* + * we found a for loop, and now we are going to parse it. + */ + while (*ptr && isspace((unsigned char) *ptr)) + ptr++; + + /* + * Find the "in". + */ + for (in = ptr; *in; in++) { + if (isspace((unsigned char) in[0]) && in[1]== 'i' && + in[2] == 'n' && + (in[3] == '\0' || isspace((unsigned char) in[3]))) + break; + } + if (*in == '\0') { + Parse_Error(level, "missing `in' in for"); + return 0; + } + + /* + * Grab the variables. + */ + accumFor.vars = NULL; + + while (ptr < in) { + wrd = ptr; + while (*ptr && !isspace((unsigned char) *ptr)) + ptr++; + ForAddVar(wrd, ptr - wrd); + while (*ptr && isspace((unsigned char) *ptr)) + ptr++; + } + + if (accumFor.nvars == 0) { + Parse_Error(level, "no iteration variables in for"); + return 0; + } + + /* At this point we should be pointing right at the "in" */ + assert(!memcmp(ptr, "in", 2)); + ptr += 2; + + while (*ptr && isspace((unsigned char) *ptr)) + ptr++; + + /* + * Make a list with the remaining words + */ + accumFor.lst = Lst_Init(FALSE); + buf = Buf_Init(0); + sub = Var_Subst(NULL, ptr, VAR_GLOBAL, FALSE); + +#define ADDWORD() \ + Buf_AddBytes(buf, ptr - wrd, (Byte *) wrd), \ + Buf_AddByte(buf, (Byte) '\0'), \ + Lst_AtFront(accumFor.lst, (ClientData) Buf_GetAll(buf, &varlen)), \ + Buf_Destroy(buf, FALSE) + + for (ptr = sub; *ptr && isspace((unsigned char) *ptr); ptr++) + continue; + + for (wrd = ptr; *ptr; ptr++) + if (isspace((unsigned char) *ptr)) { + ADDWORD(); + buf = Buf_Init(0); + while (*ptr && isspace((unsigned char) *ptr)) + ptr++; + wrd = ptr--; + } + if (DEBUG(FOR)) { + int i; + for (i = 0; i < accumFor.nvars; i++) { + (void) fprintf(stderr, "For: variable %s\n", accumFor.vars[i]); + } + (void) fprintf(stderr, "For: list %s\n", sub); + } + if (ptr - wrd > 0) + ADDWORD(); + else + Buf_Destroy(buf, TRUE); + free((Address) sub); + + accumFor.buf = Buf_Init(0); + forLevel++; + return 1; + } + else if (*ptr == '.') { + + for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++) + continue; + + if (strncmp(ptr, "endfor", 6) == 0 && + (isspace((unsigned char) ptr[6]) || !ptr[6])) { + if (DEBUG(FOR)) + (void) fprintf(stderr, "For: end for %d\n", forLevel); + if (--forLevel < 0) { + Parse_Error (level, "for-less endfor"); + return 0; + } + } + else if (strncmp(ptr, "for", 3) == 0 && + isspace((unsigned char) ptr[3])) { + forLevel++; + if (DEBUG(FOR)) + (void) fprintf(stderr, "For: new loop %d\n", forLevel); + } + } + + if (forLevel != 0) { + Buf_AddBytes(accumFor.buf, strlen(line), (Byte *) line); + Buf_AddByte(accumFor.buf, (Byte) '\n'); + return 1; + } + else { + return 0; + } +} + + +/*- + *----------------------------------------------------------------------- + * For_Run -- + * Run the for loop, imitating the actions of an include file + * + * Results: + * None. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +void +For_Run() +{ + For arg; + LstNode ln; + char **values; + int i, done = 0, len; + char *guy, *orig_guy, *old_guy; + + if (accumFor.buf == NULL || accumFor.vars == NULL || accumFor.lst == NULL) + return; + arg = accumFor; + accumFor.buf = NULL; + accumFor.vars = NULL; + accumFor.nvars = 0; + accumFor.lst = NULL; + + if (Lst_Open(arg.lst) != SUCCESS) + return; + + values = emalloc(arg.nvars * sizeof(char *)); + + while (!done) { + /* + * due to the dumb way this is set up, this loop must run + * backwards. + */ + for (i = arg.nvars - 1; i >= 0; i--) { + ln = Lst_Next(arg.lst); + if (ln == NILLNODE) { + if (i != arg.nvars-1) { + Parse_Error(PARSE_FATAL, + "Not enough words in for substitution list"); + } + done = 1; + break; + } else { + values[i] = (char *) Lst_Datum(ln); + } + } + if (done) + break; + + for (i = 0; i < arg.nvars; i++) { + Var_Set(arg.vars[i], values[i], VAR_GLOBAL, 0); + if (DEBUG(FOR)) + (void) fprintf(stderr, "--- %s = %s\n", arg.vars[i], + values[i]); + } + + /* + * Hack, hack, kludge. + * This is really ugly, but to do it any better way would require + * making major changes to var.c, which I don't want to get into + * yet. There is no mechanism for expanding some variables, only + * for expanding a single variable. That should be corrected, but + * not right away. (XXX) + */ + + guy = (char *) Buf_GetAll(arg.buf, &len); + orig_guy = guy; + for (i = 0; i < arg.nvars; i++) { + old_guy = guy; + guy = Var_Subst(arg.vars[i], guy, VAR_GLOBAL, FALSE); + if (old_guy != orig_guy) + free(old_guy); + } + Parse_FromString(guy); + + for (i = 0; i < arg.nvars; i++) + Var_Delete(arg.vars[i], VAR_GLOBAL); + } + + free(values); + + Lst_Close(arg.lst); + + for (i=0; i +#include +#include +#include + +/* + * getenv -- + * Returns ptr to value associated with name, if any, else NULL. + */ +char * +getenv(name) + const char *name; +{ + int offset; + char *__findenv(); + + return(__findenv(name, &offset)); +} + +/* + * __findenv -- + * Returns pointer to value associated with name, if any, else NULL. + * Sets offset to be the offset of the name/value combination in the + * environmental array, for use by setenv(3) and unsetenv(3). + * Explicitly removes '=' in argument name. + * + * This routine *should* be a static; don't use it. + */ +char * +__findenv(name, offset) + register char *name; + int *offset; +{ + extern char **environ; + register int len; + register char **P, *C; + + for (C = name, len = 0; *C && *C != '='; ++C, ++len); + for (P = environ; *P; ++P) + if (!strncmp(*P, name, len)) + if (*(C = *P + len) == '=') { + *offset = P - environ; + return(++C); + } + return(NULL); +} +#endif diff --git a/bootstrap/bmake/getopt.c b/bootstrap/bmake/getopt.c new file mode 100644 index 00000000000..00b8e39ae27 --- /dev/null +++ b/bootstrap/bmake/getopt.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 1987, 1993, 1994 + * 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 HAVE_CONFIG_H +# include "config.h" +#endif +#if !defined(HAVE_GETOPT) || defined(WANT_GETOPT_LONG) || defined(BROKEN_GETOPT) + +#if defined(LIBC_SCCS) && !defined(lint) +/* static char sccsid[] = "from: @(#)getopt.c 8.2 (Berkeley) 4/2/94"; */ +static char *rcsid = "$Id: getopt.c,v 1.1.1.1 2004/03/11 13:04:07 grant Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include + + +#define BADCH (int)'?' +#define BADARG (int)':' +#define EMSG "" + +int opterr = 1, /* if error message should be printed */ + optind = 1, /* index into parent argv vector */ + optopt = BADCH, /* character checked for validity */ + optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ + +/* + * getopt -- + * Parse argc/argv argument vector. + */ +int +getopt(nargc, nargv, ostr) + int nargc; + char * const *nargv; + const char *ostr; +{ + extern char *__progname; + static char *place = EMSG; /* option letter processing */ + char *oli; /* option letter list index */ + +#ifndef BSD4_4 + if (!__progname) { + if (__progname = strrchr(nargv[0], '/')) + ++__progname; + else + __progname = nargv[0]; + } +#endif + + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc || *(place = nargv[optind]) != '-') { + place = EMSG; + return (-1); + } + if (place[1] && *++place == '-' /* found "--" */ + && !place[1]) { /* and not "--foo" */ + ++optind; + place = EMSG; + return (-1); + } + } /* option letter okay? */ + if ((optopt = (int)*place++) == (int)':' || + !(oli = strchr(ostr, optopt))) { + /* + * if the user didn't specify '-' as an option, + * assume it means -1. + */ + if (optopt == (int)'-') + return (-1); + if (!*place) + ++optind; + if (opterr && *ostr != ':') + (void)fprintf(stderr, + "%s: illegal option -- %c\n", __progname, optopt); + return (BADCH); + } + if (*++oli != ':') { /* don't need argument */ + optarg = NULL; + if (!*place) + ++optind; + } + else { /* need an argument */ + if (*place) /* no white space */ + optarg = place; + else if (nargc <= ++optind) { /* no arg */ + place = EMSG; + if (*ostr == ':') + return (BADARG); + if (opterr) + (void)fprintf(stderr, + "%s: option requires an argument -- %c\n", + __progname, optopt); + return (BADCH); + } + else /* white space */ + optarg = nargv[optind]; + place = EMSG; + ++optind; + } + return (optopt); /* dump back option letter */ +} +#endif +#ifdef MAIN +#ifndef BSD4_4 +char *__progname; +#endif + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int c; + char *opts = argv[1]; + + --argc; + ++argv; + + while ((c = getopt(argc, argv, opts)) != EOF) { + switch (c) { + case '-': + if (optarg) + printf("--%s ", optarg); + break; + case '?': + exit(1); + break; + default: + if (optarg) + printf("-%c %s ", c, optarg); + else + printf("-%c ", c); + break; + } + } + + if (optind < argc) { + printf("-- "); + for (; optind < argc; ++optind) { + printf("%s ", argv[optind]); + } + } + printf("\n"); + exit(0); +} +#endif diff --git a/bootstrap/bmake/hash.c b/bootstrap/bmake/hash.c new file mode 100644 index 00000000000..edcfe86abef --- /dev/null +++ b/bootstrap/bmake/hash.c @@ -0,0 +1,429 @@ +/* $NetBSD: hash.c,v 1.1.1.1 2004/03/11 13:04:07 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. + * Copyright (c) 1988, 1989 by Adam de Boor + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: hash.c,v 1.1.1.1 2004/03/11 13:04:07 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)hash.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: hash.c,v 1.1.1.1 2004/03/11 13:04:07 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +/* hash.c -- + * + * This module contains routines to manipulate a hash table. + * See hash.h for a definition of the structure of the hash + * table. Hash tables grow automatically as the amount of + * information increases. + */ +#include "sprite.h" +#include "make.h" +#include "hash.h" + +/* + * Forward references to local procedures that are used before they're + * defined: + */ + +static void RebuildTable __P((Hash_Table *)); + +/* + * The following defines the ratio of # entries to # buckets + * at which we rebuild the table to make it larger. + */ + +#define rebuildLimit 3 + +/* + *--------------------------------------------------------- + * + * Hash_InitTable -- + * + * This routine just sets up the hash table. + * + * Results: + * None. + * + * Side Effects: + * Memory is allocated for the initial bucket area. + * + *--------------------------------------------------------- + */ + +void +Hash_InitTable(t, numBuckets) + register Hash_Table *t; /* Structure to use to hold table. */ + int numBuckets; /* How many buckets to create for starters. + * This number is rounded up to a power of + * two. If <= 0, a reasonable default is + * chosen. The table will grow in size later + * as needed. */ +{ + register int i; + register struct Hash_Entry **hp; + + /* + * Round up the size to a power of two. + */ + if (numBuckets <= 0) + i = 16; + else { + for (i = 2; i < numBuckets; i <<= 1) + continue; + } + t->numEntries = 0; + t->size = i; + t->mask = i - 1; + t->bucketPtr = hp = (struct Hash_Entry **)emalloc(sizeof(*hp) * i); + while (--i >= 0) + *hp++ = NULL; +} + +/* + *--------------------------------------------------------- + * + * Hash_DeleteTable -- + * + * This routine removes everything from a hash table + * and frees up the memory space it occupied (except for + * the space in the Hash_Table structure). + * + * Results: + * None. + * + * Side Effects: + * Lots of memory is freed up. + * + *--------------------------------------------------------- + */ + +void +Hash_DeleteTable(t) + Hash_Table *t; +{ + register struct Hash_Entry **hp, *h, *nexth = NULL; + register int i; + + for (hp = t->bucketPtr, i = t->size; --i >= 0;) { + for (h = *hp++; h != NULL; h = nexth) { + nexth = h->next; + free((char *)h); + } + } + free((char *)t->bucketPtr); + + /* + * Set up the hash table to cause memory faults on any future access + * attempts until re-initialization. + */ + t->bucketPtr = NULL; +} + +/* + *--------------------------------------------------------- + * + * Hash_FindEntry -- + * + * Searches a hash table for an entry corresponding to key. + * + * Results: + * The return value is a pointer to the entry for key, + * if key was present in the table. If key was not + * present, NULL is returned. + * + * Side Effects: + * None. + * + *--------------------------------------------------------- + */ + +Hash_Entry * +Hash_FindEntry(t, key) + Hash_Table *t; /* Hash table to search. */ + char *key; /* A hash key. */ +{ + register Hash_Entry *e; + register unsigned h; + register char *p; + + for (h = 0, p = key; *p;) + h = (h << 5) - h + *p++; + p = key; + for (e = t->bucketPtr[h & t->mask]; e != NULL; e = e->next) + if (e->namehash == h && strcmp(e->name, p) == 0) + return (e); + return (NULL); +} + +/* + *--------------------------------------------------------- + * + * Hash_CreateEntry -- + * + * Searches a hash table for an entry corresponding to + * key. If no entry is found, then one is created. + * + * Results: + * The return value is a pointer to the entry. If *newPtr + * isn't NULL, then *newPtr is filled in with TRUE if a + * new entry was created, and FALSE if an entry already existed + * with the given key. + * + * Side Effects: + * Memory may be allocated, and the hash buckets may be modified. + *--------------------------------------------------------- + */ + +Hash_Entry * +Hash_CreateEntry(t, key, newPtr) + register Hash_Table *t; /* Hash table to search. */ + char *key; /* A hash key. */ + Boolean *newPtr; /* Filled in with TRUE if new entry created, + * FALSE otherwise. */ +{ + register Hash_Entry *e; + register unsigned h; + register char *p; + int keylen; + struct Hash_Entry **hp; + + /* + * Hash the key. As a side effect, save the length (strlen) of the + * key in case we need to create the entry. + */ + for (h = 0, p = key; *p;) + h = (h << 5) - h + *p++; + keylen = p - key; + p = key; + for (e = t->bucketPtr[h & t->mask]; e != NULL; e = e->next) { + if (e->namehash == h && strcmp(e->name, p) == 0) { + if (newPtr != NULL) + *newPtr = FALSE; + return (e); + } + } + + /* + * The desired entry isn't there. Before allocating a new entry, + * expand the table if necessary (and this changes the resulting + * bucket chain). + */ + if (t->numEntries >= rebuildLimit * t->size) + RebuildTable(t); + e = (Hash_Entry *) emalloc(sizeof(*e) + keylen); + hp = &t->bucketPtr[h & t->mask]; + e->next = *hp; + *hp = e; + e->clientData = NULL; + e->namehash = h; + (void) strcpy(e->name, p); + t->numEntries++; + + if (newPtr != NULL) + *newPtr = TRUE; + return (e); +} + +/* + *--------------------------------------------------------- + * + * Hash_DeleteEntry -- + * + * Delete the given hash table entry and free memory associated with + * it. + * + * Results: + * None. + * + * Side Effects: + * Hash chain that entry lives in is modified and memory is freed. + * + *--------------------------------------------------------- + */ + +void +Hash_DeleteEntry(t, e) + Hash_Table *t; + Hash_Entry *e; +{ + register Hash_Entry **hp, *p; + + if (e == NULL) + return; + for (hp = &t->bucketPtr[e->namehash & t->mask]; + (p = *hp) != NULL; hp = &p->next) { + if (p == e) { + *hp = p->next; + free((char *)p); + t->numEntries--; + return; + } + } + (void) write(2, "bad call to Hash_DeleteEntry\n", 29); + abort(); +} + +/* + *--------------------------------------------------------- + * + * Hash_EnumFirst -- + * This procedure sets things up for a complete search + * of all entries recorded in the hash table. + * + * Results: + * The return value is the address of the first entry in + * the hash table, or NULL if the table is empty. + * + * Side Effects: + * The information in searchPtr is initialized so that successive + * calls to Hash_Next will return successive HashEntry's + * from the table. + * + *--------------------------------------------------------- + */ + +Hash_Entry * +Hash_EnumFirst(t, searchPtr) + Hash_Table *t; /* Table to be searched. */ + register Hash_Search *searchPtr;/* Area in which to keep state + * about search.*/ +{ + searchPtr->tablePtr = t; + searchPtr->nextIndex = 0; + searchPtr->hashEntryPtr = NULL; + return Hash_EnumNext(searchPtr); +} + +/* + *--------------------------------------------------------- + * + * Hash_EnumNext -- + * This procedure returns successive entries in the hash table. + * + * Results: + * The return value is a pointer to the next HashEntry + * in the table, or NULL when the end of the table is + * reached. + * + * Side Effects: + * The information in searchPtr is modified to advance to the + * next entry. + * + *--------------------------------------------------------- + */ + +Hash_Entry * +Hash_EnumNext(searchPtr) + register Hash_Search *searchPtr; /* Area used to keep state about + search. */ +{ + register Hash_Entry *e; + Hash_Table *t = searchPtr->tablePtr; + + /* + * The hashEntryPtr field points to the most recently returned + * entry, or is nil if we are starting up. If not nil, we have + * to start at the next one in the chain. + */ + e = searchPtr->hashEntryPtr; + if (e != NULL) + e = e->next; + /* + * If the chain ran out, or if we are starting up, we need to + * find the next nonempty chain. + */ + while (e == NULL) { + if (searchPtr->nextIndex >= t->size) + return (NULL); + e = t->bucketPtr[searchPtr->nextIndex++]; + } + searchPtr->hashEntryPtr = e; + return (e); +} + +/* + *--------------------------------------------------------- + * + * RebuildTable -- + * This local routine makes a new hash table that + * is larger than the old one. + * + * Results: + * None. + * + * Side Effects: + * The entire hash table is moved, so any bucket numbers + * from the old table are invalid. + * + *--------------------------------------------------------- + */ + +static void +RebuildTable(t) + register Hash_Table *t; +{ + register Hash_Entry *e, *next = NULL, **hp, **xp; + register int i, mask; + register Hash_Entry **oldhp; + int oldsize; + + oldhp = t->bucketPtr; + oldsize = i = t->size; + i <<= 1; + t->size = i; + t->mask = mask = i - 1; + t->bucketPtr = hp = (struct Hash_Entry **) emalloc(sizeof(*hp) * i); + while (--i >= 0) + *hp++ = NULL; + for (hp = oldhp, i = oldsize; --i >= 0;) { + for (e = *hp++; e != NULL; e = next) { + next = e->next; + xp = &t->bucketPtr[e->namehash & mask]; + e->next = *xp; + *xp = e; + } + } + free((char *)oldhp); +} diff --git a/bootstrap/bmake/hash.h b/bootstrap/bmake/hash.h new file mode 100644 index 00000000000..4a66828b2bb --- /dev/null +++ b/bootstrap/bmake/hash.h @@ -0,0 +1,118 @@ +/* $NetBSD: hash.h,v 1.1.1.1 2004/03/11 13:04:07 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. + * Copyright (c) 1988, 1989 by Adam de Boor + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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: @(#)hash.h 8.1 (Berkeley) 6/6/93 + */ + +/* hash.h -- + * + * This file contains definitions used by the hash module, + * which maintains hash tables. + */ + +#ifndef _HASH +#define _HASH + +/* + * The following defines one entry in the hash table. + */ + +typedef struct Hash_Entry { + struct Hash_Entry *next; /* Used to link together all the + * entries associated with the same + * bucket. */ + ClientData clientData; /* Arbitrary piece of data associated + * with key. */ + unsigned namehash; /* hash value of key */ + char name[1]; /* key string */ +} Hash_Entry; + +typedef struct Hash_Table { + struct Hash_Entry **bucketPtr;/* Pointers to Hash_Entry, one + * for each bucket in the table. */ + int size; /* Actual size of array. */ + int numEntries; /* Number of entries in the table. */ + int mask; /* Used to select bits for hashing. */ +} Hash_Table; + +/* + * The following structure is used by the searching routines + * to record where we are in the search. + */ + +typedef struct Hash_Search { + Hash_Table *tablePtr; /* Table being searched. */ + int nextIndex; /* Next bucket to check (after current). */ + Hash_Entry *hashEntryPtr; /* Next entry to check in current bucket. */ +} Hash_Search; + +/* + * Macros. + */ + +/* + * ClientData Hash_GetValue(h) + * Hash_Entry *h; + */ + +#define Hash_GetValue(h) ((h)->clientData) + +/* + * Hash_SetValue(h, val); + * Hash_Entry *h; + * char *val; + */ + +#define Hash_SetValue(h, val) ((h)->clientData = (ClientData) (val)) + +/* + * Hash_Size(n) returns the number of words in an object of n bytes + */ + +#define Hash_Size(n) (((n) + sizeof (int) - 1) / sizeof (int)) + +void Hash_InitTable __P((Hash_Table *, int)); +void Hash_DeleteTable __P((Hash_Table *)); +Hash_Entry *Hash_FindEntry __P((Hash_Table *, char *)); +Hash_Entry *Hash_CreateEntry __P((Hash_Table *, char *, Boolean *)); +void Hash_DeleteEntry __P((Hash_Table *, Hash_Entry *)); +Hash_Entry *Hash_EnumFirst __P((Hash_Table *, Hash_Search *)); +Hash_Entry *Hash_EnumNext __P((Hash_Search *)); + +#endif /* _HASH */ diff --git a/bootstrap/bmake/install-sh b/bootstrap/bmake/install-sh new file mode 100755 index 00000000000..4c57f6b824e --- /dev/null +++ b/bootstrap/bmake/install-sh @@ -0,0 +1,201 @@ +: +# NAME: +# install.sh - portable version of install(1) +# +# SYNOPSIS: +# install [-CNcs] [-f flags] [-i errs] [-o owner] [-g group] [-m mode] file1 file2 ... +# install -d [-i errs] [-o owner] [-g group] [-m mode] directory ... +# +# DESCRIPTION: +# Compatible with BSD install(1). Except that '-c' is always +# true and we always move an already installed target aside as +# this is important on many systems. Recent BSD install(1) +# versions have a '-b' option for this. +# +# +# OPTIONS: +# -b move previous target file aside (always true). +# +# -B "suffix" +# use "suffix" instead of .old for saving existing target. +# +# -c copy rather than move the file into place (always true). +# +# -C compare. Only install if target is missing or +# different. +# +# -N newer. Only install if target is missing or older. +# +# -s strip target +# +# -o "owner" +# make target owned by "owner" +# +# -g "group" +# make target group owned by "group" +# +# -m "mode" +# set permissions to "mode" +# +# -f "flags" +# Pass "flags" onto chflags(1) +# +# -i "errs" +# Ignore errors from steps indicated by "errs" (``s,o,g,m''). +# +# BUGS: +# The '-i' option is to save your sanity when 'bsd.prog.mk' +# insists on haveing a '-o' "owner" option which is doomed to +# fail on many systems. We ignore '-b', '-B' and '-c' options. +# +# AUTHOR: +# Simon J. Gerraty +# + +# RCSid: +# $Id: install-sh,v 1.1.1.1 2004/03/11 13:04:07 grant Exp $ +# +# @(#) Copyright (c) 1993 Simon J. Gerraty +# +# This file is provided in the hope that it will +# be of use. There is absolutely NO WARRANTY. +# Permission to copy, redistribute or otherwise +# use this file is hereby granted provided that +# the above copyright notice and this notice are +# left intact. +# +# Please send copies of changes and bug-fixes to: +# sjg@quick.com.au +# + +set -- `getopt B:bpxCNcsdo:g:m:i:f: $*` + +Mydir=`dirname $0` +[ -s $Mydir/.installrc ] && . $Mydir/.installrc + +owner=: +group=: +mode=: +strip=: +mkdirs= +compare=: +newer=: +chflags=: +LS1= +CP_P= + +while [ $# -gt 1 ] +do + case $1 in + --) shift; break;; + -p) CP_P=-p;; + -x) set -x;; + -B) OLD_EXT=$2; shift;; + -C) compare=Different;; + -N) newer=Newer; + # check if /bin/ls supports -1 + /bin/ls -1 $0 >/dev/null 2>&1 && LS1=1 + ;; + -o) owner="${CHOWN:-chown} $2 "; shift;; + -g) group="${CHGRP:-chgrp} $2 "; shift;; + -m) mode="${CHMOD:-chmod} $2 "; shift;; + -s) strip=${STRIP:-strip};; + -d) mkdirs="mkdir -p";; + -i) ignore_err="$ignore_err$2"; shift;; + -f) chflags="${CHFLAGS:-chflags} $2 "; shift;; + esac + shift +done + +Newer() { + n=`/bin/ls -t$LS1 $* 2>/dev/null | head -1` + [ $1 = $n ] +} + +Different() { + cmp -s $* + [ $? != 0 ] +} + +Err() { + case "$ignore_err" in + *$1*) ;; + *) exit 1;; + esac +} + +Setem() { + # the order is important + if [ ! -d $1 ]; then + $strip $1 || Err s + fi + $group $1 || Err g + $owner $1 || Err o + $mode $1 || Err m + $chflags $1 || Err f + return 0 +} + +# a bug in HP-UX's /bin/sh, means we need to re-set $* +# after any calls to add_path() +args="$*" + +# all this just for chown! +add_path () { [ -d $1 ] && eval ${2:-PATH}="\$${2:-PATH}:$1"; } +add_path /etc +add_path /usr/etc +add_path /sbin +add_path /usr/sbin + +# restore saved $* +set -- $args + +# make directories if needed +# and ensure mode etc are as desired +if [ "$mkdirs" ]; then + for d in $* + do + [ ! -d $d ] && $mkdirs $d + Setem $d + done + exit 0 # that's all we do +fi + +# install files +if [ $# -gt 2 ]; then + dest_dir=yes +elif [ $# -eq 1 ]; then + echo "what should I do with $*?" >&2 + exit 1 +fi + +# get list of files +while [ $# -gt 1 ] +do + files="$files $1" + shift +done +# last one is dest +dest=$1 +shift + + +if [ "$dest_dir" = yes -a ! -d $dest ]; then + echo "no directory $dest" >&2 + exit 1 +fi + +for f in $files +do + b=`basename $f` + if [ -d $dest ]; then + t=$dest/$b + else + t=$dest + fi + $newer $f $t || continue + $compare $f $t || continue + [ -f $t ] && { mv -f $t $t.old || exit 1; } + { cp $CP_P $f $t && Setem $t; } || exit 1 +done +exit 0 diff --git a/bootstrap/bmake/job.c b/bootstrap/bmake/job.c new file mode 100644 index 00000000000..fe958fa3ffe --- /dev/null +++ b/bootstrap/bmake/job.c @@ -0,0 +1,3483 @@ +/* $NetBSD: job.c,v 1.1.1.1 2004/03/11 13:04:08 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. + * Copyright (c) 1988, 1989 by Adam de Boor + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: job.c,v 1.1.1.1 2004/03/11 13:04:08 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)job.c 8.2 (Berkeley) 3/19/94"; +#else +__RCSID("$NetBSD: job.c,v 1.1.1.1 2004/03/11 13:04:08 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +#if !defined(MAKE_BOOTSTRAP) && !defined(lint) +__IDSTRING(rcs_id,"$Id: job.c,v 1.1.1.1 2004/03/11 13:04:08 grant Exp $"); +#endif + +/*- + * job.c -- + * handle the creation etc. of our child processes. + * + * Interface: + * Job_Make Start the creation of the given target. + * + * Job_CatchChildren Check for and handle the termination of any + * children. This must be called reasonably + * frequently to keep the whole make going at + * a decent clip, since job table entries aren't + * removed until their process is caught this way. + * Its single argument is TRUE if the function + * should block waiting for a child to terminate. + * + * Job_CatchOutput Print any output our children have produced. + * Should also be called fairly frequently to + * keep the user informed of what's going on. + * If no output is waiting, it will block for + * a time given by the SEL_* constants, below, + * or until output is ready. + * + * Job_Init Called to intialize this module. in addition, + * any commands attached to the .BEGIN target + * are executed before this function returns. + * Hence, the makefile must have been parsed + * before this function is called. + * + * Job_End Cleanup any memory used. + * + * Job_Empty Return TRUE if the job table is completely + * empty. + * + * Job_ParseShell Given the line following a .SHELL target, parse + * the line as a shell specification. Returns + * FAILURE if the spec was incorrect. + * + * Job_Finish Perform any final processing which needs doing. + * This includes the execution of any commands + * which have been/were attached to the .END + * target. It should only be called when the + * job table is empty. + * + * Job_AbortAll Abort all currently running jobs. It doesn't + * handle output or do anything for the jobs, + * just kills them. It should only be called in + * an emergency, as it were. + * + * Job_CheckCommands Verify that the commands for a target are + * ok. Provide them if necessary and possible. + * + * Job_Touch Update a target without really updating it. + * + * Job_Wait Wait for all currently-running jobs to finish. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include +#include +#include "wait.h" +#include +#include +#include +#include +#include +#include +#ifndef RMT_WILL_WATCH +#if !defined(USE_SELECT) && defined(HAVE_POLL_H) +#include +#else +#ifndef USE_SELECT /* no poll.h */ +# define USE_SELECT +#endif +#if defined(HAVE_SYS_SELECT_H) +# include +#endif +#endif +#endif +#include "make.h" +#include "hash.h" +#include "dir.h" +#include "job.h" +#include "pathnames.h" +#include "trace.h" +#ifdef REMOTE +#include "rmt.h" +# define STATIC +#else +# define STATIC static +#endif + +/* + * error handling variables + */ +static int errors = 0; /* number of errors reported */ +static int aborting = 0; /* why is the make aborting? */ +#define ABORT_ERROR 1 /* Because of an error */ +#define ABORT_INTERRUPT 2 /* Because it was interrupted */ +#define ABORT_WAIT 3 /* Waiting for jobs to finish */ + +/* + * XXX: Avoid SunOS bug... FILENO() is fp->_file, and file + * is a char! So when we go above 127 we turn negative! + */ +#define FILENO(a) ((unsigned) fileno(a)) + +/* + * post-make command processing. The node postCommands is really just the + * .END target but we keep it around to avoid having to search for it + * all the time. + */ +static GNode *postCommands; /* node containing commands to execute when + * everything else is done */ +static int numCommands; /* The number of commands actually printed + * for a target. Should this number be + * 0, no shell will be executed. */ + +/* + * Return values from JobStart. + */ +#define JOB_RUNNING 0 /* Job is running */ +#define JOB_ERROR 1 /* Error in starting the job */ +#define JOB_FINISHED 2 /* The job is already finished */ +#define JOB_STOPPED 3 /* The job is stopped */ + + + +/* + * Descriptions for various shells. + */ +static Shell shells[] = { + /* + * CSH description. The csh can do echo control by playing + * with the setting of the 'echo' shell variable. Sadly, + * however, it is unable to do error control nicely. + */ +{ + "csh", + TRUE, "unset verbose", "set verbose", "unset verbose", 10, + FALSE, "echo \"%s\"\n", "csh -c \"%s || exit 0\"", + "v", "e", +}, + /* + * SH description. Echo control is also possible and, under + * sun UNIX anyway, one can even control error checking. + */ +{ + "sh", + TRUE, "set -", "set -v", "set -", 5, + TRUE, "set -e", "set +e", +#ifdef OLDBOURNESHELL + FALSE, "echo \"%s\"\n", "sh -c '%s || exit 0'\n", +#endif +#ifdef __NetBSD__ + "vq", +#else + "v", +#endif + "e", +}, + /* + * UNKNOWN. + */ +{ + (char *) 0, + FALSE, (char *) 0, (char *) 0, (char *) 0, 0, + FALSE, (char *) 0, (char *) 0, + (char *) 0, (char *) 0, +} +}; +static Shell *commandShell = &shells[DEFSHELL];/* this is the shell to + * which we pass all + * commands in the Makefile. + * It is set by the + * Job_ParseShell function */ +static char *shellPath = NULL, /* full pathname of + * executable image */ + *shellName = NULL, /* last component of shell */ + *shellArgv = NULL; /* Custom shell args */ + + +static int maxJobs; /* The most children we can run at once */ +static int maxLocal; /* The most local ones we can have */ +STATIC int nJobs; /* The number of children currently running */ +STATIC int nLocal; /* The number of local children */ +STATIC Lst jobs; /* The structures that describe them */ +static Boolean wantToken; /* we want a token */ + +/* + * Set of descriptors of pipes connected to + * the output channels of children + */ +#ifndef RMT_WILL_WATCH +#ifdef USE_SELECT +static fd_set outputs; +#else +static struct pollfd *fds = NULL; +static Job **jobfds = NULL; +static int nfds = 0; +static int maxfds = 0; +static void watchfd __P((Job *)); +static void clearfd __P((Job *)); +static int readyfd __P((Job *)); +#define JBSTART 256 +#define JBFACTOR 2 +#endif +#endif + +STATIC GNode *lastNode; /* The node for which output was most recently + * produced. */ +STATIC char *targFmt; /* Format string to use to head output from a + * job when it's not the most-recent job heard + * from */ +static Job tokenWaitJob; /* token wait pseudo-job */ +int job_pipe[2] = { -1, -1 }; /* job server pipes. */ + +#ifdef REMOTE +# define TARG_FMT "--- %s at %s ---\n" /* Default format */ +# define MESSAGE(fp, gn) \ + (void) fprintf(fp, targFmt, gn->name, gn->rem.hname) +#else +# define TARG_FMT "--- %s ---\n" /* Default format */ +# define MESSAGE(fp, gn) \ + (void) fprintf(fp, targFmt, gn->name) +#endif + +/* + * When JobStart attempts to run a job remotely but can't, and isn't allowed + * to run the job locally, or when Job_CatchChildren detects a job that has + * been migrated home, the job is placed on the stoppedJobs queue to be run + * when the next job finishes. + */ +STATIC Lst stoppedJobs; /* Lst of Job structures describing + * jobs that were stopped due to concurrency + * limits or migration home */ + + +#if defined(USE_PGRP) && defined(SYSV) +# define KILL(pid, sig) kill(-(pid), (sig)) +#else +# if defined(USE_PGRP) +# define KILL(pid, sig) killpg((pid), (sig)) +# else +# define KILL(pid, sig) kill((pid), (sig)) +# endif +#endif + +/* + * Grmpf... There is no way to set bits of the wait structure + * anymore with the stupid W*() macros. I liked the union wait + * stuff much more. So, we devise our own macros... This is + * really ugly, use dramamine sparingly. You have been warned. + */ +#ifndef W_STOPCODE +#define W_STOPCODE(sig) (((sig) << 8) | 0177) +#endif +#ifndef W_EXITCODE +#define W_EXITCODE(ret, sig) ((ret << 8) | (sig)) +#endif + +static int JobCondPassSig __P((ClientData, ClientData)); +static void JobPassSig __P((int)); +static void JobIgnoreSig __P((int)); +#ifdef USE_PGRP +static void JobContinueSig __P((int)); +#endif +static int JobCmpPid __P((ClientData, ClientData)); +static int JobPrintCommand __P((ClientData, ClientData)); +static int JobSaveCommand __P((ClientData, ClientData)); +static void JobClose __P((Job *)); +#ifdef REMOTE +static int JobCmpRmtID __P((Job *, int)); +# ifdef RMT_WILL_WATCH +static void JobLocalInput __P((int, Job *)); +# endif +#else +static void JobFinish __P((Job *, WAIT_T)); +static void JobExec __P((Job *, char **)); +#endif +static void JobMakeArgv __P((Job *, char **)); +static void JobRestart __P((Job *)); +static int JobStart __P((GNode *, int, Job *)); +static char *JobOutput __P((Job *, char *, char *, int)); +static void JobDoOutput __P((Job *, Boolean)); +static Shell *JobMatchShell __P((char *)); +static void JobInterrupt __P((int, int)); +static void JobRestartJobs __P((void)); +static void JobTokenAdd __P((void)); + +/*- + *----------------------------------------------------------------------- + * JobCondPassSig -- + * Pass a signal to a job if the job is remote or if USE_PGRP + * is defined. + * + * Results: + * === 0 + * + * Side Effects: + * None, except the job may bite it. + * + *----------------------------------------------------------------------- + */ +static int +JobCondPassSig(jobp, signop) + ClientData jobp; /* Job to biff */ + ClientData signop; /* Signal to send it */ +{ + Job *job = (Job *) jobp; + int signo = *(int *) signop; +#ifdef RMT_WANTS_SIGNALS + if (job->flags & JOB_REMOTE) { + (void) Rmt_Signal(job, signo); + } else { + KILL(job->pid, signo); + } +#else + /* + * Assume that sending the signal to job->pid will signal any remote + * job as well. + */ + if (DEBUG(JOB)) { + (void) fprintf(stdout, + "JobCondPassSig passing signal %d to child %d.\n", + signo, job->pid); + (void) fflush(stdout); + } + KILL(job->pid, signo); +#endif + return 0; +} + +/*- + *----------------------------------------------------------------------- + * JobIgnoreSig -- + * No-op signal handler so we wake up from poll. + * + * Results: + * None. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static void +JobIgnoreSig(signo) + int signo; /* The signal number we've received */ +{ + /* + * Do nothing. The mere fact that we've been called will cause + * poll/select in Job_CatchOutput() to return early. + */ +} + + +#ifdef USE_PGRP +/*- + *----------------------------------------------------------------------- + * JobContinueSig -- + * Resume all stopped jobs. + * + * Results: + * None. + * + * Side Effects: + * Jobs start running again. + * + *----------------------------------------------------------------------- + */ +static void +JobContinueSig(signo) + int signo; /* The signal number we've received */ +{ + if (signal(SIGTSTP, SIG_IGN) != SIG_IGN) { + (void) signal(SIGTSTP, JobPassSig); + } + JobRestartJobs(); +} +#endif + +/*- + *----------------------------------------------------------------------- + * JobPassSig -- + * Pass a signal on to all remote jobs and to all local jobs if + * USE_PGRP is defined, then die ourselves. + * + * Results: + * None. + * + * Side Effects: + * We die by the same signal. + * + *----------------------------------------------------------------------- + */ +static void +JobPassSig(signo) + int signo; /* The signal number we've received */ +{ + sigset_t nmask, omask; + struct sigaction act; + int sigcont; + + if (DEBUG(JOB)) { + (void) fprintf(stdout, "JobPassSig(%d) called.\n", signo); + (void) fflush(stdout); + } + Lst_ForEach(jobs, JobCondPassSig, (ClientData) &signo); + + /* + * Deal with proper cleanup based on the signal received. We only run + * the .INTERRUPT target if the signal was in fact an interrupt. The other + * three termination signals are more of a "get out *now*" command. + */ + if (signo == SIGINT) { + JobInterrupt(TRUE, signo); + } else if ((signo == SIGHUP) || (signo == SIGTERM) || (signo == SIGQUIT)) { + JobInterrupt(FALSE, signo); + } + + /* + * Leave gracefully if SIGQUIT, rather than core dumping. + */ + if (signo == SIGQUIT) { + Finish(0); + } + + if (signo == SIGTSTP) { + Job_CatchChildren(FALSE); + } + /* + * Send ourselves the signal now we've given the message to everyone else. + * Note we block everything else possible while we're getting the signal. + * This ensures that all our jobs get continued when we wake up before + * we take any other signal. + */ + sigfillset(&nmask); + sigprocmask(SIG_SETMASK, &nmask, &omask); + act.sa_handler = SIG_DFL; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + sigaction(signo, &act, NULL); + + if (DEBUG(JOB)) { + (void) fprintf(stdout, + "JobPassSig passing signal %d to self.\n", signo); + (void) fflush(stdout); + } + + (void) kill(getpid(), signo); + if (signo != SIGTSTP) { + sigcont = SIGCONT; + Lst_ForEach(jobs, JobCondPassSig, (ClientData) &sigcont); + } + + (void) sigprocmask(SIG_SETMASK, &omask, NULL); + sigprocmask(SIG_SETMASK, &omask, NULL); + if (signo != SIGCONT && signo != SIGTSTP) { + act.sa_handler = JobPassSig; + sigaction(sigcont, &act, NULL); + } +} + +/*- + *----------------------------------------------------------------------- + * JobCmpPid -- + * Compare the pid of the job with the given pid and return 0 if they + * are equal. This function is called from Job_CatchChildren via + * Lst_Find to find the job descriptor of the finished job. + * + * Results: + * 0 if the pid's match + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +static int +JobCmpPid(job, pid) + ClientData job; /* job to examine */ + ClientData pid; /* process id desired */ +{ + return *(int *) pid - ((Job *) job)->pid; +} + +#ifdef REMOTE +/*- + *----------------------------------------------------------------------- + * JobCmpRmtID -- + * Compare the rmtID of the job with the given rmtID and return 0 if they + * are equal. + * + * Results: + * 0 if the rmtID's match + * + * Side Effects: + * None. + *----------------------------------------------------------------------- + */ +static int +JobCmpRmtID(job, rmtID) + ClientData job; /* job to examine */ + ClientData rmtID; /* remote id desired */ +{ + return(*(int *) rmtID - *(int *) job->rmtID); +} +#endif + +/*- + *----------------------------------------------------------------------- + * JobPrintCommand -- + * Put out another command for the given job. If the command starts + * with an @ or a - we process it specially. In the former case, + * so long as the -s and -n flags weren't given to make, we stick + * a shell-specific echoOff command in the script. In the latter, + * we ignore errors for the entire job, unless the shell has error + * control. + * If the command is just "..." we take all future commands for this + * job to be commands to be executed once the entire graph has been + * made and return non-zero to signal that the end of the commands + * was reached. These commands are later attached to the postCommands + * node and executed by Job_End when all things are done. + * This function is called from JobStart via Lst_ForEach. + * + * Results: + * Always 0, unless the command was "..." + * + * Side Effects: + * If the command begins with a '-' and the shell has no error control, + * the JOB_IGNERR flag is set in the job descriptor. + * If the command is "..." and we're not ignoring such things, + * tailCmds is set to the successor node of the cmd. + * numCommands is incremented if the command is actually printed. + *----------------------------------------------------------------------- + */ +static int +JobPrintCommand(cmdp, jobp) + ClientData cmdp; /* command string to print */ + ClientData jobp; /* job for which to print it */ +{ + Boolean noSpecials; /* true if we shouldn't worry about + * inserting special commands into + * the input stream. */ + Boolean shutUp = FALSE; /* true if we put a no echo command + * into the command file */ + Boolean errOff = FALSE; /* true if we turned error checking + * off before printing the command + * and need to turn it back on */ + char *cmdTemplate; /* Template to use when printing the + * command */ + char *cmdStart; /* Start of expanded command */ + LstNode cmdNode; /* Node for replacing the command */ + char *cmd = (char *) cmdp; + Job *job = (Job *) jobp; + char *cp; + + noSpecials = NoExecute(job->node); + + if (strcmp(cmd, "...") == 0) { + job->node->type |= OP_SAVE_CMDS; + if ((job->flags & JOB_IGNDOTS) == 0) { + job->tailCmds = Lst_Succ(Lst_Member(job->node->commands, + (ClientData)cmd)); + return 1; + } + return 0; + } + +#define DBPRINTF(fmt, arg) if (DEBUG(JOB)) { \ + (void) fprintf(stdout, fmt, arg); \ + (void) fflush(stdout); \ + } \ + (void) fprintf(job->cmdFILE, fmt, arg); \ + (void) fflush(job->cmdFILE); + + numCommands += 1; + + /* + * For debugging, we replace each command with the result of expanding + * the variables in the command. + */ + cmdNode = Lst_Member(job->node->commands, (ClientData)cmd); + cmdStart = cmd = Var_Subst(NULL, cmd, job->node, FALSE); + Lst_Replace(cmdNode, (ClientData)cmdStart); + + cmdTemplate = "%s\n"; + + /* + * Check for leading @' and -'s to control echoing and error checking. + */ + while (*cmd == '@' || *cmd == '-') { + if (*cmd == '@') { + shutUp = TRUE; + } else { + errOff = TRUE; + } + cmd++; + } + + while (isspace((unsigned char) *cmd)) + cmd++; + + if (shutUp) { + if (!(job->flags & JOB_SILENT) && !noSpecials && + commandShell->hasEchoCtl) { + DBPRINTF("%s\n", commandShell->echoOff); + } else { + shutUp = FALSE; + } + } + + if (errOff) { + if ( !(job->flags & JOB_IGNERR) && !noSpecials) { + if (commandShell->hasErrCtl) { + /* + * we don't want the error-control commands showing + * up either, so we turn off echoing while executing + * them. We could put another field in the shell + * structure to tell JobDoOutput to look for this + * string too, but why make it any more complex than + * it already is? + */ + if (!(job->flags & JOB_SILENT) && !shutUp && + commandShell->hasEchoCtl) { + DBPRINTF("%s\n", commandShell->echoOff); + DBPRINTF("%s\n", commandShell->ignErr); + DBPRINTF("%s\n", commandShell->echoOn); + } else { + DBPRINTF("%s\n", commandShell->ignErr); + } + } else if (commandShell->ignErr && + (*commandShell->ignErr != '\0')) + { + /* + * The shell has no error control, so we need to be + * weird to get it to ignore any errors from the command. + * If echoing is turned on, we turn it off and use the + * errCheck template to echo the command. Leave echoing + * off so the user doesn't see the weirdness we go through + * to ignore errors. Set cmdTemplate to use the weirdness + * instead of the simple "%s\n" template. + */ + if (!(job->flags & JOB_SILENT) && !shutUp && + commandShell->hasEchoCtl) { + DBPRINTF("%s\n", commandShell->echoOff); + DBPRINTF(commandShell->errCheck, cmd); + shutUp = TRUE; + } + cmdTemplate = commandShell->ignErr; + /* + * The error ignoration (hee hee) is already taken care + * of by the ignErr template, so pretend error checking + * is still on. + */ + errOff = FALSE; + } else { + errOff = FALSE; + } + } else { + errOff = FALSE; + } + } + + if (DEBUG(SHELL) && strcmp(shellName, "sh") == 0 && + (job->flags & JOB_TRACED) == 0) { + DBPRINTF("set -%s\n", "x"); + job->flags |= JOB_TRACED; + } + + if ((cp = Check_Cwd_Cmd(cmd)) != NULL) { + DBPRINTF("test -d %s && ", cp); + DBPRINTF("cd %s; ", cp); + } + DBPRINTF(cmdTemplate, cmd); + free(cmdStart); + + if (errOff) { + /* + * If echoing is already off, there's no point in issuing the + * echoOff command. Otherwise we issue it and pretend it was on + * for the whole command... + */ + if (!shutUp && !(job->flags & JOB_SILENT) && commandShell->hasEchoCtl){ + DBPRINTF("%s\n", commandShell->echoOff); + shutUp = TRUE; + } + DBPRINTF("%s\n", commandShell->errCheck); + } + if (shutUp) { + DBPRINTF("%s\n", commandShell->echoOn); + } + return 0; +} + +/*- + *----------------------------------------------------------------------- + * JobSaveCommand -- + * Save a command to be executed when everything else is done. + * Callback function for JobFinish... + * + * Results: + * Always returns 0 + * + * Side Effects: + * The command is tacked onto the end of postCommands's commands list. + * + *----------------------------------------------------------------------- + */ +static int +JobSaveCommand(cmd, gn) + ClientData cmd; + ClientData gn; +{ + cmd = (ClientData) Var_Subst(NULL, (char *) cmd, (GNode *) gn, FALSE); + (void) Lst_AtEnd(postCommands->commands, cmd); + return(0); +} + + +/*- + *----------------------------------------------------------------------- + * JobClose -- + * Called to close both input and output pipes when a job is finished. + * + * Results: + * Nada + * + * Side Effects: + * The file descriptors associated with the job are closed. + * + *----------------------------------------------------------------------- + */ +static void +JobClose(job) + Job *job; +{ + if (usePipes && (job->flags & JOB_FIRST)) { +#ifdef RMT_WILL_WATCH + Rmt_Ignore(job->inPipe); +#else +#ifdef USE_SELECT + FD_CLR(job->inPipe, &outputs); +#else + clearfd(job); +#endif +#endif + if (job->outPipe != job->inPipe) { + (void) close(job->outPipe); + } + JobDoOutput(job, TRUE); + (void) close(job->inPipe); + } else { + (void) close(job->outFd); + JobDoOutput(job, TRUE); + } +} + +/*- + *----------------------------------------------------------------------- + * JobFinish -- + * Do final processing for the given job including updating + * parents and starting new jobs as available/necessary. Note + * that we pay no attention to the JOB_IGNERR flag here. + * This is because when we're called because of a noexecute flag + * or something, jstat.w_status is 0 and when called from + * Job_CatchChildren, the status is zeroed if it s/b ignored. + * + * Results: + * None + * + * Side Effects: + * Some nodes may be put on the toBeMade queue. + * Final commands for the job are placed on postCommands. + * + * If we got an error and are aborting (aborting == ABORT_ERROR) and + * the job list is now empty, we are done for the day. + * If we recognized an error (errors !=0), we set the aborting flag + * to ABORT_ERROR so no more jobs will be started. + *----------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static void +JobFinish (job, status) + Job *job; /* job to finish */ + WAIT_T status; /* sub-why job went away */ +{ + Boolean done; + + if ((WIFEXITED(status) && + (((WEXITSTATUS(status) != 0) && !(job->flags & JOB_IGNERR)))) || + WIFSIGNALED(status)) + { + /* + * If it exited non-zero and either we're doing things our + * way or we're not ignoring errors, the job is finished. + * Similarly, if the shell died because of a signal + * the job is also finished. In these + * cases, finish out the job's output before printing the exit + * status... + */ +#ifdef REMOTE + KILL(job->pid, SIGCONT); +#endif + JobClose(job); + if (job->cmdFILE != NULL && job->cmdFILE != stdout) { + (void) fclose(job->cmdFILE); + job->cmdFILE = NULL; + } + done = TRUE; +#ifdef REMOTE + if (job->flags & JOB_REMOTE) + Rmt_Done(job->rmtID, job->node); +#endif + } else if (WIFEXITED(status)) { + /* + * Deal with ignored errors in -B mode. We need to print a message + * telling of the ignored error as well as setting status.w_status + * to 0 so the next command gets run. To do this, we set done to be + * TRUE if in -B mode and the job exited non-zero. + */ + done = WEXITSTATUS(status) != 0; + /* + * Old comment said: "Note we don't + * want to close down any of the streams until we know we're at the + * end." + * But we do. Otherwise when are we going to print the rest of the + * stuff? + */ + JobClose(job); +#ifdef REMOTE + if (job->flags & JOB_REMOTE) + Rmt_Done(job->rmtID, job->node); +#endif /* REMOTE */ + } else { + /* + * No need to close things down or anything. + */ + done = FALSE; + } + + if (done || + WIFSTOPPED(status) || + (WIFSIGNALED(status) && (WTERMSIG(status) == SIGCONT))) + { + FILE *out; + + if (compatMake && !usePipes && (job->flags & JOB_IGNERR)) { + /* + * If output is going to a file and this job is ignoring + * errors, arrange to have the exit status sent to the + * output file as well. + */ + out = fdopen(job->outFd, "w"); + if (out == NULL) + Punt("Cannot fdopen"); + } else { + out = stdout; + } + + if (WIFEXITED(status)) { + if (DEBUG(JOB)) { + (void) fprintf(stdout, "Process %d exited.\n", job->pid); + (void) fflush(stdout); + } + if (WEXITSTATUS(status) != 0) { + if (usePipes && job->node != lastNode) { + MESSAGE(out, job->node); + lastNode = job->node; + } + fprintf (out, "*** Error code %d%s\n", + WEXITSTATUS(status), + (job->flags & JOB_IGNERR) ? " (ignored)" : ""); + + if (job->flags & JOB_IGNERR) { + WAIT_STATUS(status) = 0; + } + } else if (DEBUG(JOB)) { + if (usePipes && job->node != lastNode) { + MESSAGE(out, job->node); + lastNode = job->node; + } + (void) fprintf(out, "*** Completed successfully\n"); + } + } else if (WIFSTOPPED(status) && WSTOPSIG(status) != SIGCONT) { + if (DEBUG(JOB)) { + (void) fprintf(stdout, "Process %d stopped.\n", job->pid); + (void) fflush(stdout); + } + if (usePipes && job->node != lastNode) { + MESSAGE(out, job->node); + lastNode = job->node; + } + if (!(job->flags & JOB_REMIGRATE)) { + switch (WSTOPSIG(status)) { + case SIGTSTP: + (void) fprintf(out, "*** Suspended\n"); + break; + case SIGSTOP: + (void) fprintf(out, "*** Stopped\n"); + break; + default: + (void) fprintf(out, "*** Stopped -- signal %d\n", + WSTOPSIG(status)); + } + } + job->flags |= JOB_RESUME; + (void)Lst_AtEnd(stoppedJobs, (ClientData)job); +#ifdef REMOTE + if (job->flags & JOB_REMIGRATE) + JobRestart(job); +#endif + (void) fflush(out); + return; + } else if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGCONT) { + /* + * If the beastie has continued, shift the Job from the stopped + * list to the running one (or re-stop it if concurrency is + * exceeded) and go and get another child. + */ + if (job->flags & (JOB_RESUME|JOB_REMIGRATE|JOB_RESTART)) { + if (usePipes && job->node != lastNode) { + MESSAGE(out, job->node); + lastNode = job->node; + } + (void) fprintf(out, "*** Continued\n"); + } + if (!(job->flags & JOB_CONTINUING)) { + if (DEBUG(JOB)) { + (void) fprintf(stdout, + "Warning: process %d was not continuing.\n", + job->pid); + (void) fflush(stdout); + } +#ifdef notdef + /* + * We don't really want to restart a job from scratch just + * because it continued, especially not without killing the + * continuing process! That's why this is ifdef'ed out. + * FD - 9/17/90 + */ + JobRestart(job); +#endif + } + job->flags &= ~JOB_CONTINUING; + Lst_AtEnd(jobs, (ClientData)job); + nJobs += 1; + if (!(job->flags & JOB_REMOTE)) { + if (DEBUG(JOB)) { + (void) fprintf(stdout, + "Process %d is continuing locally.\n", + job->pid); + (void) fflush(stdout); + } + nLocal += 1; + } + (void) fflush(out); + return; + } else { + if (usePipes && job->node != lastNode) { + MESSAGE(out, job->node); + lastNode = job->node; + } + (void) fprintf(out, "*** Signal %d\n", WTERMSIG(status)); + } + + (void) fflush(out); + } + + /* + * Now handle the -B-mode stuff. If the beast still isn't finished, + * try and restart the job on the next command. If JobStart says it's + * ok, it's ok. If there's an error, this puppy is done. + */ + if (compatMake && (WIFEXITED(status) && + !Lst_IsAtEnd(job->node->commands))) { + switch (JobStart(job->node, job->flags & JOB_IGNDOTS, job)) { + case JOB_RUNNING: + done = FALSE; + break; + case JOB_ERROR: + done = TRUE; + WSET_EXITCODE(status, 1, 0); + break; + case JOB_FINISHED: + /* + * If we got back a JOB_FINISHED code, JobStart has already + * called Make_Update and freed the job descriptor. We set + * done to false here to avoid fake cycles and double frees. + * JobStart needs to do the update so we can proceed up the + * graph when given the -n flag.. + */ + done = FALSE; + break; + } + } else { + done = TRUE; + } + + if (done) { + Trace_Log(JOBEND, job); + if (!compatMake && !(job->flags & JOB_SPECIAL)) { + if ((WAIT_STATUS(status) != 0) || + (aborting == ABORT_ERROR) || + (aborting == ABORT_INTERRUPT)) + Job_TokenReturn(); + } + + } + + if (done && + (aborting != ABORT_ERROR) && + (aborting != ABORT_INTERRUPT) && + (WAIT_STATUS(status) == 0)) + { + /* + * As long as we aren't aborting and the job didn't return a non-zero + * status that we shouldn't ignore, we call Make_Update to update + * the parents. In addition, any saved commands for the node are placed + * on the .END target. + */ + if (job->tailCmds != NILLNODE) { + Lst_ForEachFrom(job->node->commands, job->tailCmds, + JobSaveCommand, + (ClientData)job->node); + } + job->node->made = MADE; + if (!(job->flags & JOB_SPECIAL)) + Job_TokenReturn(); + Make_Update(job->node); + free((Address)job); + } else if (WAIT_STATUS(status)) { + errors += 1; + free((Address)job); + } + JobRestartJobs(); + + /* + * Set aborting if any error. + */ + if (errors && !keepgoing && (aborting != ABORT_INTERRUPT)) { + /* + * If we found any errors in this batch of children and the -k flag + * wasn't given, we set the aborting flag so no more jobs get + * started. + */ + aborting = ABORT_ERROR; + } + + if ((aborting == ABORT_ERROR) && Job_Empty()) { + /* + * If we are aborting and the job table is now empty, we finish. + */ + Finish(errors); + } +} + +/*- + *----------------------------------------------------------------------- + * Job_Touch -- + * Touch the given target. Called by JobStart when the -t flag was + * given + * + * Results: + * None + * + * Side Effects: + * The data modification of the file is changed. In addition, if the + * file did not exist, it is created. + *----------------------------------------------------------------------- + */ +void +Job_Touch(gn, silent) + GNode *gn; /* the node of the file to touch */ + Boolean silent; /* TRUE if should not print messages */ +{ + int streamID; /* ID of stream opened to do the touch */ + struct utimbuf times; /* Times for utime() call */ + + if (gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC|OP_OPTIONAL|OP_PHONY)) { + /* + * .JOIN, .USE, .ZEROTIME and .OPTIONAL targets are "virtual" targets + * and, as such, shouldn't really be created. + */ + return; + } + + if (!silent || NoExecute(gn)) { + (void) fprintf(stdout, "touch %s\n", gn->name); + (void) fflush(stdout); + } + + if (NoExecute(gn)) { + return; + } + + if (gn->type & OP_ARCHV) { + Arch_Touch(gn); + } else if (gn->type & OP_LIB) { + Arch_TouchLib(gn); + } else { + char *file = gn->path ? gn->path : gn->name; + + times.actime = times.modtime = now; + if (utime(file, ×) < 0){ + streamID = open(file, O_RDWR | O_CREAT, 0666); + + if (streamID >= 0) { + char c; + + /* + * Read and write a byte to the file to change the + * modification time, then close the file. + */ + if (read(streamID, &c, 1) == 1) { + (void) lseek(streamID, (off_t)0, SEEK_SET); + (void) write(streamID, &c, 1); + } + + (void) close(streamID); + } else { + (void) fprintf(stdout, "*** couldn't touch %s: %s", + file, strerror(errno)); + (void) fflush(stdout); + } + } + } +} + +/*- + *----------------------------------------------------------------------- + * Job_CheckCommands -- + * Make sure the given node has all the commands it needs. + * + * Results: + * TRUE if the commands list is/was ok. + * + * Side Effects: + * The node will have commands from the .DEFAULT rule added to it + * if it needs them. + *----------------------------------------------------------------------- + */ +Boolean +Job_CheckCommands(gn, abortProc) + GNode *gn; /* The target whose commands need + * verifying */ + void (*abortProc) __P((char *, ...)); + /* Function to abort with message */ +{ + if (OP_NOP(gn->type) && Lst_IsEmpty(gn->commands) && + (gn->type & OP_LIB) == 0) { + /* + * No commands. Look for .DEFAULT rule from which we might infer + * commands + */ + if ((DEFAULT != NILGNODE) && !Lst_IsEmpty(DEFAULT->commands)) { + char *p1; + /* + * Make only looks for a .DEFAULT if the node was never the + * target of an operator, so that's what we do too. If + * a .DEFAULT was given, we substitute its commands for gn's + * commands and set the IMPSRC variable to be the target's name + * The DEFAULT node acts like a transformation rule, in that + * gn also inherits any attributes or sources attached to + * .DEFAULT itself. + */ + Make_HandleUse(DEFAULT, gn); + Var_Set(IMPSRC, Var_Value(TARGET, gn, &p1), gn, 0); + if (p1) + free(p1); + } else if (Dir_MTime(gn) == 0) { + /* + * The node wasn't the target of an operator we have no .DEFAULT + * rule to go on and the target doesn't already exist. There's + * nothing more we can do for this branch. If the -k flag wasn't + * given, we stop in our tracks, otherwise we just don't update + * this node's parents so they never get examined. + */ + static const char msg[] = ": don't know how to make"; + + if (gn->type & OP_OPTIONAL) { + (void) fprintf(stdout, "%s%s %s(ignored)\n", progname, + msg, gn->name); + (void) fflush(stdout); + } else if (keepgoing) { + (void) fprintf(stdout, "%s%s %s(continuing)\n", progname, + msg, gn->name); + (void) fflush(stdout); + return FALSE; + } else { + (*abortProc)("%s%s %s. Stop", progname, msg, gn->name); + return FALSE; + } + } + } + return TRUE; +} +#ifdef RMT_WILL_WATCH +/*- + *----------------------------------------------------------------------- + * JobLocalInput -- + * Handle a pipe becoming readable. Callback function for Rmt_Watch + * + * Results: + * None + * + * Side Effects: + * JobDoOutput is called. + * + *----------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static void +JobLocalInput(stream, job) + int stream; /* Stream that's ready (ignored) */ + Job *job; /* Job to which the stream belongs */ +{ + JobDoOutput(job, FALSE); +} +#endif /* RMT_WILL_WATCH */ + +/*- + *----------------------------------------------------------------------- + * JobExec -- + * Execute the shell for the given job. Called from JobStart and + * JobRestart. + * + * Results: + * None. + * + * Side Effects: + * A shell is executed, outputs is altered and the Job structure added + * to the job table. + * + *----------------------------------------------------------------------- + */ +static void +JobExec(job, argv) + Job *job; /* Job to execute */ + char **argv; +{ + int cpid; /* ID of new child */ + + job->flags &= ~JOB_TRACED; + + if (DEBUG(JOB)) { + int i; + + (void) fprintf(stdout, "Running %s %sly\n", job->node->name, + job->flags&JOB_REMOTE?"remote":"local"); + (void) fprintf(stdout, "\tCommand: "); + for (i = 0; argv[i] != NULL; i++) { + (void) fprintf(stdout, "%s ", argv[i]); + } + (void) fprintf(stdout, "\n"); + (void) fflush(stdout); + } + + /* + * Some jobs produce no output and it's disconcerting to have + * no feedback of their running (since they produce no output, the + * banner with their name in it never appears). This is an attempt to + * provide that feedback, even if nothing follows it. + */ + if ((lastNode != job->node) && (job->flags & JOB_FIRST) && + !(job->flags & JOB_SILENT)) { + MESSAGE(stdout, job->node); + lastNode = job->node; + } + +#ifdef RMT_NO_EXEC + if (job->flags & JOB_REMOTE) { + goto jobExecFinish; + } +#endif /* RMT_NO_EXEC */ + + if ((cpid = vfork()) == -1) { + Punt("Cannot vfork: %s", strerror(errno)); + } else if (cpid == 0) { + + /* + * Must duplicate the input stream down to the child's input and + * reset it to the beginning (again). Since the stream was marked + * close-on-exec, we must clear that bit in the new input. + */ + if (dup2(FILENO(job->cmdFILE), 0) == -1) + Punt("Cannot dup2: %s", strerror(errno)); + (void) fcntl(0, F_SETFD, 0); + (void) lseek(0, (off_t)0, SEEK_SET); + + if (job->node->type & OP_MAKE) { + /* + * Pass job token pipe to submakes. + */ + fcntl(job_pipe[0], F_SETFD, 0); + fcntl(job_pipe[1], F_SETFD, 0); + } + + if (usePipes) { + /* + * Set up the child's output to be routed through the pipe + * we've created for it. + */ + if (dup2(job->outPipe, 1) == -1) + Punt("Cannot dup2: %s", strerror(errno)); + } else { + /* + * We're capturing output in a file, so we duplicate the + * descriptor to the temporary file into the standard + * output. + */ + if (dup2(job->outFd, 1) == -1) + Punt("Cannot dup2: %s", strerror(errno)); + } + /* + * The output channels are marked close on exec. This bit was + * duplicated by the dup2 (on some systems), so we have to clear + * it before routing the shell's error output to the same place as + * its standard output. + */ + (void) fcntl(1, F_SETFD, 0); + if (dup2(1, 2) == -1) + Punt("Cannot dup2: %s", strerror(errno)); + +#ifdef USE_PGRP + /* + * We want to switch the child into a different process family so + * we can kill it and all its descendants in one fell swoop, + * by killing its process family, but not commit suicide. + */ +# if defined(SYSV) + (void) setsid(); +# else + (void) setpgid(0, getpid()); +# endif +#endif /* USE_PGRP */ + +#ifdef REMOTE + if (job->flags & JOB_REMOTE) { + Rmt_Exec(shellPath, argv, FALSE); + } else +#endif /* REMOTE */ + { + (void) execv(shellPath, argv); + execError(shellPath); + } + _exit(1); + } else { +#ifdef REMOTE + sigset_t nmask, omask; + sigemptyset(&nmask); + sigaddset(&nmask, SIGCHLD); + sigprocmask(SIG_BLOCK, &nmask, &omask); +#endif + job->pid = cpid; + + Trace_Log(JOBSTART, job); + + if (usePipes && (job->flags & JOB_FIRST)) { + /* + * The first time a job is run for a node, we set the current + * position in the buffer to the beginning and mark another + * stream to watch in the outputs mask + */ + job->curPos = 0; + +#ifdef RMT_WILL_WATCH + Rmt_Watch(job->inPipe, JobLocalInput, job); +#else +#ifdef USE_SELECT + FD_SET(job->inPipe, &outputs); +#else + watchfd(job); +#endif +#endif /* RMT_WILL_WATCH */ + } + + if (job->flags & JOB_REMOTE) { +#ifndef REMOTE + job->rmtID = 0; +#else + job->rmtID = Rmt_LastID(job->pid); +#endif /* REMOTE */ + } else { + nLocal += 1; + /* + * XXX: Used to not happen if REMOTE. Why? + */ + if (job->cmdFILE != NULL && job->cmdFILE != stdout) { + (void) fclose(job->cmdFILE); + job->cmdFILE = NULL; + } + } +#ifdef REMOTE + sigprocmask(SIG_SETMASK, &omask, NULL); +#endif + } + +#ifdef RMT_NO_EXEC +jobExecFinish: +#endif + /* + * Now the job is actually running, add it to the table. + */ + nJobs += 1; + (void) Lst_AtEnd(jobs, (ClientData)job); +} + +/*- + *----------------------------------------------------------------------- + * JobMakeArgv -- + * Create the argv needed to execute the shell for a given job. + * + * + * Results: + * + * Side Effects: + * + *----------------------------------------------------------------------- + */ +static void +JobMakeArgv(job, argv) + Job *job; + char **argv; +{ + int argc; + static char args[10]; /* For merged arguments */ + + argv[0] = shellName; + argc = 1; + + if ((commandShell->exit && (*commandShell->exit != '-')) || + (commandShell->echo && (*commandShell->echo != '-'))) + { + /* + * At least one of the flags doesn't have a minus before it, so + * merge them together. Have to do this because the *(&(@*#*&#$# + * Bourne shell thinks its second argument is a file to source. + * Grrrr. Note the ten-character limitation on the combined arguments. + */ + (void)snprintf(args, sizeof(args), "-%s%s", + ((job->flags & JOB_IGNERR) ? "" : + (commandShell->exit ? commandShell->exit : "")), + ((job->flags & JOB_SILENT) ? "" : + (commandShell->echo ? commandShell->echo : ""))); + + if (args[1]) { + argv[argc] = args; + argc++; + } + } else { + if (!(job->flags & JOB_IGNERR) && commandShell->exit) { + argv[argc] = commandShell->exit; + argc++; + } + if (!(job->flags & JOB_SILENT) && commandShell->echo) { + argv[argc] = commandShell->echo; + argc++; + } + } + argv[argc] = NULL; +} + +/*- + *----------------------------------------------------------------------- + * JobRestart -- + * Restart a job that stopped for some reason. + * + * Results: + * None. + * + *----------------------------------------------------------------------- + */ +static void +JobRestart(job) + Job *job; /* Job to restart */ +{ +#ifdef REMOTE + int host; +#endif + + if (job->flags & JOB_REMIGRATE) { + if ( +#ifdef REMOTE + verboseRemigrates || +#endif + DEBUG(JOB)) { + (void) fprintf(stdout, "*** remigrating %x(%s)\n", + job->pid, job->node->name); + (void) fflush(stdout); + } + +#ifdef REMOTE + if (!Rmt_ReExport(job->pid, job->node, &host)) { + if (verboseRemigrates || DEBUG(JOB)) { + (void) fprintf(stdout, "*** couldn't migrate...\n"); + (void) fflush(stdout); + } +#endif + if (nLocal != maxLocal) { + /* + * Job cannot be remigrated, but there's room on the local + * machine, so resume the job and note that another + * local job has started. + */ + if ( +#ifdef REMOTE + verboseRemigrates || +#endif + DEBUG(JOB)) { + (void) fprintf(stdout, "*** resuming on local machine\n"); + (void) fflush(stdout); + } + KILL(job->pid, SIGCONT); + nLocal +=1; +#ifdef REMOTE + job->flags &= ~(JOB_REMIGRATE|JOB_RESUME|JOB_REMOTE); + job->flags |= JOB_CONTINUING; +#else + job->flags &= ~(JOB_REMIGRATE|JOB_RESUME); +#endif + } else { + /* + * Job cannot be restarted. Mark the table as full and + * place the job back on the list of stopped jobs. + */ + if ( +#ifdef REMOTE + verboseRemigrates || +#endif + DEBUG(JOB)) { + (void) fprintf(stdout, "*** holding\n"); + (void) fflush(stdout); + } + (void)Lst_AtFront(stoppedJobs, (ClientData)job); + return; + } +#ifdef REMOTE + } else { + /* + * Clear out the remigrate and resume flags. Set the continuing + * flag so we know later on that the process isn't exiting just + * because of a signal. + */ + job->flags &= ~(JOB_REMIGRATE|JOB_RESUME); + job->flags |= JOB_CONTINUING; + job->rmtID = host; + } +#endif + + (void)Lst_AtEnd(jobs, (ClientData)job); + nJobs += 1; + } else if (job->flags & JOB_RESTART) { + /* + * Set up the control arguments to the shell. This is based on the + * flags set earlier for this job. If the JOB_IGNERR flag is clear, + * the 'exit' flag of the commandShell is used to cause it to exit + * upon receiving an error. If the JOB_SILENT flag is clear, the + * 'echo' flag of the commandShell is used to get it to start echoing + * as soon as it starts processing commands. + */ + char *argv[10]; + + JobMakeArgv(job, argv); + + if (DEBUG(JOB)) { + (void) fprintf(stdout, "Restarting %s...", job->node->name); + (void) fflush(stdout); + } +#ifdef REMOTE + if ((job->node->type&OP_NOEXPORT) || + (nLocal < maxLocal && runLocalFirst) +# ifdef RMT_NO_EXEC + || !Rmt_Export(shellPath, argv, job) +# else + || !Rmt_Begin(shellPath, argv, job->node) +# endif +#endif + { + if (((nLocal >= maxLocal) && !(job->flags & JOB_SPECIAL))) { + /* + * Can't be exported and not allowed to run locally -- put it + * back on the hold queue and mark the table full + */ + if (DEBUG(JOB)) { + (void) fprintf(stdout, "holding\n"); + (void) fflush(stdout); + } + (void)Lst_AtFront(stoppedJobs, (ClientData)job); + return; + } else { + /* + * Job may be run locally. + */ + if (DEBUG(JOB)) { + (void) fprintf(stdout, "running locally\n"); + (void) fflush(stdout); + } + job->flags &= ~JOB_REMOTE; + } + } +#ifdef REMOTE + else { + /* + * Can be exported. Hooray! + */ + if (DEBUG(JOB)) { + (void) fprintf(stdout, "exporting\n"); + (void) fflush(stdout); + } + job->flags |= JOB_REMOTE; + } +#endif + JobExec(job, argv); + } else { + /* + * The job has stopped and needs to be restarted. Why it stopped, + * we don't know... + */ + if (DEBUG(JOB)) { + (void) fprintf(stdout, "Resuming %s...", job->node->name); + (void) fflush(stdout); + } + if (((job->flags & JOB_REMOTE) || + (nLocal < maxLocal) || +#ifdef REMOTE + (((job->flags & JOB_SPECIAL) && + (job->node->type & OP_NOEXPORT)) && + (maxLocal == 0))) && +#else + ((job->flags & JOB_SPECIAL) && + (maxLocal == 0))) && +#endif + (nJobs != maxJobs)) + { + /* + * If the job is remote, it's ok to resume it as long as the + * maximum concurrency won't be exceeded. If it's local and + * we haven't reached the local concurrency limit already (or the + * job must be run locally and maxLocal is 0), it's also ok to + * resume it. + */ + Boolean error; + WAIT_T status; + +#ifdef RMT_WANTS_SIGNALS + if (job->flags & JOB_REMOTE) { + error = !Rmt_Signal(job, SIGCONT); + } else +#endif /* RMT_WANTS_SIGNALS */ + error = (KILL(job->pid, SIGCONT) != 0); + + if (!error) { + /* + * Make sure the user knows we've continued the beast and + * actually put the thing in the job table. + */ + job->flags |= JOB_CONTINUING; + WSET_STOPCODE(status, SIGCONT); + JobFinish(job, status); + + job->flags &= ~(JOB_RESUME|JOB_CONTINUING); + if (DEBUG(JOB)) { + (void) fprintf(stdout, "done\n"); + (void) fflush(stdout); + } + } else { + Error("couldn't resume %s: %s", + job->node->name, strerror(errno)); + WSET_EXITCODE(status, 1, 0); + JobFinish(job, status); + } + } else { + /* + * Job cannot be restarted. Mark the table as full and + * place the job back on the list of stopped jobs. + */ + if (DEBUG(JOB)) { + (void) fprintf(stdout, "table full\n"); + (void) fflush(stdout); + } + (void) Lst_AtFront(stoppedJobs, (ClientData)job); + } + } +} + +/*- + *----------------------------------------------------------------------- + * JobStart -- + * Start a target-creation process going for the target described + * by the graph node gn. + * + * Results: + * JOB_ERROR if there was an error in the commands, JOB_FINISHED + * if there isn't actually anything left to do for the job and + * JOB_RUNNING if the job has been started. + * + * Side Effects: + * A new Job node is created and added to the list of running + * jobs. PMake is forked and a child shell created. + *----------------------------------------------------------------------- + */ +static int +JobStart(gn, flags, previous) + GNode *gn; /* target to create */ + int flags; /* flags for the job to override normal ones. + * e.g. JOB_SPECIAL or JOB_IGNDOTS */ + Job *previous; /* The previous Job structure for this node, + * if any. */ +{ + register Job *job; /* new job descriptor */ + char *argv[10]; /* Argument vector to shell */ + Boolean cmdsOK; /* true if the nodes commands were all right */ + Boolean local; /* Set true if the job was run locally */ + Boolean noExec; /* Set true if we decide not to run the job */ + int tfd; /* File descriptor to the temp file */ + + if (previous != NULL) { + previous->flags &= ~(JOB_FIRST|JOB_IGNERR|JOB_SILENT|JOB_REMOTE); + job = previous; + } else { + job = (Job *) emalloc(sizeof(Job)); + if (job == NULL) { + Punt("JobStart out of memory"); + } + flags |= JOB_FIRST; + } + + job->node = gn; + job->tailCmds = NILLNODE; + + /* + * Set the initial value of the flags for this job based on the global + * ones and the node's attributes... Any flags supplied by the caller + * are also added to the field. + */ + job->flags = 0; + if (Targ_Ignore(gn)) { + job->flags |= JOB_IGNERR; + } + if (Targ_Silent(gn)) { + job->flags |= JOB_SILENT; + } + job->flags |= flags; + + /* + * Check the commands now so any attributes from .DEFAULT have a chance + * to migrate to the node + */ + if (!compatMake && job->flags & JOB_FIRST) { + cmdsOK = Job_CheckCommands(gn, Error); + } else { + cmdsOK = TRUE; + } + +#ifndef RMT_WILL_WATCH +#ifndef USE_SELECT + job->inPollfd = NULL; +#endif +#endif + /* + * If the -n flag wasn't given, we open up OUR (not the child's) + * temporary file to stuff commands in it. The thing is rd/wr so we don't + * need to reopen it to feed it to the shell. If the -n flag *was* given, + * we just set the file to be stdout. Cute, huh? + */ + if (((gn->type & OP_MAKE) && !(noRecursiveExecute)) || + (!noExecute && !touchFlag)) { + /* + * tfile is the name of a file into which all shell commands are + * put. It is used over by removing it before the child shell is + * executed. The XXXXXX in the string are replaced by the pid of + * the make process in a 6-character field with leading zeroes. + */ + char tfile[sizeof(TMPPAT)]; + /* + * We're serious here, but if the commands were bogus, we're + * also dead... + */ + if (!cmdsOK) { + DieHorribly(); + } + + (void)strcpy(tfile, TMPPAT); + if ((tfd = mkstemp(tfile)) == -1) + Punt("Could not create temporary file %s", strerror(errno)); + (void) eunlink(tfile); + + job->cmdFILE = fdopen(tfd, "w+"); + if (job->cmdFILE == NULL) { + Punt("Could not fdopen %s", tfile); + } + (void) fcntl(FILENO(job->cmdFILE), F_SETFD, 1); + /* + * Send the commands to the command file, flush all its buffers then + * rewind and remove the thing. + */ + noExec = FALSE; + + /* + * used to be backwards; replace when start doing multiple commands + * per shell. + */ + if (compatMake) { + /* + * Be compatible: If this is the first time for this node, + * verify its commands are ok and open the commands list for + * sequential access by later invocations of JobStart. + * Once that is done, we take the next command off the list + * and print it to the command file. If the command was an + * ellipsis, note that there's nothing more to execute. + */ + if ((job->flags&JOB_FIRST) && (Lst_Open(gn->commands) != SUCCESS)){ + cmdsOK = FALSE; + } else { + LstNode ln = Lst_Next(gn->commands); + + if ((ln == NILLNODE) || + JobPrintCommand((ClientData) Lst_Datum(ln), + (ClientData) job)) + { + noExec = TRUE; + Lst_Close(gn->commands); + } + if (noExec && !(job->flags & JOB_FIRST)) { + /* + * If we're not going to execute anything, the job + * is done and we need to close down the various + * file descriptors we've opened for output, then + * call JobDoOutput to catch the final characters or + * send the file to the screen... Note that the i/o streams + * are only open if this isn't the first job. + * Note also that this could not be done in + * Job_CatchChildren b/c it wasn't clear if there were + * more commands to execute or not... + */ + JobClose(job); + } + } + } else { + /* + * We can do all the commands at once. hooray for sanity + */ + numCommands = 0; + Lst_ForEach(gn->commands, JobPrintCommand, (ClientData)job); + + /* + * If we didn't print out any commands to the shell script, + * there's not much point in executing the shell, is there? + */ + if (numCommands == 0) { + noExec = TRUE; + } + } + } else if (NoExecute(gn)) { + /* + * Not executing anything -- just print all the commands to stdout + * in one fell swoop. This will still set up job->tailCmds correctly. + */ + if (lastNode != gn) { + MESSAGE(stdout, gn); + lastNode = gn; + } + job->cmdFILE = stdout; + /* + * Only print the commands if they're ok, but don't die if they're + * not -- just let the user know they're bad and keep going. It + * doesn't do any harm in this case and may do some good. + */ + if (cmdsOK) { + Lst_ForEach(gn->commands, JobPrintCommand, (ClientData)job); + } + /* + * Don't execute the shell, thank you. + */ + noExec = TRUE; + } else { + /* + * Just touch the target and note that no shell should be executed. + * Set cmdFILE to stdout to make life easier. Check the commands, too, + * but don't die if they're no good -- it does no harm to keep working + * up the graph. + */ + job->cmdFILE = stdout; + Job_Touch(gn, job->flags&JOB_SILENT); + noExec = TRUE; + } + + /* + * If we're not supposed to execute a shell, don't. + */ + if (noExec) { + /* + * Unlink and close the command file if we opened one + */ + if (job->cmdFILE != stdout) { + if (job->cmdFILE != NULL) { + (void) fclose(job->cmdFILE); + job->cmdFILE = NULL; + } + } else { + (void) fflush(stdout); + } + + /* + * We only want to work our way up the graph if we aren't here because + * the commands for the job were no good. + */ + if (cmdsOK) { + if (aborting == 0) { + if (job->tailCmds != NILLNODE) { + Lst_ForEachFrom(job->node->commands, job->tailCmds, + JobSaveCommand, + (ClientData)job->node); + } + if (!(job->flags & JOB_SPECIAL)) + Job_TokenReturn(); + Make_Update(job->node); + } + free((Address)job); + return(JOB_FINISHED); + } else { + free((Address)job); + return(JOB_ERROR); + } + } else { + (void) fflush(job->cmdFILE); + } + + /* + * Set up the control arguments to the shell. This is based on the flags + * set earlier for this job. + */ + JobMakeArgv(job, argv); + + /* + * If we're using pipes to catch output, create the pipe by which we'll + * get the shell's output. If we're using files, print out that we're + * starting a job and then set up its temporary-file name. + */ + if (!compatMake || (job->flags & JOB_FIRST)) { + if (usePipes) { + int fd[2]; + if (pipe(fd) == -1) + Punt("Cannot create pipe: %s", strerror(errno)); + job->inPipe = fd[0]; +#ifdef USE_SELECT + if (job->inPipe >= FD_SETSIZE) + Punt("Ran out of fd_set slots; " + "recompile with a larger FD_SETSIZE."); +#endif + job->outPipe = fd[1]; + (void) fcntl(job->inPipe, F_SETFD, 1); + (void) fcntl(job->outPipe, F_SETFD, 1); + } else { + (void) fprintf(stdout, "Remaking `%s'\n", gn->name); + (void) fflush(stdout); + (void) strcpy(job->outFile, TMPPAT); + job->outFd = mkstemp(job->outFile); + (void) fcntl(job->outFd, F_SETFD, 1); + } + } + +#ifdef REMOTE + if (!(gn->type & OP_NOEXPORT) && !(runLocalFirst && nLocal < maxLocal)) { +#ifdef RMT_NO_EXEC + local = !Rmt_Export(shellPath, argv, job); +#else + local = !Rmt_Begin(shellPath, argv, job->node); +#endif /* RMT_NO_EXEC */ + if (!local) { + job->flags |= JOB_REMOTE; + } + } else +#endif + local = TRUE; + + if (local && (((nLocal >= maxLocal) && + !(job->flags & JOB_SPECIAL) && +#ifdef REMOTE + (!(gn->type & OP_NOEXPORT) || (maxLocal != 0)) +#else + (maxLocal != 0) +#endif + ))) + { + /* + * The job can only be run locally, but we've hit the limit of + * local concurrency, so put the job on hold until some other job + * finishes. Note that the special jobs (.BEGIN, .INTERRUPT and .END) + * may be run locally even when the local limit has been reached + * (e.g. when maxLocal == 0), though they will be exported if at + * all possible. In addition, any target marked with .NOEXPORT will + * be run locally if maxLocal is 0. + */ + job->flags |= JOB_RESTART; + (void) Lst_AtEnd(stoppedJobs, (ClientData)job); + } else { + JobExec(job, argv); + } + return(JOB_RUNNING); +} + +static char * +JobOutput(job, cp, endp, msg) + register Job *job; + register char *cp, *endp; + int msg; +{ + register char *ecp; + + if (commandShell->noPrint) { + ecp = Str_FindSubstring(cp, commandShell->noPrint); + while (ecp != NULL) { + if (cp != ecp) { + *ecp = '\0'; + if (msg && job->node != lastNode) { + MESSAGE(stdout, job->node); + lastNode = job->node; + } + /* + * The only way there wouldn't be a newline after + * this line is if it were the last in the buffer. + * however, since the non-printable comes after it, + * there must be a newline, so we don't print one. + */ + (void) fprintf(stdout, "%s", cp); + (void) fflush(stdout); + } + cp = ecp + commandShell->noPLen; + if (cp != endp) { + /* + * Still more to print, look again after skipping + * the whitespace following the non-printable + * command.... + */ + cp++; + while (*cp == ' ' || *cp == '\t' || *cp == '\n') { + cp++; + } + ecp = Str_FindSubstring(cp, commandShell->noPrint); + } else { + return cp; + } + } + } + return cp; +} + +/*- + *----------------------------------------------------------------------- + * JobDoOutput -- + * This function is called at different times depending on + * whether the user has specified that output is to be collected + * via pipes or temporary files. In the former case, we are called + * whenever there is something to read on the pipe. We collect more + * output from the given job and store it in the job's outBuf. If + * this makes up a line, we print it tagged by the job's identifier, + * as necessary. + * If output has been collected in a temporary file, we open the + * file and read it line by line, transfering it to our own + * output channel until the file is empty. At which point we + * remove the temporary file. + * In both cases, however, we keep our figurative eye out for the + * 'noPrint' line for the shell from which the output came. If + * we recognize a line, we don't print it. If the command is not + * alone on the line (the character after it is not \0 or \n), we + * do print whatever follows it. + * + * Results: + * None + * + * Side Effects: + * curPos may be shifted as may the contents of outBuf. + *----------------------------------------------------------------------- + */ +STATIC void +JobDoOutput(job, finish) + register Job *job; /* the job whose output needs printing */ + Boolean finish; /* TRUE if this is the last time we'll be + * called for this job */ +{ + Boolean gotNL = FALSE; /* true if got a newline */ + Boolean fbuf; /* true if our buffer filled up */ + register int nr; /* number of bytes read */ + register int i; /* auxiliary index into outBuf */ + register int max; /* limit for i (end of current data) */ + int nRead; /* (Temporary) number of bytes read */ + + FILE *oFILE; /* Stream pointer to shell's output file */ + char inLine[132]; + + + if (usePipes) { + /* + * Read as many bytes as will fit in the buffer. + */ +end_loop: + gotNL = FALSE; + fbuf = FALSE; + + nRead = read(job->inPipe, &job->outBuf[job->curPos], + JOB_BUFSIZE - job->curPos); + if (nRead < 0) { + if (DEBUG(JOB)) { + perror("JobDoOutput(piperead)"); + } + nr = 0; + } else { + nr = nRead; + } + + /* + * If we hit the end-of-file (the job is dead), we must flush its + * remaining output, so pretend we read a newline if there's any + * output remaining in the buffer. + * Also clear the 'finish' flag so we stop looping. + */ + if ((nr == 0) && (job->curPos != 0)) { + job->outBuf[job->curPos] = '\n'; + nr = 1; + finish = FALSE; + } else if (nr == 0) { + finish = FALSE; + } + + /* + * Look for the last newline in the bytes we just got. If there is + * one, break out of the loop with 'i' as its index and gotNL set + * TRUE. + */ + max = job->curPos + nr; + for (i = job->curPos + nr - 1; i >= job->curPos; i--) { + if (job->outBuf[i] == '\n') { + gotNL = TRUE; + break; + } else if (job->outBuf[i] == '\0') { + /* + * Why? + */ + job->outBuf[i] = ' '; + } + } + + if (!gotNL) { + job->curPos += nr; + if (job->curPos == JOB_BUFSIZE) { + /* + * If we've run out of buffer space, we have no choice + * but to print the stuff. sigh. + */ + fbuf = TRUE; + i = job->curPos; + } + } + if (gotNL || fbuf) { + /* + * Need to send the output to the screen. Null terminate it + * first, overwriting the newline character if there was one. + * So long as the line isn't one we should filter (according + * to the shell description), we print the line, preceded + * by a target banner if this target isn't the same as the + * one for which we last printed something. + * The rest of the data in the buffer are then shifted down + * to the start of the buffer and curPos is set accordingly. + */ + job->outBuf[i] = '\0'; + if (i >= job->curPos) { + char *cp; + + cp = JobOutput(job, job->outBuf, &job->outBuf[i], FALSE); + + /* + * There's still more in that thar buffer. This time, though, + * we know there's no newline at the end, so we add one of + * our own free will. + */ + if (*cp != '\0') { + if (job->node != lastNode) { + MESSAGE(stdout, job->node); + lastNode = job->node; + } + (void) fprintf(stdout, "%s%s", cp, gotNL ? "\n" : ""); + (void) fflush(stdout); + } + } + if (i < max - 1) { + /* shift the remaining characters down */ + (void) memcpy(job->outBuf, &job->outBuf[i + 1], max - (i + 1)); + job->curPos = max - (i + 1); + + } else { + /* + * We have written everything out, so we just start over + * from the start of the buffer. No copying. No nothing. + */ + job->curPos = 0; + } + } + if (finish) { + /* + * If the finish flag is true, we must loop until we hit + * end-of-file on the pipe. This is guaranteed to happen + * eventually since the other end of the pipe is now closed + * (we closed it explicitly and the child has exited). When + * we do get an EOF, finish will be set FALSE and we'll fall + * through and out. + */ + goto end_loop; + } + } else { + /* + * We've been called to retrieve the output of the job from the + * temporary file where it's been squirreled away. This consists of + * opening the file, reading the output line by line, being sure not + * to print the noPrint line for the shell we used, then close and + * remove the temporary file. Very simple. + * + * Change to read in blocks and do FindSubString type things as for + * pipes? That would allow for "@echo -n..." + */ + oFILE = fopen(job->outFile, "r"); + if (oFILE != NULL) { + (void) fprintf(stdout, "Results of making %s:\n", job->node->name); + (void) fflush(stdout); + while (fgets(inLine, sizeof(inLine), oFILE) != NULL) { + register char *cp, *endp, *oendp; + + cp = inLine; + oendp = endp = inLine + strlen(inLine); + if (endp[-1] == '\n') { + *--endp = '\0'; + } + cp = JobOutput(job, inLine, endp, FALSE); + + /* + * There's still more in that thar buffer. This time, though, + * we know there's no newline at the end, so we add one of + * our own free will. + */ + (void) fprintf(stdout, "%s", cp); + (void) fflush(stdout); + if (endp != oendp) { + (void) fprintf(stdout, "\n"); + (void) fflush(stdout); + } + } + (void) fclose(oFILE); + (void) eunlink(job->outFile); + } else { + Punt("Cannot open `%s'", job->outFile); + } + } +} + +/*- + *----------------------------------------------------------------------- + * Job_CatchChildren -- + * Handle the exit of a child. Called from Make_Make. + * + * Results: + * none. + * + * Side Effects: + * The job descriptor is removed from the list of children. + * + * Notes: + * We do waits, blocking or not, according to the wisdom of our + * caller, until there are no more children to report. For each + * job, call JobFinish to finish things off. This will take care of + * putting jobs on the stoppedJobs queue. + * + *----------------------------------------------------------------------- + */ +void +Job_CatchChildren(block) + Boolean block; /* TRUE if should block on the wait. */ +{ + int pid; /* pid of dead child */ + register Job *job; /* job descriptor for dead child */ + LstNode jnode; /* list element for finding job */ + WAIT_T status; /* Exit/termination status */ + + /* + * Don't even bother if we know there's no one around. + */ + if (nLocal == 0) { + return; + } + + while ((pid = waitpid((pid_t) -1, &status, + (block?0:WNOHANG)|WUNTRACED)) > 0) + { + if (DEBUG(JOB)) { + (void) fprintf(stdout, "Process %d exited or stopped %x.\n", pid, + WAIT_STATUS(status)); + (void) fflush(stdout); + } + + + jnode = Lst_Find(jobs, (ClientData)&pid, JobCmpPid); + + if (jnode == NILLNODE) { + if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGCONT)) { + jnode = Lst_Find(stoppedJobs, (ClientData) &pid, JobCmpPid); + if (jnode == NILLNODE) { + Error("Resumed child (%d) not in table", pid); + continue; + } + job = (Job *)Lst_Datum(jnode); + (void) Lst_Remove(stoppedJobs, jnode); + } else { + Error("Child (%d) not in table?", pid); + continue; + } + } else { + job = (Job *) Lst_Datum(jnode); + (void) Lst_Remove(jobs, jnode); + nJobs -= 1; +#ifdef REMOTE + if (!(job->flags & JOB_REMOTE)) { + if (DEBUG(JOB)) { + (void) fprintf(stdout, + "Job queue has one fewer local process.\n"); + (void) fflush(stdout); + } + nLocal -= 1; + } +#else + nLocal -= 1; +#endif + } + + JobFinish(job, status); + } +} + +/*- + *----------------------------------------------------------------------- + * Job_CatchOutput -- + * Catch the output from our children, if we're using + * pipes do so. Otherwise just block time until we get a + * signal (most likely a SIGCHLD) since there's no point in + * just spinning when there's nothing to do and the reaping + * of a child can wait for a while. + * + * Results: + * None + * + * Side Effects: + * Output is read from pipes if we're piping. + * ----------------------------------------------------------------------- + */ +void +Job_CatchOutput() +{ + int nready; + register LstNode ln; + register Job *job; +#ifdef RMT_WILL_WATCH + int pnJobs; /* Previous nJobs */ +#endif + + (void) fflush(stdout); + Job_TokenFlush(); +#ifdef RMT_WILL_WATCH + pnJobs = nJobs; + + /* + * It is possible for us to be called with nJobs equal to 0. This happens + * if all the jobs finish and a job that is stopped cannot be run + * locally (eg if maxLocal is 0) and cannot be exported. The job will + * be placed back on the stoppedJobs queue, Job_Empty() will return false, + * Make_Run will call us again when there's nothing for which to wait. + * nJobs never changes, so we loop forever. Hence the check. It could + * be argued that we should sleep for a bit so as not to swamp the + * exportation system with requests. Perhaps we should. + * + * NOTE: IT IS THE RESPONSIBILITY OF Rmt_Wait TO CALL Job_CatchChildren + * IN A TIMELY FASHION TO CATCH ANY LOCALLY RUNNING JOBS THAT EXIT. + * It may use the variable nLocal to determine if it needs to call + * Job_CatchChildren (if nLocal is 0, there's nothing for which to + * wait...) + */ + while (nJobs != 0 && pnJobs == nJobs) { + Rmt_Wait(); + } +#else + if (usePipes) { +#ifdef USE_SELECT + struct timeval timeout; + fd_set readfds; + + readfds = outputs; + timeout.tv_sec = SEL_SEC; + timeout.tv_usec = SEL_USEC; + + if ((nready = select(FD_SETSIZE, &readfds, (fd_set *) 0, + (fd_set *) 0, &timeout)) <= 0) + return; +#else + if ((nready = poll((wantToken ? fds : (fds + 1)), + (wantToken ? nfds : (nfds - 1)), POLL_MSEC)) <= 0) + return; +#endif + else { + if (Lst_Open(jobs) == FAILURE) { + Punt("Cannot open job table"); + } + while (nready && (ln = Lst_Next(jobs)) != NILLNODE) { + job = (Job *) Lst_Datum(ln); +#ifdef USE_SELECT + if (FD_ISSET(job->inPipe, &readfds)) +#else + if (readyfd(job)) +#endif + { + JobDoOutput(job, FALSE); + nready -= 1; + } + + } + Lst_Close(jobs); + } + } +#endif /* RMT_WILL_WATCH */ +} + +/*- + *----------------------------------------------------------------------- + * Job_Make -- + * Start the creation of a target. Basically a front-end for + * JobStart used by the Make module. + * + * Results: + * None. + * + * Side Effects: + * Another job is started. + * + *----------------------------------------------------------------------- + */ +void +Job_Make(gn) + GNode *gn; +{ + (void) JobStart(gn, 0, NULL); +} + +/*- + *----------------------------------------------------------------------- + * Job_Init -- + * Initialize the process module + * + * Results: + * none + * + * Side Effects: + * lists and counters are initialized + *----------------------------------------------------------------------- + */ +void +Job_Init(maxproc, maxlocal) + int maxproc; /* the greatest number of jobs which may be + * running at one time */ + int maxlocal; /* the greatest number of local jobs which may + * be running at once. */ +{ + GNode *begin; /* node for commands to do at the very start */ + + jobs = Lst_Init(FALSE); + stoppedJobs = Lst_Init(FALSE); + maxJobs = maxproc; + maxLocal = maxlocal; + nJobs = 0; + nLocal = 0; + wantToken = FALSE; + + aborting = 0; + errors = 0; + + lastNode = NILGNODE; + + if (maxJobs == 1 +#ifdef REMOTE + || noMessages +#endif + ) { + /* + * If only one job can run at a time, there's no need for a banner, + * is there? + */ + targFmt = ""; + } else { + targFmt = TARG_FMT; + } + + if (shellPath == NULL) { + /* + * The user didn't specify a shell to use, so we are using the + * default one... Both the absolute path and the last component + * must be set. The last component is taken from the 'name' field + * of the default shell description pointed-to by commandShell. + * All default shells are located in _PATH_DEFSHELLDIR. + */ + shellName = commandShell->name; + shellPath = str_concat(_PATH_DEFSHELLDIR, shellName, STR_ADDSLASH); + } + + if (commandShell->exit == NULL) { + commandShell->exit = ""; + } + if (commandShell->echo == NULL) { + commandShell->echo = ""; + } + + /* + * Catch the four signals that POSIX specifies if they aren't ignored. + * JobPassSig will take care of calling JobInterrupt if appropriate. + */ + if (signal(SIGINT, SIG_IGN) != SIG_IGN) { + (void) signal(SIGINT, JobPassSig); + } + if (signal(SIGHUP, SIG_IGN) != SIG_IGN) { + (void) signal(SIGHUP, JobPassSig); + } + if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) { + (void) signal(SIGQUIT, JobPassSig); + } + if (signal(SIGTERM, SIG_IGN) != SIG_IGN) { + (void) signal(SIGTERM, JobPassSig); + } + /* + * Install a NOOP SIGCHLD handler so we are woken up if we're blocked. + */ + signal(SIGCHLD, JobIgnoreSig); + + /* + * There are additional signals that need to be caught and passed if + * either the export system wants to be told directly of signals or if + * we're giving each job its own process group (since then it won't get + * signals from the terminal driver as we own the terminal) + */ +#if defined(RMT_WANTS_SIGNALS) || defined(USE_PGRP) + if (signal(SIGTSTP, SIG_IGN) != SIG_IGN) { + (void) signal(SIGTSTP, JobPassSig); + } + if (signal(SIGTTOU, SIG_IGN) != SIG_IGN) { + (void) signal(SIGTTOU, JobPassSig); + } + if (signal(SIGTTIN, SIG_IGN) != SIG_IGN) { + (void) signal(SIGTTIN, JobPassSig); + } + if (signal(SIGWINCH, SIG_IGN) != SIG_IGN) { + (void) signal(SIGWINCH, JobPassSig); + } + if (signal(SIGCONT, SIG_IGN) != SIG_IGN) { + (void) signal(SIGCONT, JobContinueSig); + } +#endif + + begin = Targ_FindNode(".BEGIN", TARG_NOCREATE); + + if (begin != NILGNODE) { + JobStart(begin, JOB_SPECIAL, (Job *)0); + while (nJobs) { + Job_CatchOutput(); +#ifndef RMT_WILL_WATCH + Job_CatchChildren(!usePipes); +#endif /* RMT_WILL_WATCH */ + } + } + postCommands = Targ_FindNode(".END", TARG_CREATE); +} + +/*- + *----------------------------------------------------------------------- + * Job_Empty -- + * See if the job table is empty. Because the local concurrency may + * be set to 0, it is possible for the job table to become empty, + * while the list of stoppedJobs remains non-empty. In such a case, + * we want to restart as many jobs as we can. + * + * Results: + * TRUE if it is. FALSE if it ain't. + * + * Side Effects: + * None. + * + * ----------------------------------------------------------------------- + */ +Boolean +Job_Empty() +{ + if (nJobs == 0) { + if (!Lst_IsEmpty(stoppedJobs) && !aborting) { + /* + * The job table is obviously not full if it has no jobs in + * it...Try and restart the stopped jobs. + */ + JobRestartJobs(); + return(FALSE); + } else { + return(TRUE); + } + } else { + return(FALSE); + } +} + +/*- + *----------------------------------------------------------------------- + * JobMatchShell -- + * Find a matching shell in 'shells' given its final component. + * + * Results: + * A pointer to the Shell structure. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static Shell * +JobMatchShell(name) + char *name; /* Final component of shell path */ +{ + register Shell *sh; /* Pointer into shells table */ + Shell *match; /* Longest-matching shell */ + register char *cp1, + *cp2; + char *eoname; + + eoname = name + strlen(name); + + match = NULL; + + for (sh = shells; sh->name != NULL; sh++) { + for (cp1 = eoname - strlen(sh->name), cp2 = sh->name; + *cp1 != '\0' && *cp1 == *cp2; + cp1++, cp2++) { + continue; + } + if (*cp1 != *cp2) { + continue; + } else if (match == NULL || strlen(match->name) < strlen(sh->name)) { + match = sh; + } + } + return(match == NULL ? sh : match); +} + +/*- + *----------------------------------------------------------------------- + * Job_ParseShell -- + * Parse a shell specification and set up commandShell, shellPath + * and shellName appropriately. + * + * Results: + * FAILURE if the specification was incorrect. + * + * Side Effects: + * commandShell points to a Shell structure (either predefined or + * created from the shell spec), shellPath is the full path of the + * shell described by commandShell, while shellName is just the + * final component of shellPath. + * + * Notes: + * A shell specification consists of a .SHELL target, with dependency + * operator, followed by a series of blank-separated words. Double + * quotes can be used to use blanks in words. A backslash escapes + * anything (most notably a double-quote and a space) and + * provides the functionality it does in C. Each word consists of + * keyword and value separated by an equal sign. There should be no + * unnecessary spaces in the word. The keywords are as follows: + * name Name of shell. + * path Location of shell. Overrides "name" if given + * quiet Command to turn off echoing. + * echo Command to turn echoing on + * filter Result of turning off echoing that shouldn't be + * printed. + * echoFlag Flag to turn echoing on at the start + * errFlag Flag to turn error checking on at the start + * hasErrCtl True if shell has error checking control + * check Command to turn on error checking if hasErrCtl + * is TRUE or template of command to echo a command + * for which error checking is off if hasErrCtl is + * FALSE. + * ignore Command to turn off error checking if hasErrCtl + * is TRUE or template of command to execute a + * command so as to ignore any errors it returns if + * hasErrCtl is FALSE. + * + *----------------------------------------------------------------------- + */ +ReturnStatus +Job_ParseShell(line) + char *line; /* The shell spec */ +{ + char **words; + int wordCount; + register char **argv; + register int argc; + char *path; + Shell newShell; + Boolean fullSpec = FALSE; + + while (isspace((unsigned char)*line)) { + line++; + } + + if (shellArgv) + free(shellArgv); + + words = brk_string(line, &wordCount, TRUE, &shellArgv); + + memset((Address)&newShell, 0, sizeof(newShell)); + + /* + * Parse the specification by keyword + */ + for (path = NULL, argc = wordCount - 1, argv = words; + argc != 0; + argc--, argv++) { + if (strncmp(*argv, "path=", 5) == 0) { + path = &argv[0][5]; + } else if (strncmp(*argv, "name=", 5) == 0) { + newShell.name = &argv[0][5]; + } else { + if (strncmp(*argv, "quiet=", 6) == 0) { + newShell.echoOff = &argv[0][6]; + } else if (strncmp(*argv, "echo=", 5) == 0) { + newShell.echoOn = &argv[0][5]; + } else if (strncmp(*argv, "filter=", 7) == 0) { + newShell.noPrint = &argv[0][7]; + newShell.noPLen = strlen(newShell.noPrint); + } else if (strncmp(*argv, "echoFlag=", 9) == 0) { + newShell.echo = &argv[0][9]; + } else if (strncmp(*argv, "errFlag=", 8) == 0) { + newShell.exit = &argv[0][8]; + } else if (strncmp(*argv, "hasErrCtl=", 10) == 0) { + char c = argv[0][10]; + newShell.hasErrCtl = !((c != 'Y') && (c != 'y') && + (c != 'T') && (c != 't')); + } else if (strncmp(*argv, "check=", 6) == 0) { + newShell.errCheck = &argv[0][6]; + } else if (strncmp(*argv, "ignore=", 7) == 0) { + newShell.ignErr = &argv[0][7]; + } else { + Parse_Error(PARSE_FATAL, "Unknown keyword \"%s\"", + *argv); + free(words); + return(FAILURE); + } + fullSpec = TRUE; + } + } + + if (path == NULL) { + /* + * If no path was given, the user wants one of the pre-defined shells, + * yes? So we find the one s/he wants with the help of JobMatchShell + * and set things up the right way. shellPath will be set up by + * Job_Init. + */ + if (newShell.name == NULL) { + Parse_Error(PARSE_FATAL, "Neither path nor name specified"); + return(FAILURE); + } else { + commandShell = JobMatchShell(newShell.name); + shellName = newShell.name; + } + } else { + /* + * The user provided a path. If s/he gave nothing else (fullSpec is + * FALSE), try and find a matching shell in the ones we know of. + * Else we just take the specification at its word and copy it + * to a new location. In either case, we need to record the + * path the user gave for the shell. + */ + shellPath = path; + path = strrchr(path, '/'); + if (path == NULL) { + path = shellPath; + } else { + path += 1; + } + if (newShell.name != NULL) { + shellName = newShell.name; + } else { + shellName = path; + } + if (!fullSpec) { + commandShell = JobMatchShell(shellName); + } else { + commandShell = (Shell *) emalloc(sizeof(Shell)); + *commandShell = newShell; + } + } + + if (commandShell->echoOn && commandShell->echoOff) { + commandShell->hasEchoCtl = TRUE; + } + + if (!commandShell->hasErrCtl) { + if (commandShell->errCheck == NULL) { + commandShell->errCheck = ""; + } + if (commandShell->ignErr == NULL) { + commandShell->ignErr = "%s\n"; + } + } + + /* + * Do not free up the words themselves, since they might be in use by the + * shell specification. + */ + free(words); + return SUCCESS; +} + +/*- + *----------------------------------------------------------------------- + * JobInterrupt -- + * Handle the receipt of an interrupt. + * + * Results: + * None + * + * Side Effects: + * All children are killed. Another job will be started if the + * .INTERRUPT target was given. + *----------------------------------------------------------------------- + */ +static void +JobInterrupt(runINTERRUPT, signo) + int runINTERRUPT; /* Non-zero if commands for the .INTERRUPT + * target should be executed */ + int signo; /* signal received */ +{ + LstNode ln; /* element in job table */ + Job *job; /* job descriptor in that element */ + GNode *interrupt; /* the node describing the .INTERRUPT target */ + + aborting = ABORT_INTERRUPT; + + (void) Lst_Open(jobs); + while ((ln = Lst_Next(jobs)) != NILLNODE) { + job = (Job *) Lst_Datum(ln); + + if (!Targ_Precious(job->node)) { + char *file = (job->node->path == NULL ? + job->node->name : + job->node->path); + if (!noExecute && eunlink(file) != -1) { + Error("*** %s removed", file); + } + } +#ifdef RMT_WANTS_SIGNALS + if (job->flags & JOB_REMOTE) { + /* + * If job is remote, let the Rmt module do the killing. + */ + if (!Rmt_Signal(job, signo)) { + /* + * If couldn't kill the thing, finish it out now with an + * error code, since no exit report will come in likely. + */ + WAIT_T status; + + WSET_EXITCODE(status, 1, 0); + JobFinish(job, status); + } + } else if (job->pid) { + KILL(job->pid, signo); + } +#else + if (job->pid) { + if (DEBUG(JOB)) { + (void) fprintf(stdout, + "JobInterrupt passing signal to child %d.\n", + job->pid); + (void) fflush(stdout); + } + KILL(job->pid, signo); + } +#endif /* RMT_WANTS_SIGNALS */ + } + +#ifdef REMOTE + (void)Lst_Open(stoppedJobs); + while ((ln = Lst_Next(stoppedJobs)) != NILLNODE) { + job = (Job *) Lst_Datum(ln); + + if (job->flags & JOB_RESTART) { + if (DEBUG(JOB)) { + (void) fprintf(stdout, "%s%s", + "JobInterrupt skipping job on stopped queue", + "-- it was waiting to be restarted.\n"); + (void) fflush(stdout); + } + continue; + } + if (!Targ_Precious(job->node)) { + char *file = (job->node->path == NULL ? + job->node->name : + job->node->path); + if (eunlink(file) == 0) { + Error("*** %s removed", file); + } + } + /* + * Resume the thing so it will take the signal. + */ + if (DEBUG(JOB)) { + (void) fprintf(stdout, + "JobInterrupt passing CONT to stopped child %d.\n", + job->pid); + (void) fflush(stdout); + } + KILL(job->pid, SIGCONT); +#ifdef RMT_WANTS_SIGNALS + if (job->flags & JOB_REMOTE) { + /* + * If job is remote, let the Rmt module do the killing. + */ + if (!Rmt_Signal(job, SIGINT)) { + /* + * If couldn't kill the thing, finish it out now with an + * error code, since no exit report will come in likely. + */ + WAIT_T status; + + WSET_EXITCODE(status, 1, 0); + JobFinish(job, status); + } + } else if (job->pid) { + if (DEBUG(JOB)) { + (void) fprintf(stdout, + "JobInterrupt passing interrupt to stopped child %d.\n", + job->pid); + (void) fflush(stdout); + } + KILL(job->pid, SIGINT); + } +#endif /* RMT_WANTS_SIGNALS */ + } +#endif + Lst_Close(stoppedJobs); + + if (runINTERRUPT && !touchFlag) { + interrupt = Targ_FindNode(".INTERRUPT", TARG_NOCREATE); + if (interrupt != NILGNODE) { + ignoreErrors = FALSE; + + JobStart(interrupt, JOB_IGNDOTS, (Job *)0); + while (nJobs) { + Job_CatchOutput(); +#ifndef RMT_WILL_WATCH + Job_CatchChildren(!usePipes); +#endif /* RMT_WILL_WATCH */ + } + } + } + Trace_Log(MAKEINTR, 0); + exit(signo); +} + +/* + *----------------------------------------------------------------------- + * Job_Finish -- + * Do final processing such as the running of the commands + * attached to the .END target. + * + * Results: + * Number of errors reported. + * + * Side Effects: + * None. + *----------------------------------------------------------------------- + */ +int +Job_Finish() +{ + if (postCommands != NILGNODE && !Lst_IsEmpty(postCommands->commands)) { + if (errors) { + Error("Errors reported so .END ignored"); + } else { + JobStart(postCommands, JOB_SPECIAL | JOB_IGNDOTS, NULL); + + while (nJobs) { + Job_CatchOutput(); +#ifndef RMT_WILL_WATCH + Job_CatchChildren(!usePipes); +#endif /* RMT_WILL_WATCH */ + } + } + } + Job_TokenFlush(); + return(errors); +} + +/*- + *----------------------------------------------------------------------- + * Job_End -- + * Cleanup any memory used by the jobs module + * + * Results: + * None. + * + * Side Effects: + * Memory is freed + *----------------------------------------------------------------------- + */ +void +Job_End() +{ +#ifdef CLEANUP + if (shellArgv) + free(shellArgv); +#endif +} + +/*- + *----------------------------------------------------------------------- + * Job_Wait -- + * Waits for all running jobs to finish and returns. Sets 'aborting' + * to ABORT_WAIT to prevent other jobs from starting. + * + * Results: + * None. + * + * Side Effects: + * Currently running jobs finish. + * + *----------------------------------------------------------------------- + */ +void +Job_Wait() +{ + aborting = ABORT_WAIT; + while (nJobs != 0) { + Job_CatchOutput(); +#ifndef RMT_WILL_WATCH + Job_CatchChildren(!usePipes); +#endif /* RMT_WILL_WATCH */ + } + Job_TokenFlush(); + aborting = 0; +} + +/*- + *----------------------------------------------------------------------- + * Job_AbortAll -- + * Abort all currently running jobs without handling output or anything. + * This function is to be called only in the event of a major + * error. Most definitely NOT to be called from JobInterrupt. + * + * Results: + * None + * + * Side Effects: + * All children are killed, not just the firstborn + *----------------------------------------------------------------------- + */ +void +Job_AbortAll() +{ + LstNode ln; /* element in job table */ + Job *job; /* the job descriptor in that element */ + WAIT_T foo; + + aborting = ABORT_ERROR; + + if (nJobs) { + + (void) Lst_Open(jobs); + while ((ln = Lst_Next(jobs)) != NILLNODE) { + job = (Job *) Lst_Datum(ln); + + /* + * kill the child process with increasingly drastic signals to make + * darn sure it's dead. + */ +#ifdef RMT_WANTS_SIGNALS + if (job->flags & JOB_REMOTE) { + Rmt_Signal(job, SIGINT); + Rmt_Signal(job, SIGKILL); + } else { + KILL(job->pid, SIGINT); + KILL(job->pid, SIGKILL); + } +#else + KILL(job->pid, SIGINT); + KILL(job->pid, SIGKILL); +#endif /* RMT_WANTS_SIGNALS */ + } + } + + /* + * Catch as many children as want to report in at first, then give up + */ + while (waitpid((pid_t) -1, &foo, WNOHANG) > 0) + continue; +} + +#ifdef REMOTE +/*- + *----------------------------------------------------------------------- + * JobFlagForMigration -- + * Handle the eviction of a child. Called from RmtStatusChange. + * Flags the child as remigratable and then suspends it. + * + * Results: + * none. + * + * Side Effects: + * The job descriptor is flagged for remigration. + * + *----------------------------------------------------------------------- + */ +void +JobFlagForMigration(hostID) + int hostID; /* ID of host we used, for matching children. */ +{ + register Job *job; /* job descriptor for dead child */ + LstNode jnode; /* list element for finding job */ + + if (DEBUG(JOB)) { + (void) fprintf(stdout, "JobFlagForMigration(%d) called.\n", hostID); + (void) fflush(stdout); + } + jnode = Lst_Find(jobs, (ClientData)hostID, JobCmpRmtID); + + if (jnode == NILLNODE) { + jnode = Lst_Find(stoppedJobs, (ClientData)hostID, JobCmpRmtID); + if (jnode == NILLNODE) { + if (DEBUG(JOB)) { + Error("Evicting host(%d) not in table", hostID); + } + return; + } + } + job = (Job *) Lst_Datum(jnode); + + if (DEBUG(JOB)) { + (void) fprintf(stdout, + "JobFlagForMigration(%d) found job '%s'.\n", hostID, + job->node->name); + (void) fflush(stdout); + } + + KILL(job->pid, SIGSTOP); + + job->flags |= JOB_REMIGRATE; +} + +#endif + +/*- + *----------------------------------------------------------------------- + * JobRestartJobs -- + * Tries to restart stopped jobs if there are slots available. + * Note that this tries to restart them regardless of pending errors. + * It's not good to leave stopped jobs lying around! + * + * Results: + * None. + * + * Side Effects: + * Resumes(and possibly migrates) jobs. + * + *----------------------------------------------------------------------- + */ +static void +JobRestartJobs() +{ + while (!Lst_IsEmpty(stoppedJobs)) { + if (DEBUG(JOB)) { + (void) fprintf(stdout, "Restarting a stopped job.\n"); + (void) fflush(stdout); + } + JobRestart((Job *)Lst_DeQueue(stoppedJobs)); + } +} + +#ifndef RMT_WILL_WATCH +#ifndef USE_SELECT +static void +watchfd(job) + Job *job; +{ + int i; + if (job->inPollfd != NULL) + Punt("Watching watched job"); + if (fds == NULL) { + maxfds = JBSTART; + fds = emalloc(sizeof(struct pollfd) * maxfds); + jobfds = emalloc(sizeof(Job **) * maxfds); + + fds[0].fd = job_pipe[0]; + fds[0].events = POLLIN; + jobfds[0] = &tokenWaitJob; + tokenWaitJob.inPollfd = &fds[0]; + nfds++; + } else if (nfds == maxfds) { + maxfds *= JBFACTOR; + fds = erealloc(fds, sizeof(struct pollfd) * maxfds); + jobfds = erealloc(jobfds, sizeof(Job **) * maxfds); + for (i = 0; i < nfds; i++) + jobfds[i]->inPollfd = &fds[i]; + } + + fds[nfds].fd = job->inPipe; + fds[nfds].events = POLLIN; + jobfds[nfds] = job; + job->inPollfd = &fds[nfds]; + nfds++; +} + +static void +clearfd(job) + Job *job; +{ + int i; + if (job->inPollfd == NULL) + Punt("Unwatching unwatched job"); + i = job->inPollfd - fds; + nfds--; + /* + * Move last job in table into hole made by dead job. + */ + if (nfds != i) { + fds[i] = fds[nfds]; + jobfds[i] = jobfds[nfds]; + jobfds[i]->inPollfd = &fds[i]; + } + job->inPollfd = NULL; +} + +static int +readyfd(job) + Job *job; +{ + if (job->inPollfd == NULL) + Punt("Polling unwatched job"); + return (job->inPollfd->revents & POLLIN) != 0; +} +#endif +#endif + +/*- + *----------------------------------------------------------------------- + * JobTokenAdd -- + * Put a token into the job pipe so that some make process can start + * another job. + * + * Side Effects: + * Allows more build jobs to be spawned somewhere. + * + *----------------------------------------------------------------------- + */ + +static void +JobTokenAdd() +{ + + if (DEBUG(JOB)) + printf("deposit token\n"); + write(job_pipe[1], "+", 1); +} + +/*- + *----------------------------------------------------------------------- + * Job_ServerStartTokenAdd -- + * Prep the job token pipe in the root make process. + * + *----------------------------------------------------------------------- + */ + +void Job_ServerStart(maxproc) + int maxproc; +{ + int i, flags; + char jobarg[64]; + + if (pipe(job_pipe) < 0) + Fatal ("error in pipe: %s", strerror(errno)); + + /* + * We mark the input side of the pipe non-blocking; we poll(2) the + * pipe when we're waiting for a job token, but we might lose the + * race for the token when a new one becomes available, so the read + * from the pipe should not block. + */ + flags = fcntl(job_pipe[0], F_GETFL, 0); + flags |= O_NONBLOCK; + fcntl(job_pipe[0], F_SETFL, flags); + + /* + * Mark job pipes as close-on-exec. + * Note that we will clear this when executing submakes. + */ + fcntl(job_pipe[0], F_SETFD, 1); + fcntl(job_pipe[1], F_SETFD, 1); + + snprintf(jobarg, sizeof(jobarg), "%d,%d", job_pipe[0], job_pipe[1]); + + Var_Append(MAKEFLAGS, "-J", VAR_GLOBAL); + Var_Append(MAKEFLAGS, jobarg, VAR_GLOBAL); + + /* + * Preload job_pipe with one token per job, save the one + * "extra" token for the primary job. + * + * XXX should clip maxJobs against PIPE_BUF -- if maxJobs is + * larger than the write buffer size of the pipe, we will + * deadlock here. + */ + for (i=1; i < maxproc; i++) + JobTokenAdd(); +} + +/* + * this tracks the number of tokens currently "out" to build jobs. + */ +int jobTokensRunning = 0; +int jobTokensFree = 0; +/*- + *----------------------------------------------------------------------- + * Job_TokenReturn -- + * Return a withdrawn token to the pool. + * + *----------------------------------------------------------------------- + */ + +void +Job_TokenReturn() +{ + jobTokensRunning--; + if (jobTokensRunning < 0) + Punt("token botch"); + if (jobTokensRunning) + jobTokensFree++; +} + +/*- + *----------------------------------------------------------------------- + * Job_TokenWithdraw -- + * Attempt to withdraw a token from the pool. + * + * Results: + * Returns TRUE if a token was withdrawn, and FALSE if the pool + * is currently empty. + * + * Side Effects: + * If pool is empty, set wantToken so that we wake up + * when a token is released. + * + *----------------------------------------------------------------------- + */ + + +Boolean +Job_TokenWithdraw() +{ + char tok; + int count; + + if (aborting) + return FALSE; + + if (jobTokensRunning == 0) { + if (DEBUG(JOB)) + printf("first one's free\n"); + jobTokensRunning++; + wantToken = FALSE; + return TRUE; + } + if (jobTokensFree > 0) { + jobTokensFree--; + jobTokensRunning++; + wantToken = FALSE; + return TRUE; + } + count = read(job_pipe[0], &tok, 1); + if (count == 0) + Fatal("eof on job pipe!"); + else if (count < 0) { + if (errno != EAGAIN) { + Fatal("job pipe read: %s", strerror(errno)); + } + if (DEBUG(JOB)) + printf("blocked for token\n"); + wantToken = TRUE; + return FALSE; + } + wantToken = FALSE; + jobTokensRunning++; + if (DEBUG(JOB)) + printf("withdrew token\n"); + return TRUE; +} + +/*- + *----------------------------------------------------------------------- + * Job_TokenFlush -- + * Return free tokens to the pool. + * + *----------------------------------------------------------------------- + */ + +void +Job_TokenFlush() +{ + if (compatMake) return; + + while (jobTokensFree > 0) { + JobTokenAdd(); + jobTokensFree--; + } +} + diff --git a/bootstrap/bmake/job.h b/bootstrap/bmake/job.h new file mode 100644 index 00000000000..87bf8e1d675 --- /dev/null +++ b/bootstrap/bmake/job.h @@ -0,0 +1,265 @@ +/* $NetBSD: job.h,v 1.1.1.1 2004/03/11 13:04:08 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. + * Copyright (c) 1988, 1989 by Adam de Boor + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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: @(#)job.h 8.1 (Berkeley) 6/6/93 + */ + +/*- + * job.h -- + * Definitions pertaining to the running of jobs in parallel mode. + * Exported from job.c for the use of remote-execution modules. + */ +#ifndef _JOB_H_ +#define _JOB_H_ + +#define TMPPAT "/tmp/makeXXXXXX" + +#ifdef USE_SELECT +/* + * The SEL_ constants determine the maximum amount of time spent in select + * before coming out to see if a child has finished. SEL_SEC is the number of + * seconds and SEL_USEC is the number of micro-seconds + */ +#define SEL_SEC 5 +#define SEL_USEC 0 +#else +/* + * The POLL_MSEC constant determines the maximum number of milliseconds spent + * in poll before coming out to see if a child has finished. + */ +#define POLL_MSEC 5000 +#endif + + +/*- + * Job Table definitions. + * + * Each job has several things associated with it: + * 1) The process id of the child shell + * 2) The graph node describing the target being made by this job + * 3) A LstNode for the first command to be saved after the job + * completes. This is NILLNODE if there was no "..." in the job's + * commands. + * 4) An FILE* for writing out the commands. This is only + * used before the job is actually started. + * 5) A union of things used for handling the shell's output. Different + * parts of the union are used based on the value of the usePipes + * flag. If it is true, the output is being caught via a pipe and + * the descriptors of our pipe, an array in which output is line + * buffered and the current position in that buffer are all + * maintained for each job. If, on the other hand, usePipes is false, + * the output is routed to a temporary file and all that is kept + * is the name of the file and the descriptor open to the file. + * 6) An identifier provided by and for the exclusive use of the + * Rmt module. + * 7) A word of flags which determine how the module handles errors, + * echoing, etc. for the job + * + * The job "table" is kept as a linked Lst in 'jobs', with the number of + * active jobs maintained in the 'nJobs' variable. At no time will this + * exceed the value of 'maxJobs', initialized by the Job_Init function. + * + * When a job is finished, the Make_Update function is called on each of the + * parents of the node which was just remade. This takes care of the upward + * traversal of the dependency graph. + */ +#ifndef RMT_WILL_WATCH +#ifndef USE_SELECT +struct pollfd; +#endif +#endif + +#define JOB_BUFSIZE 1024 +typedef struct Job { + int pid; /* The child's process ID */ + GNode *node; /* The target the child is making */ + LstNode tailCmds; /* The node of the first command to be + * saved when the job has been run */ + FILE *cmdFILE; /* When creating the shell script, this is + * where the commands go */ + int rmtID; /* ID returned from Rmt module */ + short flags; /* Flags to control treatment of job */ +#define JOB_IGNERR 0x001 /* Ignore non-zero exits */ +#define JOB_SILENT 0x002 /* no output */ +#define JOB_SPECIAL 0x004 /* Target is a special one. i.e. run it locally + * if we can't export it and maxLocal is 0 */ +#define JOB_IGNDOTS 0x008 /* Ignore "..." lines when processing + * commands */ +#define JOB_REMOTE 0x010 /* Job is running remotely */ +#define JOB_FIRST 0x020 /* Job is first job for the node */ +#define JOB_REMIGRATE 0x040 /* Job needs to be remigrated */ +#define JOB_RESTART 0x080 /* Job needs to be completely restarted */ +#define JOB_RESUME 0x100 /* Job needs to be resumed b/c it stopped, + * for some reason */ +#define JOB_CONTINUING 0x200 /* We are in the process of resuming this job. + * Used to avoid infinite recursion between + * JobFinish and JobRestart */ +#define JOB_TRACED 0x400 /* we've sent 'set -x' */ + + union { + struct { + int op_inPipe; /* Input side of pipe associated + * with job's output channel */ +#ifndef RMT_WILL_WATCH +#ifndef USE_SELECT + struct pollfd *op_inPollfd; /* pollfd associated with inPipe */ +#endif +#endif + int op_outPipe; /* Output side of pipe associated with + * job's output channel */ + char op_outBuf[JOB_BUFSIZE + 1]; + /* Buffer for storing the output of the + * job, line by line */ + int op_curPos; /* Current position in op_outBuf */ + } o_pipe; /* data used when catching the output via + * a pipe */ + struct { + char of_outFile[sizeof(TMPPAT)+2]; + /* Name of file to which shell output + * was rerouted */ + int of_outFd; /* Stream open to the output + * file. Used to funnel all + * from a single job to one file + * while still allowing + * multiple shell invocations */ + } o_file; /* Data used when catching the output in + * a temporary file */ + } output; /* Data for tracking a shell's output */ +} Job; + +#define outPipe output.o_pipe.op_outPipe +#define inPipe output.o_pipe.op_inPipe +#define inPollfd output.o_pipe.op_inPollfd +#define outBuf output.o_pipe.op_outBuf +#define curPos output.o_pipe.op_curPos +#define outFile output.o_file.of_outFile +#define outFd output.o_file.of_outFd + + +/*- + * Shell Specifications: + * Each shell type has associated with it the following information: + * 1) The string which must match the last character of the shell name + * for the shell to be considered of this type. The longest match + * wins. + * 2) A command to issue to turn off echoing of command lines + * 3) A command to issue to turn echoing back on again + * 4) What the shell prints, and its length, when given the echo-off + * command. This line will not be printed when received from the shell + * 5) A boolean to tell if the shell has the ability to control + * error checking for individual commands. + * 6) The string to turn this checking on. + * 7) The string to turn it off. + * 8) The command-flag to give to cause the shell to start echoing + * commands right away. + * 9) The command-flag to cause the shell to Lib_Exit when an error is + * detected in one of the commands. + * + * Some special stuff goes on if a shell doesn't have error control. In such + * a case, errCheck becomes a printf template for echoing the command, + * should echoing be on and ignErr becomes another printf template for + * executing the command while ignoring the return status. If either of these + * strings is empty when hasErrCtl is FALSE, the command will be executed + * anyway as is and if it causes an error, so be it. + */ +typedef struct Shell { + char *name; /* the name of the shell. For Bourne and C + * shells, this is used only to find the + * shell description when used as the single + * source of a .SHELL target. For user-defined + * shells, this is the full path of the shell. + */ + Boolean hasEchoCtl; /* True if both echoOff and echoOn defined */ + char *echoOff; /* command to turn off echo */ + char *echoOn; /* command to turn it back on again */ + char *noPrint; /* command to skip when printing output from + * shell. This is usually the command which + * was executed to turn off echoing */ + int noPLen; /* length of noPrint command */ + Boolean hasErrCtl; /* set if can control error checking for + * individual commands */ + char *errCheck; /* string to turn error checking on */ + char *ignErr; /* string to turn off error checking */ + /* + * command-line flags + */ + char *echo; /* echo commands */ + char *exit; /* exit on error */ +} Shell; + +extern int job_pipe[2]; /* token pipe for jobs. */ +extern int jobTokensRunning; /* tokens currently "out" */ +extern int jobTokensFree; /* tokens free but not yet released to pipe */ + +#ifdef REMOTE +extern char *targFmt; /* Format string for banner that separates + * output from multiple jobs. Contains a + * single %s where the name of the node being + * made should be put. */ +extern GNode *lastNode; /* Last node for which a banner was printed. + * If Rmt module finds it necessary to print + * a banner, it should set this to the node + * for which the banner was printed */ +extern int nJobs; /* Number of jobs running (local and remote) */ +extern int nLocal; /* Number of jobs running locally */ +extern Lst jobs; /* List of active job descriptors */ +extern Lst stoppedJobs; /* List of jobs that are stopped or didn't + * quite get started */ +#endif + +void Job_Touch __P((GNode *, Boolean)); +Boolean Job_CheckCommands __P((GNode *, void (*abortProc )(char *, ...))); +void Job_CatchChildren __P((Boolean)); +void Job_CatchOutput __P((void)); +void Job_Make __P((GNode *)); +void Job_Init __P((int, int)); +Boolean Job_Full __P((void)); +Boolean Job_Empty __P((void)); +ReturnStatus Job_ParseShell __P((char *)); +int Job_Finish __P((void)); +void Job_End __P((void)); +void Job_Wait __P((void)); +void Job_AbortAll __P((void)); +void JobFlagForMigration __P((int)); +void Job_TokenReturn __P((void)); +void Job_TokenFlush __P((void)); +Boolean Job_TokenWithdraw __P((void)); +void Job_ServerStart __P((int)); + +#endif /* _JOB_H_ */ diff --git a/bootstrap/bmake/list.h b/bootstrap/bmake/list.h new file mode 100644 index 00000000000..27e88f91934 --- /dev/null +++ b/bootstrap/bmake/list.h @@ -0,0 +1,300 @@ +/* $NetBSD: list.h,v 1.1.1.1 2004/03/11 13:04:08 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. + * Copyright (c) 1988, 1989 by Adam de Boor + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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: @(#)list.h 8.1 (Berkeley) 6/6/93 + */ + +/* + * list.h -- + * + * Structures, macros, and routines exported by the List module. + */ + +#ifndef _LIST +#define _LIST + +#ifndef _SPRITE +#include "sprite.h" +#endif _SPRITE + +/* + * This module defines the list abstraction, which enables one to link + * together arbitrary data structures. Lists are doubly-linked and + * circular. A list contains a header followed by its real members, if + * any. (An empty list therefore consists of a single element, the + * header, whose nextPtr and prevPtr fields point to itself). To refer + * to a list as a whole, the user keeps a pointer to the header; that + * header is initialized by a call to List_Init(), which creates an empty + * list given a pointer to a List_Links structure (described below). + * + * The links are contained in a two-element structure called List_Links. + * A list joins List_Links records (that is, each List_Links structure + * points to other List_Links structures), but if the List_Links is the + * first field within a larger structure, then the larger structures are + * effectively linked together as follows: + * + * header + * (List_Links) first elt. second elt. + * ----------------- ----------------- ----------------- + * ..-> | nextPtr | ----> | List_Links | ----> | List_Links |----.. + * | - - - - - - - | | | | | + * ..-- | prevPtr | <---- | | <---- | |<---.. + * ----------------- - --- --- --- - - --- --- --- - + * | rest of | | rest of | + * | structure | | structure | + * | | | | + * | ... | | ... | + * ----------------- ----------------- + * + * It is possible to link structures through List_Links fields that are + * not at the beginning of the larger structure, but it is then necessary + * to perform pointer arithmetic to find the beginning of the larger + * structure, given a pointer to some point within it. + * + * A typical structure might be something like: + * + * typedef struct { + * List_Links links; + * char ch; + * integer flags; + * } EditChar; + * + * Before an element is inserted in a list for the first time, it must + * be initialized by calling the macro List_InitElement(). + */ + + +/* + * data structure for lists + */ + +typedef struct List_Links { + struct List_Links *prevPtr; + struct List_Links *nextPtr; +} List_Links; + +/* + * procedures + */ + +void List_Init(); /* initialize a header to a list */ +void List_Insert(); /* insert an element into a list */ +void List_Remove(); /* remove an element from a list */ +void List_Move(); /* move an element elsewhere in a list */ + +/* + * ---------------------------------------------------------------------------- + * + * List_InitElement -- + * + * Initialize a list element. Must be called before an element is first + * inserted into a list. + * + * ---------------------------------------------------------------------------- + */ +#define List_InitElement(elementPtr) \ + (elementPtr)->prevPtr = (List_Links *) NIL; \ + (elementPtr)->nextPtr = (List_Links *) NIL; + +/* + * Macros for stepping through or selecting parts of lists + */ + +/* + * ---------------------------------------------------------------------------- + * + * LIST_FORALL -- + * + * Macro to loop through a list and perform an operation on each member. + * + * Usage: LIST_FORALL(headerPtr, itemPtr) { + * / * + * * operation on itemPtr, which points to successive members + * * of the list + * * + * * It may be appropriate to first assign + * * foobarPtr = (Foobar *) itemPtr; + * * to refer to the entire Foobar structure. + * * / + * } + * + * Note: itemPtr must be a List_Links pointer variable, and headerPtr + * must evaluate to a pointer to a List_Links structure. + * + * ---------------------------------------------------------------------------- + */ + +#define LIST_FORALL(headerPtr, itemPtr) \ + for (itemPtr = List_First(headerPtr); \ + !List_IsAtEnd((headerPtr),itemPtr); \ + itemPtr = List_Next(itemPtr)) + +/* + * ---------------------------------------------------------------------------- + * + * List_IsEmpty -- + * + * Macro: Boolean value, TRUE if the given list does not contain any + * members. + * + * Usage: if (List_IsEmpty(headerPtr)) ... + * + * ---------------------------------------------------------------------------- + */ + +#define List_IsEmpty(headerPtr) \ + ((headerPtr) == (headerPtr)->nextPtr) + +/* + * ---------------------------------------------------------------------------- + * + * List_IsAtEnd -- + * + * Macro: Boolean value, TRUE if itemPtr is after the end of headerPtr + * (i.e., itemPtr is the header of the list). + * + * Usage: if (List_IsAtEnd(headerPtr, itemPtr)) ... + * + * ---------------------------------------------------------------------------- + */ + + +#define List_IsAtEnd(headerPtr, itemPtr) \ + ((itemPtr) == (headerPtr)) + + +/* + * ---------------------------------------------------------------------------- + * + * List_First -- + * + * Macro to return the first member in a list, which is the header if + * the list is empty. + * + * Usage: firstPtr = List_First(headerPtr); + * + * ---------------------------------------------------------------------------- + */ + +#define List_First(headerPtr) ((headerPtr)->nextPtr) + +/* + * ---------------------------------------------------------------------------- + * + * List_Last -- + * + * Macro to return the last member in a list, which is the header if + * the list is empty. + * + * Usage: lastPtr = List_Last(headerPtr); + * + * ---------------------------------------------------------------------------- + */ + +#define List_Last(headerPtr) ((headerPtr)->prevPtr) + +/* + * ---------------------------------------------------------------------------- + * + * List_Prev -- + * + * Macro to return the member preceding the given member in its list. + * If the given list member is the first element in the list, List_Prev + * returns the list header. + * + * Usage: prevPtr = List_Prev(itemPtr); + * + * ---------------------------------------------------------------------------- + */ + +#define List_Prev(itemPtr) ((itemPtr)->prevPtr) + +/* + * ---------------------------------------------------------------------------- + * + * List_Next -- + * + * Macro to return the member following the given member in its list. + * If the given list member is the last element in the list, List_Next + * returns the list header. + * + * Usage: nextPtr = List_Next(itemPtr); + * + * ---------------------------------------------------------------------------- + */ + +#define List_Next(itemPtr) ((itemPtr)->nextPtr) + + +/* + * ---------------------------------------------------------------------------- + * The List_Insert procedure takes two arguments. The first argument + * is a pointer to the structure to be inserted into a list, and + * the second argument is a pointer to the list member after which + * the new element is to be inserted. Macros are used to determine + * which existing member will precede the new one. + * + * The List_Move procedure takes a destination argument with the same + * semantics as List_Insert. + * + * The following macros define where to insert the new element + * in the list: + * + * LIST_AFTER(itemPtr) -- insert after itemPtr + * LIST_BEFORE(itemPtr) -- insert before itemPtr + * LIST_ATFRONT(headerPtr) -- insert at front of list + * LIST_ATREAR(headerPtr) -- insert at end of list + * + * For example, + * + * List_Insert(itemPtr, LIST_AFTER(otherPtr)); + * + * will insert itemPtr following otherPtr in the list containing otherPtr. + * ---------------------------------------------------------------------------- + */ + +#define LIST_AFTER(itemPtr) ((List_Links *) itemPtr) + +#define LIST_BEFORE(itemPtr) (((List_Links *) itemPtr)->prevPtr) + +#define LIST_ATFRONT(headerPtr) ((List_Links *) headerPtr) + +#define LIST_ATREAR(headerPtr) (((List_Links *) headerPtr)->prevPtr) + +#endif /* _LIST */ diff --git a/bootstrap/bmake/lst.h b/bootstrap/bmake/lst.h new file mode 100644 index 00000000000..83eaf9cafba --- /dev/null +++ b/bootstrap/bmake/lst.h @@ -0,0 +1,166 @@ +/* $NetBSD: lst.h,v 1.1.1.1 2004/03/11 13:04:08 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. + * Copyright (c) 1988, 1989 by Adam de Boor + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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: @(#)lst.h 8.1 (Berkeley) 6/6/93 + */ + +/*- + * lst.h -- + * Header for using the list library + */ +#ifndef _LST_H_ +#define _LST_H_ + +#include +#if __STDC__ +#include +#endif +#include "sprite.h" + +/* + * basic typedef. This is what the Lst_ functions handle + */ + +typedef struct Lst *Lst; +typedef struct LstNode *LstNode; + +#define NILLST ((Lst) NIL) +#define NILLNODE ((LstNode) NIL) + +/* + * NOFREE can be used as the freeProc to Lst_Destroy when the elements are + * not to be freed. + * NOCOPY performs similarly when given as the copyProc to Lst_Duplicate. + */ +#define NOFREE ((void (*) __P((ClientData))) 0) +#define NOCOPY ((ClientData (*) __P((ClientData))) 0) + +#define LST_CONCNEW 0 /* create new LstNode's when using Lst_Concat */ +#define LST_CONCLINK 1 /* relink LstNode's when using Lst_Concat */ + +/* + * Creation/destruction functions + */ +/* Create a new list */ +Lst Lst_Init __P((Boolean)); +/* Duplicate an existing list */ +Lst Lst_Duplicate __P((Lst, ClientData (*)(ClientData))); +/* Destroy an old one */ +void Lst_Destroy __P((Lst, void (*)(ClientData))); +/* True if list is empty */ +Boolean Lst_IsEmpty __P((Lst)); + +/* + * Functions to modify a list + */ +/* Insert an element before another */ +ReturnStatus Lst_Insert __P((Lst, LstNode, ClientData)); +/* Insert an element after another */ +ReturnStatus Lst_Append __P((Lst, LstNode, ClientData)); +/* Place an element at the front of a lst. */ +ReturnStatus Lst_AtFront __P((Lst, ClientData)); +/* Place an element at the end of a lst. */ +ReturnStatus Lst_AtEnd __P((Lst, ClientData)); +/* Remove an element */ +ReturnStatus Lst_Remove __P((Lst, LstNode)); +/* Replace a node with a new value */ +ReturnStatus Lst_Replace __P((LstNode, ClientData)); +/* Concatenate two lists */ +ReturnStatus Lst_Concat __P((Lst, Lst, int)); + +/* + * Node-specific functions + */ +/* Return first element in list */ +LstNode Lst_First __P((Lst)); +/* Return last element in list */ +LstNode Lst_Last __P((Lst)); +/* Return successor to given element */ +LstNode Lst_Succ __P((LstNode)); +/* Get datum from LstNode */ +ClientData Lst_Datum __P((LstNode)); + +/* + * Functions for entire lists + */ +/* Find an element in a list */ +LstNode Lst_Find __P((Lst, ClientData, + int (*)(ClientData, ClientData))); +/* Find an element starting from somewhere */ +LstNode Lst_FindFrom __P((Lst, LstNode, ClientData, + int (*cProc)(ClientData, ClientData))); +/* + * See if the given datum is on the list. Returns the LstNode containing + * the datum + */ +LstNode Lst_Member __P((Lst, ClientData)); +/* Apply a function to all elements of a lst */ +void Lst_ForEach __P((Lst, int (*)(ClientData, ClientData), + ClientData)); +/* + * Apply a function to all elements of a lst starting from a certain point. + * If the list is circular, the application will wrap around to the + * beginning of the list again. + */ +void Lst_ForEachFrom __P((Lst, LstNode, + int (*)(ClientData, ClientData), + ClientData)); +/* + * these functions are for dealing with a list as a table, of sorts. + * An idea of the "current element" is kept and used by all the functions + * between Lst_Open() and Lst_Close(). + */ +/* Open the list */ +ReturnStatus Lst_Open __P((Lst)); +/* Next element please */ +LstNode Lst_Next __P((Lst)); +/* Done yet? */ +Boolean Lst_IsAtEnd __P((Lst)); +/* Finish table access */ +void Lst_Close __P((Lst)); + +/* + * for using the list as a queue + */ +/* Place an element at tail of queue */ +ReturnStatus Lst_EnQueue __P((Lst, ClientData)); +/* Remove an element from head of queue */ +ClientData Lst_DeQueue __P((Lst)); + +#endif /* _LST_H_ */ diff --git a/bootstrap/bmake/lst.lib/Makefile b/bootstrap/bmake/lst.lib/Makefile new file mode 100644 index 00000000000..303b0f57ef2 --- /dev/null +++ b/bootstrap/bmake/lst.lib/Makefile @@ -0,0 +1,10 @@ +# $NetBSD: Makefile,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $ + +OBJ=lstAppend.o lstDupl.o lstInit.o lstOpen.o lstAtEnd.o lstEnQueue.o \ + lstInsert.o lstAtFront.o lstIsAtEnd.o lstClose.o lstFind.o lstIsEmpty.o \ + lstRemove.o lstConcat.o lstFindFrom.o lstLast.o lstReplace.o lstFirst.o \ + lstDatum.o lstForEach.o lstMember.o lstSucc.o lstDeQueue.o \ + lstForEachFrom.o lstDestroy.o lstNext.o + +CPPFLAGS=-I${.CURDIR}/.. +all: ${OBJ} diff --git a/bootstrap/bmake/lst.lib/lstAppend.c b/bootstrap/bmake/lst.lib/lstAppend.c new file mode 100644 index 00000000000..48c8984e66b --- /dev/null +++ b/bootstrap/bmake/lst.lib/lstAppend.c @@ -0,0 +1,124 @@ +/* $NetBSD: lstAppend.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: lstAppend.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstAppend.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstAppend.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstAppend.c -- + * Add a new node with a new datum after an existing node + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Append -- + * Create a new node and add it to the given list after the given node. + * + * Results: + * SUCCESS if all went well. + * + * Side Effects: + * A new ListNode is created and linked in to the List. The lastPtr + * field of the List will be altered if ln is the last node in the + * list. lastPtr and firstPtr will alter if the list was empty and + * ln was NILLNODE. + * + *----------------------------------------------------------------------- + */ +ReturnStatus +Lst_Append (l, ln, d) + Lst l; /* affected list */ + LstNode ln; /* node after which to append the datum */ + ClientData d; /* said datum */ +{ + register List list; + register ListNode lNode; + register ListNode nLNode; + + if (LstValid (l) && (ln == NILLNODE && LstIsEmpty (l))) { + goto ok; + } + + if (!LstValid (l) || LstIsEmpty (l) || ! LstNodeValid (ln, l)) { + return (FAILURE); + } + ok: + + list = (List)l; + lNode = (ListNode)ln; + + PAlloc (nLNode, ListNode); + nLNode->datum = d; + nLNode->useCount = nLNode->flags = 0; + + if (lNode == NilListNode) { + if (list->isCirc) { + nLNode->nextPtr = nLNode->prevPtr = nLNode; + } else { + nLNode->nextPtr = nLNode->prevPtr = NilListNode; + } + list->firstPtr = list->lastPtr = nLNode; + } else { + nLNode->prevPtr = lNode; + nLNode->nextPtr = lNode->nextPtr; + + lNode->nextPtr = nLNode; + if (nLNode->nextPtr != NilListNode) { + nLNode->nextPtr->prevPtr = nLNode; + } + + if (lNode == list->lastPtr) { + list->lastPtr = nLNode; + } + } + + return (SUCCESS); +} + diff --git a/bootstrap/bmake/lst.lib/lstAtEnd.c b/bootstrap/bmake/lst.lib/lstAtEnd.c new file mode 100644 index 00000000000..129c0a315fd --- /dev/null +++ b/bootstrap/bmake/lst.lib/lstAtEnd.c @@ -0,0 +1,81 @@ +/* $NetBSD: lstAtEnd.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: lstAtEnd.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstAtEnd.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstAtEnd.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstAtEnd.c -- + * Add a node at the end of the list + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_AtEnd -- + * Add a node to the end of the given list + * + * Results: + * SUCCESS if life is good. + * + * Side Effects: + * A new ListNode is created and added to the list. + * + *----------------------------------------------------------------------- + */ +ReturnStatus +Lst_AtEnd (l, d) + Lst l; /* List to which to add the datum */ + ClientData d; /* Datum to add */ +{ + register LstNode end; + + end = Lst_Last (l); + return (Lst_Append (l, end, d)); +} diff --git a/bootstrap/bmake/lst.lib/lstAtFront.c b/bootstrap/bmake/lst.lib/lstAtFront.c new file mode 100644 index 00000000000..9fb4ee623e0 --- /dev/null +++ b/bootstrap/bmake/lst.lib/lstAtFront.c @@ -0,0 +1,82 @@ +/* $NetBSD: lstAtFront.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: lstAtFront.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstAtFront.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstAtFront.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstAtFront.c -- + * Add a node at the front of the list + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_AtFront -- + * Place a piece of data at the front of a list + * + * Results: + * SUCCESS or FAILURE + * + * Side Effects: + * A new ListNode is created and stuck at the front of the list. + * hence, firstPtr (and possible lastPtr) in the list are altered. + * + *----------------------------------------------------------------------- + */ +ReturnStatus +Lst_AtFront (l, d) + Lst l; + ClientData d; +{ + register LstNode front; + + front = Lst_First (l); + return (Lst_Insert (l, front, d)); +} diff --git a/bootstrap/bmake/lst.lib/lstClose.c b/bootstrap/bmake/lst.lib/lstClose.c new file mode 100644 index 00000000000..38f1e599f73 --- /dev/null +++ b/bootstrap/bmake/lst.lib/lstClose.c @@ -0,0 +1,88 @@ +/* $NetBSD: lstClose.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: lstClose.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstClose.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstClose.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstClose.c -- + * Close a list for sequential access. + * The sequential functions access the list in a slightly different way. + * CurPtr points to their idea of the current node in the list and they + * access the list based on it. Because the list is circular, Lst_Next + * and Lst_Prev will go around the list forever. Lst_IsAtEnd must be + * used to determine when to stop. + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Close -- + * Close a list which was opened for sequential access. + * + * Results: + * None. + * + * Side Effects: + * The list is closed. + * + *----------------------------------------------------------------------- + */ +void +Lst_Close (l) + Lst l; /* The list to close */ +{ + register List list = (List) l; + + if (LstValid(l) == TRUE) { + list->isOpen = FALSE; + list->atEnd = Unknown; + } +} + diff --git a/bootstrap/bmake/lst.lib/lstConcat.c b/bootstrap/bmake/lst.lib/lstConcat.c new file mode 100644 index 00000000000..602bb0a5620 --- /dev/null +++ b/bootstrap/bmake/lst.lib/lstConcat.c @@ -0,0 +1,187 @@ +/* $NetBSD: lstConcat.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: lstConcat.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstConcat.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstConcat.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * listConcat.c -- + * Function to concatentate two lists. + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Concat -- + * Concatenate two lists. New elements are created to hold the data + * elements, if specified, but the elements themselves are not copied. + * If the elements should be duplicated to avoid confusion with another + * list, the Lst_Duplicate function should be called first. + * If LST_CONCLINK is specified, the second list is destroyed since + * its pointers have been corrupted and the list is no longer useable. + * + * Results: + * SUCCESS if all went well. FAILURE otherwise. + * + * Side Effects: + * New elements are created and appended the first list. + *----------------------------------------------------------------------- + */ +ReturnStatus +Lst_Concat (l1, l2, flags) + Lst l1; /* The list to which l2 is to be appended */ + Lst l2; /* The list to append to l1 */ + int flags; /* LST_CONCNEW if LstNode's should be duplicated + * LST_CONCLINK if should just be relinked */ +{ + register ListNode ln; /* original LstNode */ + register ListNode nln; /* new LstNode */ + register ListNode last; /* the last element in the list. Keeps + * bookkeeping until the end */ + register List list1 = (List)l1; + register List list2 = (List)l2; + + if (!LstValid (l1) || !LstValid (l2)) { + return (FAILURE); + } + + if (flags == LST_CONCLINK) { + if (list2->firstPtr != NilListNode) { + /* + * We set the nextPtr of the + * last element of list two to be NIL to make the loop easier and + * so we don't need an extra case should the first list turn + * out to be non-circular -- the final element will already point + * to NIL space and the first element will be untouched if it + * existed before and will also point to NIL space if it didn't. + */ + list2->lastPtr->nextPtr = NilListNode; + /* + * So long as the second list isn't empty, we just link the + * first element of the second list to the last element of the + * first list. If the first list isn't empty, we then link the + * last element of the list to the first element of the second list + * The last element of the second list, if it exists, then becomes + * the last element of the first list. + */ + list2->firstPtr->prevPtr = list1->lastPtr; + if (list1->lastPtr != NilListNode) { + list1->lastPtr->nextPtr = list2->firstPtr; + } else { + list1->firstPtr = list2->firstPtr; + } + list1->lastPtr = list2->lastPtr; + } + if (list1->isCirc && list1->firstPtr != NilListNode) { + /* + * If the first list is supposed to be circular and it is (now) + * non-empty, we must make sure it's circular by linking the + * first element to the last and vice versa + */ + list1->firstPtr->prevPtr = list1->lastPtr; + list1->lastPtr->nextPtr = list1->firstPtr; + } + free ((Address)l2); + } else if (list2->firstPtr != NilListNode) { + /* + * We set the nextPtr of the last element of list 2 to be nil to make + * the loop less difficult. The loop simply goes through the entire + * second list creating new LstNodes and filling in the nextPtr, and + * prevPtr to fit into l1 and its datum field from the + * datum field of the corresponding element in l2. The 'last' node + * follows the last of the new nodes along until the entire l2 has + * been appended. Only then does the bookkeeping catch up with the + * changes. During the first iteration of the loop, if 'last' is nil, + * the first list must have been empty so the newly-created node is + * made the first node of the list. + */ + list2->lastPtr->nextPtr = NilListNode; + for (last = list1->lastPtr, ln = list2->firstPtr; + ln != NilListNode; + ln = ln->nextPtr) + { + PAlloc (nln, ListNode); + nln->datum = ln->datum; + if (last != NilListNode) { + last->nextPtr = nln; + } else { + list1->firstPtr = nln; + } + nln->prevPtr = last; + nln->flags = nln->useCount = 0; + last = nln; + } + + /* + * Finish bookkeeping. The last new element becomes the last element + * of list one. + */ + list1->lastPtr = last; + + /* + * The circularity of both list one and list two must be corrected + * for -- list one because of the new nodes added to it; list two + * because of the alteration of list2->lastPtr's nextPtr to ease the + * above for loop. + */ + if (list1->isCirc) { + list1->lastPtr->nextPtr = list1->firstPtr; + list1->firstPtr->prevPtr = list1->lastPtr; + } else { + last->nextPtr = NilListNode; + } + + if (list2->isCirc) { + list2->lastPtr->nextPtr = list2->firstPtr; + } + } + + return (SUCCESS); +} + diff --git a/bootstrap/bmake/lst.lib/lstDatum.c b/bootstrap/bmake/lst.lib/lstDatum.c new file mode 100644 index 00000000000..35d90c9f57d --- /dev/null +++ b/bootstrap/bmake/lst.lib/lstDatum.c @@ -0,0 +1,82 @@ +/* $NetBSD: lstDatum.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: lstDatum.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstDatum.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstDatum.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstDatum.c -- + * Return the datum associated with a list node. + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Datum -- + * Return the datum stored in the given node. + * + * Results: + * The datum or (ick!) NIL if the node is invalid. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +ClientData +Lst_Datum (ln) + LstNode ln; +{ + if (ln != NILLNODE) { + return (((ListNode)ln)->datum); + } else { + return ((ClientData) NIL); + } +} + diff --git a/bootstrap/bmake/lst.lib/lstDeQueue.c b/bootstrap/bmake/lst.lib/lstDeQueue.c new file mode 100644 index 00000000000..1aa2c7211b2 --- /dev/null +++ b/bootstrap/bmake/lst.lib/lstDeQueue.c @@ -0,0 +1,92 @@ +/* $NetBSD: lstDeQueue.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: lstDeQueue.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstDeQueue.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstDeQueue.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstDeQueue.c -- + * Remove the node and return its datum from the head of the list + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_DeQueue -- + * Remove and return the datum at the head of the given list. + * + * Results: + * The datum in the node at the head or (ick) NIL if the list + * is empty. + * + * Side Effects: + * The head node is removed from the list. + * + *----------------------------------------------------------------------- + */ +ClientData +Lst_DeQueue (l) + Lst l; +{ + ClientData rd; + register ListNode tln; + + tln = (ListNode) Lst_First (l); + if (tln == NilListNode) { + return ((ClientData) NIL); + } + + rd = tln->datum; + if (Lst_Remove (l, (LstNode)tln) == FAILURE) { + return ((ClientData) NIL); + } else { + return (rd); + } +} + diff --git a/bootstrap/bmake/lst.lib/lstDestroy.c b/bootstrap/bmake/lst.lib/lstDestroy.c new file mode 100644 index 00000000000..28bcf771065 --- /dev/null +++ b/bootstrap/bmake/lst.lib/lstDestroy.c @@ -0,0 +1,113 @@ +/* $NetBSD: lstDestroy.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: lstDestroy.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstDestroy.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstDestroy.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstDestroy.c -- + * Nuke a list and all its resources + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Destroy -- + * Destroy a list and free all its resources. If the freeProc is + * given, it is called with the datum from each node in turn before + * the node is freed. + * + * Results: + * None. + * + * Side Effects: + * The given list is freed in its entirety. + * + *----------------------------------------------------------------------- + */ +void +Lst_Destroy (l, freeProc) + Lst l; + register void (*freeProc) __P((ClientData)); +{ + register ListNode ln; + register ListNode tln = NilListNode; + register List list = (List)l; + + if (l == NILLST || ! l) { + /* + * Note the check for l == (Lst)0 to catch uninitialized static Lst's. + * Gross, but useful. + */ + return; + } + + /* To ease scanning */ + if (list->lastPtr != NilListNode) + list->lastPtr->nextPtr = NilListNode; + else { + free ((Address)l); + return; + } + + if (freeProc) { + for (ln = list->firstPtr; ln != NilListNode; ln = tln) { + tln = ln->nextPtr; + (*freeProc) (ln->datum); + free ((Address)ln); + } + } else { + for (ln = list->firstPtr; ln != NilListNode; ln = tln) { + tln = ln->nextPtr; + free ((Address)ln); + } + } + + free ((Address)l); +} diff --git a/bootstrap/bmake/lst.lib/lstDupl.c b/bootstrap/bmake/lst.lib/lstDupl.c new file mode 100644 index 00000000000..556e2abc84b --- /dev/null +++ b/bootstrap/bmake/lst.lib/lstDupl.c @@ -0,0 +1,110 @@ +/* $NetBSD: lstDupl.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: lstDupl.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstDupl.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstDupl.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * listDupl.c -- + * Duplicate a list. This includes duplicating the individual + * elements. + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Duplicate -- + * Duplicate an entire list. If a function to copy a ClientData is + * given, the individual client elements will be duplicated as well. + * + * Results: + * The new Lst structure or NILLST if failure. + * + * Side Effects: + * A new list is created. + *----------------------------------------------------------------------- + */ +Lst +Lst_Duplicate (l, copyProc) + Lst l; /* the list to duplicate */ + /* A function to duplicate each ClientData */ + ClientData (*copyProc) __P((ClientData)); +{ + register Lst nl; + register ListNode ln; + register List list = (List)l; + + if (!LstValid (l)) { + return (NILLST); + } + + nl = Lst_Init (list->isCirc); + if (nl == NILLST) { + return (NILLST); + } + + ln = list->firstPtr; + while (ln != NilListNode) { + if (copyProc != NOCOPY) { + if (Lst_AtEnd (nl, (*copyProc) (ln->datum)) == FAILURE) { + return (NILLST); + } + } else if (Lst_AtEnd (nl, ln->datum) == FAILURE) { + return (NILLST); + } + + if (list->isCirc && ln == list->lastPtr) { + ln = NilListNode; + } else { + ln = ln->nextPtr; + } + } + + return (nl); +} diff --git a/bootstrap/bmake/lst.lib/lstEnQueue.c b/bootstrap/bmake/lst.lib/lstEnQueue.c new file mode 100644 index 00000000000..c473f72f7a6 --- /dev/null +++ b/bootstrap/bmake/lst.lib/lstEnQueue.c @@ -0,0 +1,84 @@ +/* $NetBSD: lstEnQueue.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: lstEnQueue.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstEnQueue.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstEnQueue.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstEnQueue.c-- + * Treat the list as a queue and place a datum at its end + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_EnQueue -- + * Add the datum to the tail of the given list. + * + * Results: + * SUCCESS or FAILURE as returned by Lst_Append. + * + * Side Effects: + * the lastPtr field is altered all the time and the firstPtr field + * will be altered if the list used to be empty. + * + *----------------------------------------------------------------------- + */ +ReturnStatus +Lst_EnQueue (l, d) + Lst l; + ClientData d; +{ + if (LstValid (l) == FALSE) { + return (FAILURE); + } + + return (Lst_Append (l, Lst_Last(l), d)); +} + diff --git a/bootstrap/bmake/lst.lib/lstFind.c b/bootstrap/bmake/lst.lib/lstFind.c new file mode 100644 index 00000000000..24ef0dfccc4 --- /dev/null +++ b/bootstrap/bmake/lst.lib/lstFind.c @@ -0,0 +1,81 @@ +/* $NetBSD: lstFind.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: lstFind.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstFind.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstFind.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstFind.c -- + * Find a node on a list. + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Find -- + * Find a node on the given list using the given comparison function + * and the given datum. + * + * Results: + * The found node or NILLNODE if none matches. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +LstNode +Lst_Find (l, d, cProc) + Lst l; + ClientData d; + int (*cProc) __P((ClientData, ClientData)); +{ + return (Lst_FindFrom (l, Lst_First(l), d, cProc)); +} + diff --git a/bootstrap/bmake/lst.lib/lstFindFrom.c b/bootstrap/bmake/lst.lib/lstFindFrom.c new file mode 100644 index 00000000000..a402fec8353 --- /dev/null +++ b/bootstrap/bmake/lst.lib/lstFindFrom.c @@ -0,0 +1,105 @@ +/* $NetBSD: lstFindFrom.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: lstFindFrom.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstFindFrom.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstFindFrom.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstFindFrom.c -- + * Find a node on a list from a given starting point. Used by Lst_Find. + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_FindFrom -- + * Search for a node starting and ending with the given one on the + * given list using the passed datum and comparison function to + * determine when it has been found. + * + * Results: + * The found node or NILLNODE + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +LstNode +Lst_FindFrom (l, ln, d, cProc) + Lst l; + register LstNode ln; + register ClientData d; + register int (*cProc) __P((ClientData, ClientData)); +{ + register ListNode tln; + Boolean found = FALSE; + + if (!LstValid (l) || LstIsEmpty (l) || !LstNodeValid (ln, l)) { + return (NILLNODE); + } + + tln = (ListNode)ln; + + do { + if ((*cProc) (tln->datum, d) == 0) { + found = TRUE; + break; + } else { + tln = tln->nextPtr; + } + } while (tln != (ListNode)ln && tln != NilListNode); + + if (found) { + return ((LstNode)tln); + } else { + return (NILLNODE); + } +} + diff --git a/bootstrap/bmake/lst.lib/lstFirst.c b/bootstrap/bmake/lst.lib/lstFirst.c new file mode 100644 index 00000000000..245f632dc4e --- /dev/null +++ b/bootstrap/bmake/lst.lib/lstFirst.c @@ -0,0 +1,82 @@ +/* $NetBSD: lstFirst.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: lstFirst.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstFirst.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstFirst.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstFirst.c -- + * Return the first node of a list + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_First -- + * Return the first node on the given list. + * + * Results: + * The first node or NILLNODE if the list is empty. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +LstNode +Lst_First (l) + Lst l; +{ + if (!LstValid (l) || LstIsEmpty (l)) { + return (NILLNODE); + } else { + return ((LstNode)((List)l)->firstPtr); + } +} + diff --git a/bootstrap/bmake/lst.lib/lstForEach.c b/bootstrap/bmake/lst.lib/lstForEach.c new file mode 100644 index 00000000000..6f919468ba7 --- /dev/null +++ b/bootstrap/bmake/lst.lib/lstForEach.c @@ -0,0 +1,83 @@ +/* $NetBSD: lstForEach.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: lstForEach.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstForEach.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstForEach.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstForeach.c -- + * Perform a given function on all elements of a list. + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_ForEach -- + * Apply the given function to each element of the given list. The + * function should return 0 if Lst_ForEach should continue and non- + * zero if it should abort. + * + * Results: + * None. + * + * Side Effects: + * Only those created by the passed-in function. + * + *----------------------------------------------------------------------- + */ +/*VARARGS2*/ +void +Lst_ForEach (l, proc, d) + Lst l; + register int (*proc) __P((ClientData, ClientData)); + register ClientData d; +{ + Lst_ForEachFrom(l, Lst_First(l), proc, d); +} + diff --git a/bootstrap/bmake/lst.lib/lstForEachFrom.c b/bootstrap/bmake/lst.lib/lstForEachFrom.c new file mode 100644 index 00000000000..66ed9d9b240 --- /dev/null +++ b/bootstrap/bmake/lst.lib/lstForEachFrom.c @@ -0,0 +1,123 @@ +/* $NetBSD: lstForEachFrom.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: lstForEachFrom.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstForEachFrom.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstForEachFrom.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * lstForEachFrom.c -- + * Perform a given function on all elements of a list starting from + * a given point. + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_ForEachFrom -- + * Apply the given function to each element of the given list. The + * function should return 0 if traversal should continue and non- + * zero if it should abort. + * + * Results: + * None. + * + * Side Effects: + * Only those created by the passed-in function. + * + *----------------------------------------------------------------------- + */ +/*VARARGS2*/ +void +Lst_ForEachFrom (l, ln, proc, d) + Lst l; + LstNode ln; + register int (*proc) __P((ClientData, ClientData)); + register ClientData d; +{ + register ListNode tln = (ListNode)ln; + register List list = (List)l; + register ListNode next; + Boolean done; + int result; + + if (!LstValid (list) || LstIsEmpty (list)) { + return; + } + + do { + /* + * Take care of having the current element deleted out from under + * us. + */ + + next = tln->nextPtr; + + (void) tln->useCount++; + result = (*proc) (tln->datum, d); + (void) tln->useCount--; + + /* + * We're done with the traversal if + * - nothing's been added after the current node and + * - the next node to examine is the first in the queue or + * doesn't exist. + */ + done = (next == tln->nextPtr && + (next == NilListNode || next == list->firstPtr)); + + next = tln->nextPtr; + + if (tln->flags & LN_DELETED) { + free((char *)tln); + } + tln = next; + } while (!result && !LstIsEmpty(list) && !done); + +} + diff --git a/bootstrap/bmake/lst.lib/lstInit.c b/bootstrap/bmake/lst.lib/lstInit.c new file mode 100644 index 00000000000..e8fe69afece --- /dev/null +++ b/bootstrap/bmake/lst.lib/lstInit.c @@ -0,0 +1,87 @@ +/* $NetBSD: lstInit.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: lstInit.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstInit.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstInit.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * init.c -- + * Initialize a new linked list. + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Init -- + * Create and initialize a new list. + * + * Results: + * The created list. + * + * Side Effects: + * A list is created, what else? + * + *----------------------------------------------------------------------- + */ +Lst +Lst_Init(circ) + Boolean circ; /* TRUE if the list should be made circular */ +{ + register List nList; + + PAlloc (nList, List); + + nList->firstPtr = NilListNode; + nList->lastPtr = NilListNode; + nList->isOpen = FALSE; + nList->isCirc = circ; + nList->atEnd = Unknown; + + return ((Lst)nList); +} diff --git a/bootstrap/bmake/lst.lib/lstInsert.c b/bootstrap/bmake/lst.lib/lstInsert.c new file mode 100644 index 00000000000..7656418a1f3 --- /dev/null +++ b/bootstrap/bmake/lst.lib/lstInsert.c @@ -0,0 +1,124 @@ +/* $NetBSD: lstInsert.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: lstInsert.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstInsert.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstInsert.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstInsert.c -- + * Insert a new datum before an old one + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Insert -- + * Insert a new node with the given piece of data before the given + * node in the given list. + * + * Results: + * SUCCESS or FAILURE. + * + * Side Effects: + * the firstPtr field will be changed if ln is the first node in the + * list. + * + *----------------------------------------------------------------------- + */ +ReturnStatus +Lst_Insert (l, ln, d) + Lst l; /* list to manipulate */ + LstNode ln; /* node before which to insert d */ + ClientData d; /* datum to be inserted */ +{ + register ListNode nLNode; /* new lnode for d */ + register ListNode lNode = (ListNode)ln; + register List list = (List)l; + + + /* + * check validity of arguments + */ + if (LstValid (l) && (LstIsEmpty (l) && ln == NILLNODE)) + goto ok; + + if (!LstValid (l) || LstIsEmpty (l) || !LstNodeValid (ln, l)) { + return (FAILURE); + } + + ok: + PAlloc (nLNode, ListNode); + + nLNode->datum = d; + nLNode->useCount = nLNode->flags = 0; + + if (ln == NILLNODE) { + if (list->isCirc) { + nLNode->prevPtr = nLNode->nextPtr = nLNode; + } else { + nLNode->prevPtr = nLNode->nextPtr = NilListNode; + } + list->firstPtr = list->lastPtr = nLNode; + } else { + nLNode->prevPtr = lNode->prevPtr; + nLNode->nextPtr = lNode; + + if (nLNode->prevPtr != NilListNode) { + nLNode->prevPtr->nextPtr = nLNode; + } + lNode->prevPtr = nLNode; + + if (lNode == list->firstPtr) { + list->firstPtr = nLNode; + } + } + + return (SUCCESS); +} + diff --git a/bootstrap/bmake/lst.lib/lstInt.h b/bootstrap/bmake/lst.lib/lstInt.h new file mode 100644 index 00000000000..718f9faff1a --- /dev/null +++ b/bootstrap/bmake/lst.lib/lstInt.h @@ -0,0 +1,113 @@ +/* $NetBSD: lstInt.h,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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: @(#)lstInt.h 8.1 (Berkeley) 6/6/93 + */ + +/*- + * lstInt.h -- + * Internals for the list library + */ +#ifndef _LSTINT_H_ +#define _LSTINT_H_ + +#include "make.h" +#include "lst.h" + +typedef struct ListNode { + struct ListNode *prevPtr; /* previous element in list */ + struct ListNode *nextPtr; /* next in list */ + short useCount:8, /* Count of functions using the node. + * node may not be deleted until count + * goes to 0 */ + flags:8; /* Node status flags */ + ClientData datum; /* datum associated with this element */ +} *ListNode; +/* + * Flags required for synchronization + */ +#define LN_DELETED 0x0001 /* List node should be removed when done */ + +#define NilListNode ((ListNode)-1) + +typedef enum { + Head, Middle, Tail, Unknown +} Where; + +typedef struct { + ListNode firstPtr; /* first node in list */ + ListNode lastPtr; /* last node in list */ + Boolean isCirc; /* true if the list should be considered + * circular */ +/* + * fields for sequential access + */ + Where atEnd; /* Where in the list the last access was */ + Boolean isOpen; /* true if list has been Lst_Open'ed */ + ListNode curPtr; /* current node, if open. NilListNode if + * *just* opened */ + ListNode prevPtr; /* Previous node, if open. Used by + * Lst_Remove */ +} *List; + +#define NilList ((List)-1) + +/* + * PAlloc (var, ptype) -- + * Allocate a pointer-typedef structure 'ptype' into the variable 'var' + */ +#define PAlloc(var,ptype) var = (ptype) emalloc (sizeof (*var)) + +/* + * LstValid (l) -- + * Return TRUE if the list l is valid + */ +#define LstValid(l) (((Lst)l == NILLST) ? FALSE : TRUE) + +/* + * LstNodeValid (ln, l) -- + * Return TRUE if the LstNode ln is valid with respect to l + */ +#define LstNodeValid(ln, l) ((((LstNode)ln) == NILLNODE) ? FALSE : TRUE) + +/* + * LstIsEmpty (l) -- + * TRUE if the list l is empty. + */ +#define LstIsEmpty(l) (((List)l)->firstPtr == NilListNode) + +#endif /* _LSTINT_H_ */ diff --git a/bootstrap/bmake/lst.lib/lstIsAtEnd.c b/bootstrap/bmake/lst.lib/lstIsAtEnd.c new file mode 100644 index 00000000000..6fd9714e0c6 --- /dev/null +++ b/bootstrap/bmake/lst.lib/lstIsAtEnd.c @@ -0,0 +1,92 @@ +/* $NetBSD: lstIsAtEnd.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: lstIsAtEnd.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstIsAtEnd.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstIsAtEnd.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstIsAtEnd.c -- + * Tell if the current node is at the end of the list. + * The sequential functions access the list in a slightly different way. + * CurPtr points to their idea of the current node in the list and they + * access the list based on it. Because the list is circular, Lst_Next + * and Lst_Prev will go around the list forever. Lst_IsAtEnd must be + * used to determine when to stop. + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_IsAtEnd -- + * Return true if have reached the end of the given list. + * + * Results: + * TRUE if at the end of the list (this includes the list not being + * open or being invalid) or FALSE if not. We return TRUE if the list + * is invalid or unopend so as to cause the caller to exit its loop + * asap, the assumption being that the loop is of the form + * while (!Lst_IsAtEnd (l)) { + * ... + * } + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +Boolean +Lst_IsAtEnd (l) + Lst l; +{ + register List list = (List) l; + + return (!LstValid (l) || !list->isOpen || + (list->atEnd == Head) || (list->atEnd == Tail)); +} + diff --git a/bootstrap/bmake/lst.lib/lstIsEmpty.c b/bootstrap/bmake/lst.lib/lstIsEmpty.c new file mode 100644 index 00000000000..dc2a5cf2b27 --- /dev/null +++ b/bootstrap/bmake/lst.lib/lstIsEmpty.c @@ -0,0 +1,80 @@ +/* $NetBSD: lstIsEmpty.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: lstIsEmpty.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstIsEmpty.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstIsEmpty.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstIsEmpty.c -- + * A single function to decide if a list is empty + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_IsEmpty -- + * Return TRUE if the given list is empty. + * + * Results: + * TRUE if the list is empty, FALSE otherwise. + * + * Side Effects: + * None. + * + * A list is considered empty if its firstPtr == NilListNode (or if + * the list itself is NILLIST). + *----------------------------------------------------------------------- + */ +Boolean +Lst_IsEmpty (l) + Lst l; +{ + return ( ! LstValid (l) || LstIsEmpty(l)); +} + diff --git a/bootstrap/bmake/lst.lib/lstLast.c b/bootstrap/bmake/lst.lib/lstLast.c new file mode 100644 index 00000000000..e091ebb36fe --- /dev/null +++ b/bootstrap/bmake/lst.lib/lstLast.c @@ -0,0 +1,82 @@ +/* $NetBSD: lstLast.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: lstLast.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstLast.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstLast.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstLast.c -- + * Return the last element of a list + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Last -- + * Return the last node on the list l. + * + * Results: + * The requested node or NILLNODE if the list is empty. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +LstNode +Lst_Last (l) + Lst l; +{ + if (!LstValid(l) || LstIsEmpty (l)) { + return (NILLNODE); + } else { + return ((LstNode)((List)l)->lastPtr); + } +} + diff --git a/bootstrap/bmake/lst.lib/lstMember.c b/bootstrap/bmake/lst.lib/lstMember.c new file mode 100644 index 00000000000..4847bb23924 --- /dev/null +++ b/bootstrap/bmake/lst.lib/lstMember.c @@ -0,0 +1,80 @@ +/* $NetBSD: lstMember.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: lstMember.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstMember.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstMember.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * lstMember.c -- + * See if a given datum is on a given list. + */ + +#include "lstInt.h" + +LstNode +Lst_Member (l, d) + Lst l; + ClientData d; +{ + List list = (List) l; + register ListNode lNode; + + lNode = list->firstPtr; + if (lNode == NilListNode) { + return NILLNODE; + } + + do { + if (lNode->datum == d) { + return (LstNode)lNode; + } + lNode = lNode->nextPtr; + } while (lNode != NilListNode && lNode != list->firstPtr); + + return NILLNODE; +} diff --git a/bootstrap/bmake/lst.lib/lstNext.c b/bootstrap/bmake/lst.lib/lstNext.c new file mode 100644 index 00000000000..c98bff2c0ab --- /dev/null +++ b/bootstrap/bmake/lst.lib/lstNext.c @@ -0,0 +1,125 @@ +/* $NetBSD: lstNext.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: lstNext.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstNext.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstNext.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstNext.c -- + * Return the next node for a list. + * The sequential functions access the list in a slightly different way. + * CurPtr points to their idea of the current node in the list and they + * access the list based on it. Because the list is circular, Lst_Next + * and Lst_Prev will go around the list forever. Lst_IsAtEnd must be + * used to determine when to stop. + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Next -- + * Return the next node for the given list. + * + * Results: + * The next node or NILLNODE if the list has yet to be opened. Also + * if the list is non-circular and the end has been reached, NILLNODE + * is returned. + * + * Side Effects: + * the curPtr field is updated. + * + *----------------------------------------------------------------------- + */ +LstNode +Lst_Next (l) + Lst l; +{ + register ListNode tln; + register List list = (List)l; + + if ((LstValid (l) == FALSE) || + (list->isOpen == FALSE)) { + return (NILLNODE); + } + + list->prevPtr = list->curPtr; + + if (list->curPtr == NilListNode) { + if (list->atEnd == Unknown) { + /* + * If we're just starting out, atEnd will be Unknown. + * Then we want to start this thing off in the right + * direction -- at the start with atEnd being Middle. + */ + list->curPtr = tln = list->firstPtr; + list->atEnd = Middle; + } else { + tln = NilListNode; + list->atEnd = Tail; + } + } else { + tln = list->curPtr->nextPtr; + list->curPtr = tln; + + if (tln == list->firstPtr || tln == NilListNode) { + /* + * If back at the front, then we've hit the end... + */ + list->atEnd = Tail; + } else { + /* + * Reset to Middle if gone past first. + */ + list->atEnd = Middle; + } + } + + return ((LstNode)tln); +} + diff --git a/bootstrap/bmake/lst.lib/lstOpen.c b/bootstrap/bmake/lst.lib/lstOpen.c new file mode 100644 index 00000000000..401b9a896ac --- /dev/null +++ b/bootstrap/bmake/lst.lib/lstOpen.c @@ -0,0 +1,92 @@ +/* $NetBSD: lstOpen.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: lstOpen.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstOpen.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstOpen.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstOpen.c -- + * Open a list for sequential access. The sequential functions access the + * list in a slightly different way. CurPtr points to their idea of the + * current node in the list and they access the list based on it. + * If the list is circular, Lst_Next and Lst_Prev will go around + * the list forever. Lst_IsAtEnd must be used to determine when to stop. + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Open -- + * Open a list for sequential access. A list can still be searched, + * etc., without confusing these functions. + * + * Results: + * SUCCESS or FAILURE. + * + * Side Effects: + * isOpen is set TRUE and curPtr is set to NilListNode so the + * other sequential functions no it was just opened and can choose + * the first element accessed based on this. + * + *----------------------------------------------------------------------- + */ +ReturnStatus +Lst_Open (l) + register Lst l; +{ + if (LstValid (l) == FALSE) { + return (FAILURE); + } + ((List) l)->isOpen = TRUE; + ((List) l)->atEnd = LstIsEmpty (l) ? Head : Unknown; + ((List) l)->curPtr = NilListNode; + + return (SUCCESS); +} + diff --git a/bootstrap/bmake/lst.lib/lstRemove.c b/bootstrap/bmake/lst.lib/lstRemove.c new file mode 100644 index 00000000000..9790fc8ce96 --- /dev/null +++ b/bootstrap/bmake/lst.lib/lstRemove.c @@ -0,0 +1,142 @@ +/* $NetBSD: lstRemove.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: lstRemove.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstRemove.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstRemove.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstRemove.c -- + * Remove an element from a list + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Remove -- + * Remove the given node from the given list. + * + * Results: + * SUCCESS or FAILURE. + * + * Side Effects: + * The list's firstPtr will be set to NilListNode if ln is the last + * node on the list. firsPtr and lastPtr will be altered if ln is + * either the first or last node, respectively, on the list. + * + *----------------------------------------------------------------------- + */ +ReturnStatus +Lst_Remove (l, ln) + Lst l; + LstNode ln; +{ + register List list = (List) l; + register ListNode lNode = (ListNode) ln; + + if (!LstValid (l) || + !LstNodeValid (ln, l)) { + return (FAILURE); + } + + /* + * unlink it from the list + */ + if (lNode->nextPtr != NilListNode) { + lNode->nextPtr->prevPtr = lNode->prevPtr; + } + if (lNode->prevPtr != NilListNode) { + lNode->prevPtr->nextPtr = lNode->nextPtr; + } + + /* + * if either the firstPtr or lastPtr of the list point to this node, + * adjust them accordingly + */ + if (list->firstPtr == lNode) { + list->firstPtr = lNode->nextPtr; + } + if (list->lastPtr == lNode) { + list->lastPtr = lNode->prevPtr; + } + + /* + * Sequential access stuff. If the node we're removing is the current + * node in the list, reset the current node to the previous one. If the + * previous one was non-existent (prevPtr == NilListNode), we set the + * end to be Unknown, since it is. + */ + if (list->isOpen && (list->curPtr == lNode)) { + list->curPtr = list->prevPtr; + if (list->curPtr == NilListNode) { + list->atEnd = Unknown; + } + } + + /* + * the only way firstPtr can still point to ln is if ln is the last + * node on the list (the list is circular, so lNode->nextptr == lNode in + * this case). The list is, therefore, empty and is marked as such + */ + if (list->firstPtr == lNode) { + list->firstPtr = NilListNode; + } + + /* + * note that the datum is unmolested. The caller must free it as + * necessary and as expected. + */ + if (lNode->useCount == 0) { + free ((Address)ln); + } else { + lNode->flags |= LN_DELETED; + } + + return (SUCCESS); +} + diff --git a/bootstrap/bmake/lst.lib/lstReplace.c b/bootstrap/bmake/lst.lib/lstReplace.c new file mode 100644 index 00000000000..2b06b25caa1 --- /dev/null +++ b/bootstrap/bmake/lst.lib/lstReplace.c @@ -0,0 +1,84 @@ +/* $NetBSD: lstReplace.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: lstReplace.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstReplace.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstReplace.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstReplace.c -- + * Replace the datum in a node with a new datum + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Replace -- + * Replace the datum in the given node with the new datum + * + * Results: + * SUCCESS or FAILURE. + * + * Side Effects: + * The datum field fo the node is altered. + * + *----------------------------------------------------------------------- + */ +ReturnStatus +Lst_Replace (ln, d) + register LstNode ln; + ClientData d; +{ + if (ln == NILLNODE) { + return (FAILURE); + } else { + ((ListNode) ln)->datum = d; + return (SUCCESS); + } +} + diff --git a/bootstrap/bmake/lst.lib/lstSucc.c b/bootstrap/bmake/lst.lib/lstSucc.c new file mode 100644 index 00000000000..6c3375407d7 --- /dev/null +++ b/bootstrap/bmake/lst.lib/lstSucc.c @@ -0,0 +1,84 @@ +/* $NetBSD: lstSucc.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: lstSucc.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstSucc.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstSucc.c,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstSucc.c -- + * return the successor to a given node + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Succ -- + * Return the sucessor to the given node on its list. + * + * Results: + * The successor of the node, if it exists (note that on a circular + * list, if the node is the only one in the list, it is its own + * successor). + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +LstNode +Lst_Succ (ln) + LstNode ln; +{ + if (ln == NILLNODE) { + return (NILLNODE); + } else { + return ((LstNode) ((ListNode) ln)->nextPtr); + } +} + diff --git a/bootstrap/bmake/lst.lib/makefile.boot.in b/bootstrap/bmake/lst.lib/makefile.boot.in new file mode 100644 index 00000000000..ebb29195412 --- /dev/null +++ b/bootstrap/bmake/lst.lib/makefile.boot.in @@ -0,0 +1,45 @@ +# RCSid: +# $Id: makefile.boot.in,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $ + +srcdir=@srcdir@ +VPATH=.:$(srcdir) + +OBJ=lstAppend.o lstDupl.o lstInit.o lstOpen.o lstAtEnd.o lstEnQueue.o \ + lstInsert.o lstAtFront.o lstIsAtEnd.o lstClose.o lstFind.o lstIsEmpty.o \ + lstRemove.o lstConcat.o lstFindFrom.o lstLast.o lstReplace.o lstFirst.o \ + lstDatum.o lstForEach.o lstMember.o lstSucc.o lstDeQueue.o \ + lstForEachFrom.o lstDestroy.o lstNext.o + +CFLAGS=-I.. +all: ${OBJ} + mv *.o .. + +depend: + VPATH=${VPATH} ${MKDEP} -f makefile.boot ${CFLAGS} ${OBJ:.o=.c} + +#lstAppend.o: $(srcdir)/lstAppend.c +#lstDupl.o: $(srcdir)/lstDupl.c +#lstInit.o: $(srcdir)/lstInit.c +#lstOpen.o: $(srcdir)/lstOpen.c +#lstAtEnd.o: $(srcdir)/lstAtEnd.c +#lstEnQueue.o: $(srcdir)/lstEnQueue.c +#lstInsert.o: $(srcdir)/lstInsert.c +#lstAtFront.o: $(srcdir)/lstAtFront.c +#lstIsAtEnd.o: $(srcdir)/lstIsAtEnd.c +#lstClose.o: $(srcdir)/lstClose.c +#lstFind.o: $(srcdir)/lstFind.c +#lstIsEmpty.o: $(srcdir)/lstIsEmpty.c +#lstRemove.o: $(srcdir)/lstRemove.c +#lstConcat.o: $(srcdir)/lstConcat.c +#lstFindFrom.o: $(srcdir)/lstFindFrom.c +#lstLast.o: $(srcdir)/lstLast.c +#lstReplace.o: $(srcdir)/lstReplace.c +#lstFirst.o: $(srcdir)/lstFirst.c +#lstDatum.o: $(srcdir)/lstDatum.c +#lstForEach.o: $(srcdir)/lstForEach.c +#lstMember.o: $(srcdir)/lstMember.c +#lstSucc.o: $(srcdir)/lstSucc.c +#lstDeQueue.o: $(srcdir)/lstDeQueue.c +#lstForEachFrom.o: $(srcdir)/lstForEachFrom.c +#lstDestroy.o: $(srcdir)/lstDestroy.c +#lstNext.o: $(srcdir)/lstNext.c diff --git a/bootstrap/bmake/machine.sh b/bootstrap/bmake/machine.sh new file mode 100755 index 00000000000..14f198f687f --- /dev/null +++ b/bootstrap/bmake/machine.sh @@ -0,0 +1,80 @@ +: +# derrived from /etc/rc_d/os.sh + +# RCSid: +# $Id: machine.sh,v 1.1.1.1 2004/03/11 13:04:08 grant Exp $ +# +# @(#) Copyright (c) 1994 Simon J. Gerraty +# +# This file is provided in the hope that it will +# be of use. There is absolutely NO WARRANTY. +# Permission to copy, redistribute or otherwise +# use this file is hereby granted provided that +# the above copyright notice and this notice are +# left intact. +# +# Please send copies of changes and bug-fixes to: +# sjg@quick.com.au +# + +OS=`uname` +OSREL=`uname -r` +OSMAJOR=`IFS=.; set $OSREL; echo $1` +machine=`uname -m` +MACHINE= + +# Great! Solaris keeps moving arch(1) +# we need this here, and it is not always available... +Which() { + for d in `IFS=:; echo ${2:-$PATH}` + do + test -x $d/$1 && { echo $d/$1; break; } + done +} + +arch=`Which arch /usr/bin:/usr/ucb:$PATH` +test "$arch" && MACHINE_ARCH=`$arch` + +case $OS in +OpenBSD) + MACHINE=$OS$OSMAJOR.$machine + MACHINE_ARCH=`$arch -s`; + ;; +*BSD) + MACHINE=$OS$OSMAJOR.$machine + ;; +SunOS) + MACHINE=$OS$OSMAJOR + case "$MACHINE_ARCH" in + sparc|sun4*) ;; + sun386*) MACHINE=$MACHINE_ARCH;; + *) MACHINE=$MACHINE.$machine;; + esac + ;; +HP-UX) + MACHINE_ARCH=`IFS="/-."; set $machine; echo $1` + ;; +IRIX) + MACHINE_ARCH=`uname -p 2>/dev/null` + ;; +Interix) + MACHINE=i386 + MACHINE_ARCH=i386 + ;; +esac + +MACHINE=${MACHINE:-$OS$OSMAJOR} +MACHINE_ARCH=${MACHINE_ARCH:-$machine} + + +( +case "$0" in +arch*) echo $MACHINE_ARCH;; +*) + case "$1" in + "") echo $MACHINE;; + *) echo $MACHINE_ARCH;; + esac + ;; +esac +) | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz diff --git a/bootstrap/bmake/main.c b/bootstrap/bmake/main.c new file mode 100644 index 00000000000..4de0cede325 --- /dev/null +++ b/bootstrap/bmake/main.c @@ -0,0 +1,1743 @@ +/* $NetBSD: main.c,v 1.1.1.1 2004/03/11 13:04:10 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 MAKE_BOOTSTRAP +static char rcsid[] = "$NetBSD: main.c,v 1.1.1.1 2004/03/11 13:04:10 grant Exp $"; +#else +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)main.c 8.3 (Berkeley) 3/19/94"; +#else +__RCSID("$NetBSD: main.c,v 1.1.1.1 2004/03/11 13:04:10 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +#if !defined(MAKE_BOOTSTRAP) && !defined(lint) +__IDSTRING(rcs_id,"$Id: main.c,v 1.1.1.1 2004/03/11 13:04:10 grant Exp $"); +#endif + +/*- + * main.c -- + * The main file for this entire program. Exit routines etc + * reside here. + * + * Utility functions defined in this file: + * Main_ParseArgLine Takes a line of arguments, breaks them and + * treats them as if they were given when first + * invoked. Used by the parse module to implement + * the .MFLAGS target. + * + * Error Print a tagged error message. The global + * MAKE variable must have been defined. This + * takes a format string and two optional + * arguments for it. + * + * Fatal Print an error message and exit. Also takes + * a format string and two arguments. + * + * Punt Aborts all jobs and exits with a message. Also + * takes a format string and two arguments. + * + * Finish Finish things up by printing the number of + * errors which occurred, as passed to it, and + * exiting. + */ + +#include +#include +#include +#include +#include +#include +#ifndef MAKE_BOOTSTRAP +#include +#endif +#include +#include +#include +#include +#include +#ifdef __STDC__ +#include +#include +#else +#include +#endif +#include "make.h" +#include "hash.h" +#include "dir.h" +#include "job.h" +#include "pathnames.h" +#include "trace.h" + +#ifdef USE_IOVEC +#include +#endif + +#ifndef DEFMAXLOCAL +#define DEFMAXLOCAL DEFMAXJOBS +#endif /* DEFMAXLOCAL */ + +Lst create; /* Targets to be made */ +time_t now; /* Time at start of make */ +GNode *DEFAULT; /* .DEFAULT node */ +Boolean allPrecious; /* .PRECIOUS given on line by itself */ + +static Boolean noBuiltins; /* -r flag */ +static Lst makefiles; /* ordered list of makefiles to read */ +static Boolean printVars; /* print value of one or more vars */ +static Lst variables; /* list of variables to print */ +int maxJobs; /* -j argument */ +static int maxLocal; /* -L argument */ +Boolean compatMake; /* -B argument */ +Boolean debug; /* -d flag */ +Boolean noExecute; /* -n flag */ +Boolean noRecursiveExecute; /* -N flag */ +Boolean keepgoing; /* -k flag */ +Boolean queryFlag; /* -q flag */ +Boolean touchFlag; /* -t flag */ +Boolean usePipes; /* !-P flag */ +Boolean ignoreErrors; /* -i flag */ +Boolean beSilent; /* -s flag */ +Boolean oldVars; /* variable substitution style */ +Boolean checkEnvFirst; /* -e flag */ +Boolean parseWarnFatal; /* -W flag */ +Boolean jobServer; /* -J flag */ +static Boolean jobsRunning; /* TRUE if the jobs might be running */ +static const char * tracefile; +#ifndef NO_CHECK_MAKE_CHDIR +static char * Check_Cwd_av __P((int, char **, int)); +#endif +static void MainParseArgs __P((int, char **)); +char * chdir_verify_path __P((char *, char *)); +static int ReadMakefile __P((ClientData, ClientData)); +static void usage __P((void)); + +static char *curdir; /* startup directory */ +static char *objdir; /* where we chdir'ed to */ +char *progname; /* the program name */ + +Boolean forceJobs = FALSE; + +/* + * On some systems MACHINE is defined as something other than + * what we want. + */ +#ifdef FORCE_MACHINE +# undef MACHINE +# define MACHINE FORCE_MACHINE +#endif + +extern Lst parseIncPath; + +/*- + * MainParseArgs -- + * Parse a given argument vector. Called from main() and from + * Main_ParseArgLine() when the .MAKEFLAGS target is used. + * + * XXX: Deal with command line overriding .MAKEFLAGS in makefile + * + * Results: + * None + * + * Side Effects: + * Various global and local flags will be set depending on the flags + * given + */ +static void +MainParseArgs(argc, argv) + int argc; + char **argv; +{ + char *p; + int c; + + if (argv[0] == 0) + argv[0] = progname; /* avoid problems in getopt */ + + optind = 1; /* since we're called more than once */ +#ifdef REMOTE +# define OPTFLAGS "BD:I:J:L:NPST:V:Wd:ef:ij:km:nqrst" +#else +# define OPTFLAGS "BD:I:J:NPST:V:Wd:ef:ij:km:nqrst" +#endif +rearg: while((c = getopt(argc, argv, OPTFLAGS)) != -1) { + switch(c) { + case 'D': + Var_Set(optarg, "1", VAR_GLOBAL, 0); + Var_Append(MAKEFLAGS, "-D", VAR_GLOBAL); + Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL); + break; + case 'I': + Parse_AddIncludeDir(optarg); + Var_Append(MAKEFLAGS, "-I", VAR_GLOBAL); + Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL); + break; + case 'J': + if (sscanf(optarg, "%d,%d", &job_pipe[0], &job_pipe[1]) != 2) { + /* backslash to avoid trigraph ??) */ + (void)fprintf(stderr, + "%s: internal error -- J option malformed (%s?\?)\n", + progname, optarg); + usage(); + } + if ((fcntl(job_pipe[0], F_GETFD, 0) < 0) || + (fcntl(job_pipe[1], F_GETFD, 0) < 0)) { +#if 0 + (void)fprintf(stderr, + "%s: warning -- J descriptors were closed!\n", + progname); +#endif + job_pipe[0] = -1; + job_pipe[1] = -1; + compatMake = TRUE; + } else { + Var_Append(MAKEFLAGS, "-J", VAR_GLOBAL); + Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL); + jobServer = TRUE; + } + break; + case 'V': + printVars = TRUE; + (void)Lst_AtEnd(variables, (ClientData)optarg); + Var_Append(MAKEFLAGS, "-V", VAR_GLOBAL); + Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL); + break; + case 'B': + compatMake = TRUE; + break; +#ifdef REMOTE + case 'L': + maxLocal = strtol(optarg, &p, 0); + if (*p != '\0' || maxLocal < 1) { + (void) fprintf(stderr, "%s: illegal argument to -L -- must be positive integer!\n", + progname); + exit(1); + } + Var_Append(MAKEFLAGS, "-L", VAR_GLOBAL); + Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL); + break; +#endif + case 'N': + noExecute = TRUE; + noRecursiveExecute = TRUE; + Var_Append(MAKEFLAGS, "-N", VAR_GLOBAL); + break; + case 'P': + usePipes = FALSE; + Var_Append(MAKEFLAGS, "-P", VAR_GLOBAL); + break; + case 'S': + keepgoing = FALSE; + Var_Append(MAKEFLAGS, "-S", VAR_GLOBAL); + break; + case 'T': + tracefile = estrdup(optarg); + Var_Append(MAKEFLAGS, "-T", VAR_GLOBAL); + Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL); + break; + case 'W': + parseWarnFatal = TRUE; + break; + case 'd': { + char *modules = optarg; + + for (; *modules; ++modules) + switch (*modules) { + case 'A': + debug = ~0; + break; + case 'a': + debug |= DEBUG_ARCH; + break; + case 'c': + debug |= DEBUG_COND; + break; + case 'd': + debug |= DEBUG_DIR; + break; + case 'f': + debug |= DEBUG_FOR; + break; + case 'g': + if (modules[1] == '1') { + debug |= DEBUG_GRAPH1; + ++modules; + } + else if (modules[1] == '2') { + debug |= DEBUG_GRAPH2; + ++modules; + } + break; + case 'j': + debug |= DEBUG_JOB; + break; + case 'm': + debug |= DEBUG_MAKE; + break; + case 's': + debug |= DEBUG_SUFF; + break; + case 't': + debug |= DEBUG_TARG; + break; + case 'v': + debug |= DEBUG_VAR; + break; + case 'x': + debug |= DEBUG_SHELL; + break; + default: + (void)fprintf(stderr, + "%s: illegal argument to d option -- %c\n", + progname, *modules); + usage(); + } + Var_Append(MAKEFLAGS, "-d", VAR_GLOBAL); + Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL); + break; + } + case 'e': + checkEnvFirst = TRUE; + Var_Append(MAKEFLAGS, "-e", VAR_GLOBAL); + break; + case 'f': + (void)Lst_AtEnd(makefiles, (ClientData)optarg); + break; + case 'i': + ignoreErrors = TRUE; + Var_Append(MAKEFLAGS, "-i", VAR_GLOBAL); + break; + case 'j': + forceJobs = TRUE; + maxJobs = strtol(optarg, &p, 0); + if (*p != '\0' || maxJobs < 1) { + (void) fprintf(stderr, "%s: illegal argument to -j -- must be positive integer!\n", + progname); + exit(1); + } +#ifndef REMOTE + maxLocal = maxJobs; +#endif + Var_Append(MAKEFLAGS, "-j", VAR_GLOBAL); + Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL); + break; + case 'k': + keepgoing = TRUE; + Var_Append(MAKEFLAGS, "-k", VAR_GLOBAL); + break; + case 'm': + (void) Dir_AddDir(sysIncPath, optarg); + Var_Append(MAKEFLAGS, "-m", VAR_GLOBAL); + Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL); + break; + case 'n': + noExecute = TRUE; + Var_Append(MAKEFLAGS, "-n", VAR_GLOBAL); + break; + case 'q': + queryFlag = TRUE; + /* Kind of nonsensical, wot? */ + Var_Append(MAKEFLAGS, "-q", VAR_GLOBAL); + break; + case 'r': + noBuiltins = TRUE; + Var_Append(MAKEFLAGS, "-r", VAR_GLOBAL); + break; + case 's': + beSilent = TRUE; + Var_Append(MAKEFLAGS, "-s", VAR_GLOBAL); + break; + case 't': + touchFlag = TRUE; + Var_Append(MAKEFLAGS, "-t", VAR_GLOBAL); + break; + default: + case '?': +#ifdef MAKE_BOOTSTRAP + fprintf(stderr, "getopt(%s) -> %d (%c)\n", + OPTFLAGS, c, c); +#endif + usage(); + } + } + + oldVars = TRUE; + + /* + * See if the rest of the arguments are variable assignments and + * perform them if so. Else take them to be targets and stuff them + * on the end of the "create" list. + */ + for (argv += optind, argc -= optind; *argv; ++argv, --argc) + if (Parse_IsVar(*argv)) { + Parse_DoVar(*argv, VAR_CMD); + } else { + if (!**argv) + Punt("illegal (null) argument."); + if (**argv == '-') { + if ((*argv)[1]) + optind = 0; /* -flag... */ + else + optind = 1; /* - */ + goto rearg; + } + (void)Lst_AtEnd(create, (ClientData)estrdup(*argv)); + } +} + +/*- + * Main_ParseArgLine -- + * Used by the parse module when a .MFLAGS or .MAKEFLAGS target + * is encountered and by main() when reading the .MAKEFLAGS envariable. + * Takes a line of arguments and breaks it into its + * component words and passes those words and the number of them to the + * MainParseArgs function. + * The line should have all its leading whitespace removed. + * + * Results: + * None + * + * Side Effects: + * Only those that come from the various arguments. + */ +void +Main_ParseArgLine(line) + char *line; /* Line to fracture */ +{ + char **argv; /* Manufactured argument vector */ + int argc; /* Number of arguments in argv */ + char *args; /* Space used by the args */ + char *buf, *p1; + char *argv0 = Var_Value(".MAKE", VAR_GLOBAL, &p1); + size_t len; + + if (line == NULL) + return; + for (; *line == ' '; ++line) + continue; + if (!*line) + return; + +#ifndef POSIX + { + /* + * $MAKE may simply be naming the make(1) binary + */ + char *cp; + + if (!(cp = strrchr(line, '/'))) + cp = line; + if ((cp = strstr(cp, "make")) && + strcmp(cp, "make") == 0) + return; + } +#endif + buf = emalloc(len = strlen(line) + strlen(argv0) + 2); + (void)snprintf(buf, len, "%s %s", argv0, line); + if (p1) + free(p1); + + argv = brk_string(buf, &argc, TRUE, &args); + free(buf); + MainParseArgs(argc, argv); + + free(args); + free(argv); +} + +char * +chdir_verify_path(path, obpath) + char *path; + char *obpath; +{ + struct stat sb; + + if (strchr(path, '$') != 0) { + path = Var_Subst(NULL, path, VAR_GLOBAL, 0); + } + if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) { + if (chdir(path)) { + (void)fprintf(stderr, "make warning: %s: %s.\n", + path, strerror(errno)); + return 0; + } + else { + if (path[0] != '/') { + (void) snprintf(obpath, MAXPATHLEN, "%s/%s", + curdir, path); + return obpath; + } + else + return path; + } + } + + return 0; +} + + +/*- + * main -- + * The main function, for obvious reasons. Initializes variables + * and a few modules, then parses the arguments give it in the + * environment and on the command line. Reads the system makefile + * followed by either Makefile, makefile or the file given by the + * -f argument. Sets the .MAKEFLAGS PMake variable based on all the + * flags it has received by then uses either the Make or the Compat + * module to create the initial list of targets. + * + * Results: + * If -q was given, exits -1 if anything was out-of-date. Else it exits + * 0. + * + * Side Effects: + * The program exits when done. Targets are created. etc. etc. etc. + */ +int +main(argc, argv) + int argc; + char **argv; +{ + Lst targs; /* target nodes to create -- passed to Make_Init */ + Boolean outOfDate = TRUE; /* FALSE if all targets up to date */ + struct stat sb, sa; + char *p1, *path, *pathp, *pwd, *getenv(), *getcwd(); + char mdpath[MAXPATHLEN + 1]; + char obpath[MAXPATHLEN + 1]; + char cdpath[MAXPATHLEN + 1]; +#ifdef FORCE_MACHINE + char *machine = FORCE_MACHINE; +#else + char *machine = getenv("MACHINE"); +#endif + char *machine_arch = getenv("MACHINE_ARCH"); + char *syspath = getenv("MAKESYSPATH"); + Lst sysMkPath; /* Path of sys.mk */ + char *cp = NULL, *start; + /* avoid faults on read-only strings */ + static char defsyspath[] = _PATH_DEFSYSPATH; + + if ((progname = strrchr(argv[0], '/')) != NULL) + progname++; + else + progname = argv[0]; + +#ifdef RLIMIT_NOFILE + /* + * get rid of resource limit on file descriptors + */ + { + struct rlimit rl; + if (getrlimit(RLIMIT_NOFILE, &rl) != -1 && + rl.rlim_cur != rl.rlim_max) { + rl.rlim_cur = rl.rlim_max; + (void) setrlimit(RLIMIT_NOFILE, &rl); + } + } +#endif + /* + * Find where we are and take care of PWD for the automounter... + * All this code is so that we know where we are when we start up + * on a different machine with pmake. + */ + curdir = cdpath; + if (getcwd(curdir, MAXPATHLEN) == NULL) { + (void)fprintf(stderr, "%s: %s.\n", progname, strerror(errno)); + exit(2); + } + + if (stat(curdir, &sa) == -1) { + (void)fprintf(stderr, "%s: %s: %s.\n", + progname, curdir, strerror(errno)); + exit(2); + } + + /* + * Overriding getcwd() with $PWD totally breaks MAKEOBJDIRPREFIX + * since the value of curdir can very depending on how we got + * here. Ie sitting at a shell prompt (shell that provides $PWD) + * or via subdir.mk in which case its likely a shell which does + * not provide it. + * So, to stop it breaking this case only, we ignore PWD if + * MAKEOBJDIRPREFIX is set. + */ +#ifndef NO_PWD_OVERRIDE + if ((pwd = getenv("PWD")) != NULL && + getenv("MAKEOBJDIRPREFIX") == NULL) { + if (stat(pwd, &sb) == 0 && sa.st_ino == sb.st_ino && + sa.st_dev == sb.st_dev) + (void) strcpy(curdir, pwd); + } +#endif + /* + * Get the name of this type of MACHINE from utsname + * so we can share an executable for similar machines. + * (i.e. m68k: amiga hp300, mac68k, sun3, ...) + * + * Note that both MACHINE and MACHINE_ARCH are decided at + * run-time. + */ + if (!machine) { +#ifndef MAKE_BOOTSTRAP + struct utsname utsname; + + if (uname(&utsname) == -1) { + (void)fprintf(stderr, "%s: uname failed (%s).\n", progname, + strerror(errno)); + exit(2); + } + machine = utsname.machine; +#else + machine = MACHINE; +#endif + } + + if (!machine_arch) { +#ifndef MACHINE_ARCH +#ifdef __ARCHITECTURE__ + machine_arch = __ARCHITECTURE__; +#else + machine_arch = "unknown"; /* XXX: no uname -p yet */ +#endif +#else + machine_arch = MACHINE_ARCH; +#endif + } + + /* + * Just in case MAKEOBJDIR wants us to do something tricky. + */ + Var_Init(); /* Initialize the lists of variables for + * parsing arguments */ + Var_Set(".CURDIR", curdir, VAR_GLOBAL, 0); + Var_Set("MACHINE", machine, VAR_GLOBAL, 0); + Var_Set("MACHINE_ARCH", machine_arch, VAR_GLOBAL, 0); +#ifdef MAKE_VERSION + Var_Set("MAKE_VERSION", MAKE_VERSION, VAR_GLOBAL, 0); +#endif + Var_Set(".newline", "\n", VAR_GLOBAL, 0); /* handy for :@ loops */ + + /* + * If the MAKEOBJDIR (or by default, the _PATH_OBJDIR) directory + * exists, change into it and build there. (If a .${MACHINE} suffix + * exists, use that directory instead). + * Otherwise check MAKEOBJDIRPREFIX`cwd` (or by default, + * _PATH_OBJDIRPREFIX`cwd`) and build there if it exists. + * If all fails, use the current directory to build. + * + * Once things are initted, + * have to add the original directory to the search path, + * and modify the paths for the Makefiles apropriately. The + * current directory is also placed as a variable for make scripts. + */ + if (!(pathp = getenv("MAKEOBJDIRPREFIX"))) { + if (!(path = getenv("MAKEOBJDIR"))) { + path = _PATH_OBJDIR; +#ifdef _PATH_OBJDIRPREFIX + pathp = _PATH_OBJDIRPREFIX; +#endif + (void) snprintf(mdpath, MAXPATHLEN, "%s.%s", + path, machine); + if (!(objdir = chdir_verify_path(mdpath, obpath))) + if (!(objdir=chdir_verify_path(path, obpath))) { +#ifdef _PATH_OBJDIRPREFIX + (void) snprintf(mdpath, MAXPATHLEN, + "%s%s", pathp, curdir); + if (!(objdir=chdir_verify_path(mdpath, + obpath))) +#endif + objdir = curdir; + } + } + else if (!(objdir = chdir_verify_path(path, obpath))) + objdir = curdir; + } + else { + (void) snprintf(mdpath, MAXPATHLEN, "%s%s", pathp, curdir); + if (!(objdir = chdir_verify_path(mdpath, obpath))) + objdir = curdir; + } + + setenv("PWD", objdir, 1); + + create = Lst_Init(FALSE); + makefiles = Lst_Init(FALSE); + printVars = FALSE; + variables = Lst_Init(FALSE); + beSilent = FALSE; /* Print commands as executed */ + ignoreErrors = FALSE; /* Pay attention to non-zero returns */ + noExecute = FALSE; /* Execute all commands */ + noRecursiveExecute = FALSE; /* Execute all .MAKE targets */ + keepgoing = FALSE; /* Stop on error */ + allPrecious = FALSE; /* Remove targets when interrupted */ + queryFlag = FALSE; /* This is not just a check-run */ + noBuiltins = FALSE; /* Read the built-in rules */ + touchFlag = FALSE; /* Actually update targets */ + usePipes = TRUE; /* Catch child output in pipes */ + debug = 0; /* No debug verbosity, please. */ + jobsRunning = FALSE; + + maxLocal = DEFMAXLOCAL; /* Set default local max concurrency */ +#ifdef REMOTE + maxJobs = DEFMAXJOBS; /* Set default max concurrency */ +#else + maxJobs = maxLocal; +#endif + compatMake = FALSE; /* No compat mode */ + + + /* + * Initialize the parsing, directory and variable modules to prepare + * for the reading of inclusion paths and variable settings on the + * command line + */ + + /* + * Initialize directory structures so -I flags can be processed + * correctly, if we have a different objdir, then let the directory + * know our curdir. + */ + Dir_Init(curdir != objdir ? curdir : NULL); + Parse_Init(); /* Need to initialize the paths of #include + * directories */ + Var_Set(".OBJDIR", objdir, VAR_GLOBAL, 0); + + /* + * Initialize various variables. + * MAKE also gets this name, for compatibility + * .MAKEFLAGS gets set to the empty string just in case. + * MFLAGS also gets initialized empty, for compatibility. + */ + Var_Set("MAKE", argv[0], VAR_GLOBAL, 0); + Var_Set(".MAKE", argv[0], VAR_GLOBAL, 0); + Var_Set(MAKEFLAGS, "", VAR_GLOBAL, 0); + Var_Set(MAKEOVERRIDES, "", VAR_GLOBAL, 0); + Var_Set("MFLAGS", "", VAR_GLOBAL, 0); + + /* + * First snag any flags out of the MAKE environment variable. + * (Note this is *not* MAKEFLAGS since /bin/make uses that and it's + * in a different format). + */ +#ifdef POSIX + Main_ParseArgLine(getenv("MAKEFLAGS")); +#else + Main_ParseArgLine(getenv("MAKE")); +#endif + + MainParseArgs(argc, argv); + + /* + * Be compatible if user did not specify -j and did not explicitly + * turned compatibility on + */ + if (!compatMake && !forceJobs) { + compatMake = TRUE; + } + + /* + * Initialize archive, target and suffix modules in preparation for + * parsing the makefile(s) + */ + Arch_Init(); + Targ_Init(); + Suff_Init(); + Trace_Init(tracefile); + + DEFAULT = NILGNODE; + (void)time(&now); + + Trace_Log(MAKESTART, NULL); + + /* + * Set up the .TARGETS variable to contain the list of targets to be + * created. If none specified, make the variable empty -- the parser + * will fill the thing in with the default or .MAIN target. + */ + if (!Lst_IsEmpty(create)) { + LstNode ln; + + for (ln = Lst_First(create); ln != NILLNODE; + ln = Lst_Succ(ln)) { + char *name = (char *)Lst_Datum(ln); + + Var_Append(".TARGETS", name, VAR_GLOBAL); + } + } else + Var_Set(".TARGETS", "", VAR_GLOBAL, 0); + + + /* + * If no user-supplied system path was given (through the -m option) + * add the directories from the DEFSYSPATH (more than one may be given + * as dir1:...:dirn) to the system include path. + */ + if (Lst_IsEmpty(sysIncPath)) { + if (syspath == NULL || *syspath == '\0') + syspath = defsyspath; + else + syspath = strdup(syspath); + + for (start = syspath; *start != '\0'; start = cp) { + for (cp = start; *cp != '\0' && *cp != ':'; cp++) + continue; + if (*cp == '\0') { + (void) Dir_AddDir(sysIncPath, start); + } else { + *cp++ = '\0'; + (void) Dir_AddDir(sysIncPath, start); + } + } + if (syspath != defsyspath) + free(syspath); + } + + /* + * Read in the built-in rules first, followed by the specified + * makefile, if it was (makefile != (char *) NULL), or the default + * Makefile and makefile, in that order, if it wasn't. + */ + if (!noBuiltins) { + LstNode ln; + + sysMkPath = Lst_Init (FALSE); + Dir_Expand (_PATH_DEFSYSMK, sysIncPath, sysMkPath); + if (Lst_IsEmpty(sysMkPath)) + Fatal("%s: no system rules (%s).", progname, + _PATH_DEFSYSMK); + ln = Lst_Find(sysMkPath, (ClientData)NULL, ReadMakefile); + if (ln != NILLNODE) + Fatal("%s: cannot open %s.", progname, + (char *)Lst_Datum(ln)); + } + + if (!Lst_IsEmpty(makefiles)) { + LstNode ln; + + ln = Lst_Find(makefiles, (ClientData)NULL, ReadMakefile); + if (ln != NILLNODE) + Fatal("%s: cannot open %s.", progname, + (char *)Lst_Datum(ln)); + } else if (!ReadMakefile("makefile", NULL)) + (void)ReadMakefile("Makefile", NULL); + + (void)ReadMakefile(".depend", NULL); + + Var_Append("MFLAGS", Var_Value(MAKEFLAGS, VAR_GLOBAL, &p1), VAR_GLOBAL); + if (p1) + free(p1); + + if (!jobServer && !compatMake) + Job_ServerStart(maxJobs); + if (DEBUG(JOB)) + printf("job_pipe %d %d, maxjobs %d maxlocal %d compat %d\n", job_pipe[0], job_pipe[1], maxJobs, + maxLocal, compatMake); + + ExportMAKEFLAGS(1); /* initial export */ + +#ifndef NO_CHECK_MAKE_CHDIR + Check_Cwd_av(0, NULL, 0); /* initialize it */ +#endif + + /* + * For compatibility, look at the directories in the VPATH variable + * and add them to the search path, if the variable is defined. The + * variable's value is in the same format as the PATH envariable, i.e. + * ::... + */ + if (Var_Exists("VPATH", VAR_CMD)) { + char *vpath, *path, *cp, savec; + /* + * GCC stores string constants in read-only memory, but + * Var_Subst will want to write this thing, so store it + * in an array + */ + static char VPATH[] = "${VPATH}"; + + vpath = Var_Subst(NULL, VPATH, VAR_CMD, FALSE); + path = vpath; + do { + /* skip to end of directory */ + for (cp = path; *cp != ':' && *cp != '\0'; cp++) + continue; + /* Save terminator character so know when to stop */ + savec = *cp; + *cp = '\0'; + /* Add directory to search path */ + (void) Dir_AddDir(dirSearchPath, path); + *cp = savec; + path = cp + 1; + } while (savec == ':'); + (void)free((Address)vpath); + } + + /* + * Now that all search paths have been read for suffixes et al, it's + * time to add the default search path to their lists... + */ + Suff_DoPaths(); + + /* + * Propagate attributes through :: dependency lists. + */ + Targ_Propagate(); + + /* print the initial graph, if the user requested it */ + if (DEBUG(GRAPH1)) + Targ_PrintGraph(1); + + /* print the values of any variables requested by the user */ + if (printVars) { + LstNode ln; + + for (ln = Lst_First(variables); ln != NILLNODE; + ln = Lst_Succ(ln)) { + char *value = Var_Value((char *)Lst_Datum(ln), + VAR_GLOBAL, &p1); + + printf("%s\n", value ? value : ""); + if (p1) + free(p1); + } + } + + /* + * Have now read the entire graph and need to make a list of targets + * to create. If none was given on the command line, we consult the + * parsing module to find the main target(s) to create. + */ + if (Lst_IsEmpty(create)) + targs = Parse_MainName(); + else + targs = Targ_FindList(create, TARG_CREATE); + + if (!compatMake && !printVars) { + /* + * Initialize job module before traversing the graph, now that + * any .BEGIN and .END targets have been read. This is done + * only if the -q flag wasn't given (to prevent the .BEGIN from + * being executed should it exist). + */ + if (!queryFlag) { + if (maxLocal == -1) + maxLocal = maxJobs; + Job_Init(maxJobs, maxLocal); + jobsRunning = TRUE; + } + + /* Traverse the graph, checking on all the targets */ + outOfDate = Make_Run(targs); + } else if (!printVars) { + /* + * Compat_Init will take care of creating all the targets as + * well as initializing the module. + */ + Compat_Run(targs); + } + +#ifdef CLEANUP + Lst_Destroy(targs, NOFREE); + Lst_Destroy(variables, NOFREE); + Lst_Destroy(makefiles, NOFREE); + Lst_Destroy(create, (void (*) __P((ClientData))) free); +#endif + + /* print the graph now it's been processed if the user requested it */ + if (DEBUG(GRAPH2)) + Targ_PrintGraph(2); + + Trace_Log(MAKEEND, 0); + + Suff_End(); + Targ_End(); + Arch_End(); + Var_End(); + Parse_End(); + Dir_End(); + Job_End(); + Trace_End(); + + if (queryFlag && outOfDate) + return(1); + else + return(0); +} + +/*- + * ReadMakefile -- + * Open and parse the given makefile. + * + * Results: + * TRUE if ok. FALSE if couldn't open file. + * + * Side Effects: + * lots + */ +static Boolean +ReadMakefile(p, q) + ClientData p, q; +{ + char *fname = p; /* makefile to read */ + FILE *stream; + size_t len = MAXPATHLEN; + char *name, *path = emalloc(len); + int setMAKEFILE; + + if (!strcmp(fname, "-")) { + Parse_File("(stdin)", stdin); + Var_Set("MAKEFILE", "", VAR_GLOBAL, 0); + } else { + setMAKEFILE = strcmp(fname, ".depend"); + + /* if we've chdir'd, rebuild the path name */ + if (curdir != objdir && *fname != '/') { + size_t plen = strlen(curdir) + strlen(fname) + 2; + if (len < plen) + path = erealloc(path, len = 2 * plen); + + (void)snprintf(path, len, "%s/%s", curdir, fname); + if ((stream = fopen(path, "r")) != NULL) { + fname = path; + goto found; + } + } else if ((stream = fopen(fname, "r")) != NULL) + goto found; + /* look in -I and system include directories. */ + name = Dir_FindFile(fname, parseIncPath); + if (!name) + name = Dir_FindFile(fname, sysIncPath); + if (!name || !(stream = fopen(name, "r"))) { + free(path); + return(FALSE); + } + fname = name; + /* + * set the MAKEFILE variable desired by System V fans -- the + * placement of the setting here means it gets set to the last + * makefile specified, as it is set by SysV make. + */ +found: + if (setMAKEFILE) + Var_Set("MAKEFILE", fname, VAR_GLOBAL, 0); + Parse_File(fname, stream); + (void)fclose(stream); + } + free(path); + return(TRUE); +} + + +/* + * If MAKEOBJDIRPREFIX is in use, make ends up not in .CURDIR + * in situations that would not arrise with ./obj (links or not). + * This tends to break things like: + * + * build: + * ${MAKE} includes + * + * This function spots when ${.MAKE:T} or ${.MAKE} is a command (as + * opposed to an argument) in a command line and if so returns + * ${.CURDIR} so caller can chdir() so that the assumptions made by + * the Makefile hold true. + * + * If ${.MAKE} does not contain any '/', then ${.MAKE:T} is skipped. + * + * The chdir() only happens in the child process, and does nothing if + * MAKEOBJDIRPREFIX and MAKEOBJDIR are not in the environment so it + * should not break anything. Also if NOCHECKMAKECHDIR is set we + * do nothing - to ensure historic semantics can be retained. + */ +#ifdef NO_CHECK_MAKE_CHDIR +char * +Check_Cwd_Cmd(cmd) + char *cmd; +{ + return 0; +} + +void +Check_Cwd(argv) + char **argv; +{ + return; +} + +#else + +static int Check_Cwd_Off = 0; + +static char * +Check_Cwd_av(ac, av, copy) + int ac; + char **av; + int copy; +{ + static char *make[4]; + static char *curdir = NULL; + char *cp, **mp; + int is_cmd, next_cmd; + int i; + int n; + + if (Check_Cwd_Off) + return NULL; + + if (make[0] == NULL) { + if (Var_Exists("NOCHECKMAKECHDIR", VAR_GLOBAL)) { + Check_Cwd_Off = 1; + return NULL; + } + + make[1] = Var_Value(".MAKE", VAR_GLOBAL, &cp); + if ((make[0] = strrchr(make[1], '/')) == NULL) { + make[0] = make[1]; + make[1] = NULL; + } else + ++make[0]; + make[2] = NULL; + curdir = Var_Value(".CURDIR", VAR_GLOBAL, &cp); + } + if (ac == 0 || av == NULL) + return NULL; /* initialization only */ + + if (getenv("MAKEOBJDIR") == NULL && + getenv("MAKEOBJDIRPREFIX") == NULL) + return NULL; + + + next_cmd = 1; + for (i = 0; i < ac; ++i) { + is_cmd = next_cmd; + + n = strlen(av[i]); + cp = &(av[i])[n - 1]; + if (strspn(av[i], "|&;") == n) { + next_cmd = 1; + continue; + } else if (*cp == ';' || *cp == '&' || *cp == '|' || *cp == ')') { + next_cmd = 1; + if (copy) { + do { + *cp-- = '\0'; + } while (*cp == ';' || *cp == '&' || *cp == '|' || + *cp == ')' || *cp == '}') ; + } else { + /* + * XXX this should not happen. + */ + fprintf(stderr, "WARNING: raw arg ends in shell meta '%s'\n", + av[i]); + } + } else + next_cmd = 0; + + cp = av[i]; + if (*cp == ';' || *cp == '&' || *cp == '|') + is_cmd = 1; + +#ifdef check_cwd_debug + fprintf(stderr, "av[%d] == %s '%s'", + i, (is_cmd) ? "cmd" : "arg", av[i]); +#endif + if (is_cmd != 0) { + if (*cp == '(' || *cp == '{' || + *cp == ';' || *cp == '&' || *cp == '|') { + do { + ++cp; + } while (*cp == '(' || *cp == '{' || + *cp == ';' || *cp == '&' || *cp == '|'); + if (*cp == '\0') { + next_cmd = 1; + continue; + } + } + if (strcmp(cp, "cd") == 0 || strcmp(cp, "chdir") == 0) { +#ifdef check_cwd_debug + fprintf(stderr, " == cd, done.\n"); +#endif + return NULL; + } + for (mp = make; *mp != NULL; ++mp) { + n = strlen(*mp); + if (strcmp(cp, *mp) == 0) { +#ifdef check_cwd_debug + fprintf(stderr, " %s == '%s', chdir(%s)\n", + cp, *mp, curdir); +#endif + return curdir; + } + } + } +#ifdef check_cwd_debug + fprintf(stderr, "\n"); +#endif + } + return NULL; +} + +char * +Check_Cwd_Cmd(cmd) + char *cmd; +{ + char *cp, *bp, **av; + int ac; + + if (Check_Cwd_Off) + return NULL; + + if (cmd) { + av = brk_string(cmd, &ac, TRUE, &bp); +#ifdef check_cwd_debug + fprintf(stderr, "splitting: '%s' -> %d words\n", + cmd, ac); +#endif + } else { + ac = 0; + av = NULL; + bp = NULL; + } + cp = Check_Cwd_av(ac, av, 1); + if (bp) { + free(av); + free(bp); + } + return cp; +} + +void +Check_Cwd(argv) + char **argv; +{ + char *cp; + int ac; + + if (Check_Cwd_Off) + return; + + for (ac = 0; argv[ac] != NULL; ++ac) + /* NOTHING */; + if (ac == 3 && *argv[1] == '-') { + cp = Check_Cwd_Cmd(argv[2]); + } else { + cp = Check_Cwd_av(ac, argv, 0); + } + if (cp) { + chdir(cp); + } +} +#endif /* NO_CHECK_MAKE_CHDIR */ + +/*- + * Cmd_Exec -- + * Execute the command in cmd, and return the output of that command + * in a string. + * + * Results: + * A string containing the output of the command, or the empty string + * If err is not NULL, it contains the reason for the command failure + * + * Side Effects: + * The string must be freed by the caller. + */ +char * +Cmd_Exec(cmd, err) + char *cmd; + char **err; +{ + char *args[4]; /* Args for invoking the shell */ + int fds[2]; /* Pipe streams */ + int cpid; /* Child PID */ + int pid; /* PID from wait() */ + char *res; /* result */ + int status; /* command exit status */ + Buffer buf; /* buffer to store the result */ + char *cp; + int cc; + + + *err = NULL; + + /* + * Set up arguments for shell + */ + args[0] = "sh"; + args[1] = "-c"; + args[2] = cmd; + args[3] = NULL; + + /* + * Open a pipe for fetching its output + */ + if (pipe(fds) == -1) { + *err = "Couldn't create pipe for \"%s\""; + goto bad; + } + + /* + * Fork + */ + switch (cpid = vfork()) { + case 0: + /* + * Close input side of pipe + */ + (void) close(fds[0]); + + /* + * Duplicate the output stream to the shell's output, then + * shut the extra thing down. Note we don't fetch the error + * stream...why not? Why? + */ + (void) dup2(fds[1], 1); + (void) close(fds[1]); + + (void) execv("/bin/sh", args); + _exit(1); + /*NOTREACHED*/ + + case -1: + *err = "Couldn't exec \"%s\""; + goto bad; + + default: + /* + * No need for the writing half + */ + (void) close(fds[1]); + + buf = Buf_Init (MAKE_BSIZE); + + do { + char result[BUFSIZ]; + cc = read(fds[0], result, sizeof(result)); + if (cc > 0) + Buf_AddBytes(buf, cc, (Byte *) result); + } + while (cc > 0 || (cc == -1 && errno == EINTR)); + + /* + * Close the input side of the pipe. + */ + (void) close(fds[0]); + + /* + * Wait for the process to exit. + */ + while(((pid = wait(&status)) != cpid) && (pid >= 0)) + continue; + + res = (char *)Buf_GetAll (buf, &cc); + Buf_Destroy (buf, FALSE); + + if (cc == 0) + *err = "Couldn't read shell's output for \"%s\""; + + if (status) + *err = "\"%s\" returned non-zero status"; + + /* + * Null-terminate the result, convert newlines to spaces and + * install it in the variable. + */ + res[cc] = '\0'; + cp = &res[cc]; + + if (cc > 0 && *--cp == '\n') { + /* + * A final newline is just stripped + */ + *cp-- = '\0'; + } + while (cp >= res) { + if (*cp == '\n') { + *cp = ' '; + } + cp--; + } + break; + } + return res; +bad: + res = emalloc(1); + *res = '\0'; + return res; +} + +/*- + * Error -- + * Print an error message given its format. + * + * Results: + * None. + * + * Side Effects: + * The message is printed. + */ +/* VARARGS */ +void +#ifdef __STDC__ +Error(char *fmt, ...) +#else +Error(va_alist) + va_dcl +#endif +{ + va_list ap; +#ifdef __STDC__ + va_start(ap, fmt); +#else + char *fmt; + + va_start(ap); + fmt = va_arg(ap, char *); +#endif + fprintf(stderr, "%s: ", progname); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + (void)fflush(stderr); +} + +/*- + * Fatal -- + * Produce a Fatal error message. If jobs are running, waits for them + * to finish. + * + * Results: + * None + * + * Side Effects: + * The program exits + */ +/* VARARGS */ +void +#ifdef __STDC__ +Fatal(char *fmt, ...) +#else +Fatal(va_alist) + va_dcl +#endif +{ + va_list ap; +#ifdef __STDC__ + va_start(ap, fmt); +#else + char *fmt; + + va_start(ap); + fmt = va_arg(ap, char *); +#endif + if (jobsRunning) + Job_Wait(); + Job_TokenFlush(); + + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + (void)fflush(stderr); + + PrintOnError(NULL); + + if (DEBUG(GRAPH2)) + Targ_PrintGraph(2); + Trace_Log(MAKEERROR, 0); + exit(2); /* Not 1 so -q can distinguish error */ +} + +/* + * Punt -- + * Major exception once jobs are being created. Kills all jobs, prints + * a message and exits. + * + * Results: + * None + * + * Side Effects: + * All children are killed indiscriminately and the program Lib_Exits + */ +/* VARARGS */ +void +#ifdef __STDC__ +Punt(char *fmt, ...) +#else +Punt(va_alist) + va_dcl +#endif +{ + va_list ap; +#ifdef __STDC__ + va_start(ap, fmt); +#else + char *fmt; + + va_start(ap); + fmt = va_arg(ap, char *); +#endif + + (void)fprintf(stderr, "%s: ", progname); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + (void)fflush(stderr); + + PrintOnError(NULL); + + DieHorribly(); +} + +/*- + * DieHorribly -- + * Exit without giving a message. + * + * Results: + * None + * + * Side Effects: + * A big one... + */ +void +DieHorribly() +{ + if (jobsRunning) + Job_AbortAll(); + if (DEBUG(GRAPH2)) + Targ_PrintGraph(2); + Trace_Log(MAKEERROR, 0); + exit(2); /* Not 1, so -q can distinguish error */ +} + +/* + * Finish -- + * Called when aborting due to errors in child shell to signal + * abnormal exit. + * + * Results: + * None + * + * Side Effects: + * The program exits + */ +void +Finish(errors) + int errors; /* number of errors encountered in Make_Make */ +{ + Fatal("%d error%s", errors, errors == 1 ? "" : "s"); +} + +/* + * emalloc -- + * malloc, but die on error. + */ +void * +emalloc(len) + size_t len; +{ + void *p; + + if ((p = malloc(len)) == NULL) + enomem(); + return(p); +} + +/* + * estrdup -- + * strdup, but die on error. + */ +char * +estrdup(str) + const char *str; +{ + char *p; + + if ((p = strdup(str)) == NULL) + enomem(); + return(p); +} + +/* + * erealloc -- + * realloc, but die on error. + */ +void * +erealloc(ptr, size) + void *ptr; + size_t size; +{ + if ((ptr = realloc(ptr, size)) == NULL) + enomem(); + return(ptr); +} + +/* + * enomem -- + * die when out of memory. + */ +void +enomem() +{ + (void)fprintf(stderr, "%s: %s.\n", progname, strerror(errno)); + exit(2); +} + +/* + * enunlink -- + * Remove a file carefully, avoiding directories. + */ +int +eunlink(file) + const char *file; +{ + struct stat st; + + if (lstat(file, &st) == -1) + return -1; + + if (S_ISDIR(st.st_mode)) { + errno = EISDIR; + return -1; + } + return unlink(file); +} + +/* + * execError -- + * Print why exec failed, avoiding stdio. + */ +void +execError(av) + const char *av; +{ +#ifdef USE_IOVEC + int i = 0; + struct iovec iov[6]; +#define IOADD(s) \ + (void)(iov[i].iov_base = (s), \ + iov[i].iov_len = strlen(iov[i].iov_base), \ + i++) +#else +#define IOADD (void)write(2, s, strlen(s)) +#endif + + IOADD(progname); + IOADD(": Exec of `"); + IOADD((char *)av); + IOADD("' failed ("); + IOADD(strerror(errno)); + IOADD(")\n"); + +#ifdef USE_IOVEC + (void)writev(2, iov, 6); +#endif +} + +/* + * usage -- + * exit with usage message + */ +static void +usage() +{ + (void)fprintf(stderr, +"Usage: %s [-Beiknqrst] [-D variable] [-d flags] [-f makefile ]\n\ + [-I directory] [-j max_jobs] [-m directory] [-V variable]\n\ + [variable=value] [target ...]\n", progname); + exit(2); +} + + +int +PrintAddr(a, b) + ClientData a; + ClientData b; +{ + printf("%lx ", (unsigned long) a); + return b ? 0 : 0; +} + + + +void +PrintOnError(s) + char *s; +{ + char tmp[64]; + + if (s) + printf("%s", s); + + printf("\n%s: stopped in %s\n", progname, curdir); + strncpy(tmp, "${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'\n@}", + sizeof(tmp) - 1); + s = Var_Subst(NULL, tmp, VAR_GLOBAL, 0); + if (s && *s) + printf("%s", s); +} + +void +ExportMAKEFLAGS(first) + int first; +{ + static int once = 1; + char tmp[64]; + char *s; + + if (once != first) + return; + once = 0; + + strncpy(tmp, "${.MAKEFLAGS} ${.MAKEOVERRIDES:O:u:@v@$v=${$v:Q}@}", + sizeof(tmp)); + s = Var_Subst(NULL, tmp, VAR_CMD, 0); + if (s && *s) { +#ifdef POSIX + setenv("MAKEFLAGS", s, 1); +#else + setenv("MAKE", s, 1); +#endif + } +} diff --git a/bootstrap/bmake/make-conf.h b/bootstrap/bmake/make-conf.h new file mode 100644 index 00000000000..dcc702788c6 --- /dev/null +++ b/bootstrap/bmake/make-conf.h @@ -0,0 +1,133 @@ +/* $NetBSD: make-conf.h,v 1.1.1.1 2004/03/11 13:04:10 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. + * Copyright (c) 1988, 1989 by Adam de Boor + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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: @(#)config.h 8.1 (Berkeley) 6/6/93 + */ + +#define DEFSHELL 1 /* Bourne shell */ + +/* + * DEFMAXJOBS + * DEFMAXLOCAL + * These control the default concurrency. On no occasion will more + * than DEFMAXJOBS targets be created at once (locally or remotely) + * DEFMAXLOCAL is the highest number of targets which will be + * created on the local machine at once. Note that if you set this + * to 0, nothing will ever happen... + */ +#define DEFMAXJOBS 4 +#define DEFMAXLOCAL 1 + +/* + * INCLUDES + * LIBRARIES + * These control the handling of the .INCLUDES and .LIBS variables. + * If INCLUDES is defined, the .INCLUDES variable will be filled + * from the search paths of those suffixes which are marked by + * .INCLUDES dependency lines. Similarly for LIBRARIES and .LIBS + * See suff.c for more details. + */ +#define INCLUDES +#define LIBRARIES + +/* + * LIBSUFF + * Is the suffix used to denote libraries and is used by the Suff module + * to find the search path on which to seek any -l targets. + * + * RECHECK + * If defined, Make_Update will check a target for its current + * modification time after it has been re-made, setting it to the + * starting time of the make only if the target still doesn't exist. + * Unfortunately, under NFS the modification time often doesn't + * get updated in time, so a target will appear to not have been + * re-made, causing later targets to appear up-to-date. On systems + * that don't have this problem, you should defined this. Under + * NFS you probably should not, unless you aren't exporting jobs. + */ +#define LIBSUFF ".a" +#define RECHECK + +/* + * POSIX + * Adhere to the POSIX 1003.2 draft for the make(1) program. + * - Use MAKEFLAGS instead of MAKE to pick arguments from the + * environment. + * - Allow empty command lines if starting with tab. + */ +#define POSIX + +/* + * SYSVINCLUDE + * Recognize system V like include directives [include "filename"] + * SYSVVARSUB + * Recognize system V like ${VAR:x=y} variable substitutions + */ +#define SYSVINCLUDE +#define SYSVVARSUB + +/* + * SUNSHCMD + * Recognize SunOS and Solaris: + * VAR :sh= CMD # Assign VAR to the command substitution of CMD + * ${VAR:sh} # Return the command substitution of the value + * # of ${VAR} + */ +#define SUNSHCMD + +/* + * USE_PGRP + * Kill the process group of the job so that all the progeny of the + * current job dies, instead of just the one forked. + */ +#define USE_PGRP + +/* + * USE_IOVEC + * We have writev(2) + */ +#ifdef HAVE_SYS_UIO_H +# define USE_IOVEC +#endif + +#if !defined(__svr4__) && !defined(__SVR4) && !defined(__ELF__) +# ifndef RANLIBMAG +# define RANLIBMAG "__.SYMDEF" +# endif +#endif diff --git a/bootstrap/bmake/make.1 b/bootstrap/bmake/make.1 new file mode 100644 index 00000000000..b71b99983a7 --- /dev/null +++ b/bootstrap/bmake/make.1 @@ -0,0 +1,1238 @@ +.\" $NetBSD: make.1,v 1.1.1.1 2004/03/11 13:04:10 grant Exp $ +.\" +.\" 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. +.\" +.\" from: @(#)make.1 8.4 (Berkeley) 3/19/94 +.\" +.Dd March 19, 1994 +.Dt MAKE 1 +.Os +.Sh NAME +.Nm make +.Nd maintain program dependencies +.Sh SYNOPSIS +.Nm "" +.Op Fl BeikNnqrstW +.Bk -words +.Op Fl D Ar variable +.Ek +.Bk -words +.Op Fl d Ar flags +.Ek +.Bk -words +.Op Fl f Ar makefile +.Ek +.Bk -words +.Op Fl I Ar directory +.Ek +.Bk -words +.Op Fl j Ar max_jobs +.Ek +.Bk -words +.Op Fl J Ar private +.Ek +.Bk -words +.Op Fl m Ar directory +.Ek +.Bk -words +.Op Fl T Ar file +.Ek +.Bk -words +.Op Fl V Ar variable +.Ek +.Op Ar variable=value +.Bk -words +.Op Ar target ... +.Ek +.Sh DESCRIPTION +.Nm +is a program designed to simplify the maintenance of other programs. +Its input is a list of specifications as to the files upon which programs +and other files depend. +If the file +.Ql Pa makefile +exists, it is read for this list of specifications. +If it does not exist, the file +.Ql Pa Makefile +is read. +If the file +.Ql Pa .depend +exists, it is read (see +.Xr mkdep 1) . +.Pp +This manual page is intended as a reference document only. +For a more thorough description of +.Nm +and makefiles, please refer to +.%T "Make \- A Tutorial" . +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl B +Try to be backwards compatible by executing a single shell per command and +by executing the commands to make the sources of a dependency line in sequence. +.It Fl D Ar variable +Define +.Ar variable +to be 1, in the global context. +.It Fl d Ar flags +Turn on debugging, and specify which portions of +.Nm +are to print debugging information. +.Ar Flags +is one or more of the following: +.Bl -tag -width Ds +.It Ar A +Print all possible debugging information; +equivalent to specifying all of the debugging flags. +.It Ar a +Print debugging information about archive searching and caching. +.It Ar c +Print debugging information about conditional evaluation. +.It Ar d +Print debugging information about directory searching and caching. +.It Ar "g1" +Print the input graph before making anything. +.It Ar "g2" +Print the input graph after making everything, or before exiting +on error. +.It Ar j +Print debugging information about running multiple shells. +.It Ar m +Print debugging information about making targets, including modification +dates. +.It Ar s +Print debugging information about suffix-transformation rules. +.It Ar t +Print debugging information about target list maintenance. +.It Ar v +Print debugging information about variable assignment. +.It Ar x +Run shell commands with +.Fl x +so the actual commands are printed as they are executed. +.El +.It Fl e +Specify that environmental variables override macro assignments within +makefiles. +.It Fl f Ar makefile +Specify a makefile to read instead of the default +.Ql Pa makefile +and +If +.Ar makefile +is +.Ql Fl , +standard input is read. +Multiple makefile's may be specified, and are read in the order specified. +.It Fl I Ar directory +Specify a directory in which to search for makefiles and included makefiles. +The system makefile directory (or directories, see the +.Fl m +option) is automatically included as part of this list. +.It Fl i +Ignore non-zero exit of shell commands in the makefile. +Equivalent to specifying +.Ql Fl +before each command line in the makefile. +.It Fl J Ar private +This option should +.Em not +be specified by the user. +.Pp +When the +.Ar j +option is in use in a recursive build, this option is passed by a make +to child makes to allow all the make processes in the build to +cooperate to avoid overloading the system. +.It Fl j Ar max_jobs +Specify the maximum number of jobs that +.Nm +may have running at any one time. Turns compatibility mode off, unless the +.Ar B +flag is also specified. +.It Fl k +Continue processing after errors are encountered, but only on those targets +that do not depend on the target whose creation caused the error. +.It Fl m Ar directory +Specify a directory in which to search for sys.mk and makefiles included +via the <...> style. Multiple directories can be added to form a search path. +This path will override the default system include path: /usr/share/mk. +Furthermore the system include path will be appended to the search path used +for "..."-style inclusions (see the +.Fl I +option). +.It Fl n +Display the commands that would have been executed, but do not +actually execute them unless the target depends on the .MAKE special +source (see below) +.It Fl N +Display the commands which would have been executed, but do not +actually execute any of them; useful for debugging top-level makefiles +without descending into subdirectories. +.It Fl q +Do not execute any commands, but exit 0 if the specified targets are +up-to-date and 1, otherwise. +.It Fl r +Do not use the built-in rules specified in the system makefile. +.It Fl s +Do not echo any commands as they are executed. +Equivalent to specifying +.Ql Ic @ +before each command line in the makefile. +.It Fl T Ar tracefile +When used with the +.Fl j +flag, +append a trace record to +.Ar tracefile +for each job started and completed. +.It Fl t +Rather than re-building a target as specified in the makefile, create it +or update its modification time to make it appear up-to-date. +.It Fl V Ar variable +Print +.Nm "" Ns 's +idea of the value of +.Ar variable , +in the global context. +Do not build any targets. +Multiple instances of this option may be specified; +the variables will be printed one per line, +with a blank line for each null or undefined variable. +.It Fl W +Treat any warnings during makefile parsing as errors. +.It Ar variable=value +Set the value of the variable +.Ar variable +to +.Ar value . +.El +.Pp +There are seven different types of lines in a makefile: file dependency +specifications, shell commands, variable assignments, include statements, +conditional directives, for loops, and comments. +.Pp +In general, lines may be continued from one line to the next by ending +them with a backslash +.Pq Ql \e . +The trailing newline character and initial whitespace on the following +line are compressed into a single space. +.Sh FILE DEPENDENCY SPECIFICATIONS +Dependency lines consist of one or more targets, an operator, and zero +or more sources. +This creates a relationship where the targets ``depend'' on the sources +and are usually created from them. +The exact relationship between the target and the source is determined +by the operator that separates them. +The three operators are as follows: +.Bl -tag -width flag +.It Ic \&: +A target is considered out-of-date if its modification time is less than +those of any of its sources. +Sources for a target accumulate over dependency lines when this operator +is used. +The target is removed if +.Nm +is interrupted. +.It Ic \&! +Targets are always re-created, but not until all sources have been +examined and re-created as necessary. +Sources for a target accumulate over dependency lines when this operator +is used. +The target is removed if +.Nm +is interrupted. +.It Ic \&:: +If no sources are specified, the target is always re-created. +Otherwise, a target is considered out-of-date if any of its sources has +been modified more recently than the target. +Sources for a target do not accumulate over dependency lines when this +operator is used. +The target will not be removed if +.Nm +is interrupted. +.El +.Pp +Targets and sources may contain the shell wildcard values +.Ql ? , +.Ql * , +.Ql [] +and +.Ql {} . +The values +.Ql ? , +.Ql * +and +.Ql [] +may only be used as part of the final +component of the target or source, and must be used to describe existing +files. +The value +.Ql {} +need not necessarily be used to describe existing files. +Expansion is in directory order, not alphabetically as done in the shell. +.Sh SHELL COMMANDS +Each target may have associated with it a series of shell commands, normally +used to create the target. +Each of the commands in this script +.Em must +be preceded by a tab. +While any target may appear on a dependency line, only one of these +dependencies may be followed by a creation script, unless the +.Ql Ic :: +operator is used. +.Pp +If the first or first two characters of the command line are +.Ql Ic @ +and/or +.Ql Ic \- , +the command is treated specially. +A +.Ql Ic @ +causes the command not to be echoed before it is executed. +A +.Ql Ic \- +causes any non-zero exit status of the command line to be ignored. +.Sh VARIABLE ASSIGNMENTS +Variables in make are much like variables in the shell, and, by tradition, +consist of all upper-case letters. +The five operators that can be used to assign values to variables are as +follows: +.Bl -tag -width Ds +.It Ic \&= +Assign the value to the variable. +Any previous value is overridden. +.It Ic \&+= +Append the value to the current value of the variable. +.It Ic \&?= +Assign the value to the variable if it is not already defined. +.It Ic \&:= +Assign with expansion, i.e. expand the value before assigning it +to the variable. +Normally, expansion is not done until the variable is referenced. +.It Ic \&!= +Expand the value and pass it to the shell for execution and assign +the result to the variable. +Any newlines in the result are replaced with spaces. +.El +.Pp +Any white-space before the assigned +.Ar value +is removed; if the value is being appended, a single space is inserted +between the previous contents of the variable and the appended value. +.Pp +Variables are expanded by surrounding the variable name with either +curly braces +.Pq Ql {} +or parentheses +.Pq Ql () +and preceding it with +a dollar sign +.Pq Ql \&$ . +If the variable name contains only a single letter, the surrounding +braces or parentheses are not required. +This shorter form is not recommended. +.Pp +Variable substitution occurs at two distinct times, depending on where +the variable is being used. +Variables in dependency lines are expanded as the line is read. +Variables in shell commands are expanded when the shell command is +executed. +.Pp +The four different classes of variables (in order of increasing precedence) +are: +.Bl -tag -width Ds +.It Environment variables +Variables defined as part of +.Nm "" Ns 's +environment. +.It Global variables +Variables defined in the makefile or in included makefiles. +.It Command line variables +Variables defined as part of the command line. +.It Local variables +Variables that are defined specific to a certain target. +The seven local variables are as follows: +.Bl -tag -width ".ARCHIVE" +.It Va .ALLSRC +The list of all sources for this target; also known as +.Ql Va \&> . +.It Va .ARCHIVE +The name of the archive file. +.It Va .IMPSRC +The name/path of the source from which the target is to be transformed +(the ``implied'' source); also known as +.Ql Va \&< . +.It Va .MEMBER +The name of the archive member. +.It Va .OODATE +The list of sources for this target that were deemed out-of-date; also +known as +.Ql Va \&? . +.It Va .PREFIX +The file prefix of the file, containing only the file portion, no suffix +or preceding directory components; also known as +.Ql Va * . +.It Va .TARGET +The name of the target; also known as +.Ql Va @ . +.El +.Pp +The shorter forms +.Ql Va @ , +.Ql Va ? , +.Ql Va \&> +and +.Ql Va * +are permitted for backward +compatibility with historical makefiles and are not recommended. +The six variables +.Ql Va "@F" , +.Ql Va "@D" , +.Ql Va " +#ifndef lint +#if 0 +static char sccsid[] = "@(#)make.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: make.c,v 1.1.1.1 2004/03/11 13:04:10 grant Exp $"); +#endif +#endif /* not lint */ +#endif + +#if !defined(MAKE_BOOTSTRAP) && !defined(lint) +__IDSTRING(rcs_id,"$Id: make.c,v 1.1.1.1 2004/03/11 13:04:10 grant Exp $"); +#endif + +/*- + * make.c -- + * The functions which perform the examination of targets and + * their suitability for creation + * + * Interface: + * Make_Run Initialize things for the module and recreate + * whatever needs recreating. Returns TRUE if + * work was (or would have been) done and FALSE + * otherwise. + * + * Make_Update Update all parents of a given child. Performs + * various bookkeeping chores like the updating + * of the cmtime field of the parent, filling + * of the IMPSRC context variable, etc. It will + * place the parent on the toBeMade queue if it + * should be. + * + * Make_TimeStamp Function to set the parent's cmtime field + * based on a child's modification time. + * + * Make_DoAllVar Set up the various local variables for a + * target, including the .ALLSRC variable, making + * sure that any variable that needs to exist + * at the very least has the empty value. + * + * Make_OODate Determine if a target is out-of-date. + * + * Make_HandleUse See if a child is a .USE node for a parent + * and perform the .USE actions if so. + * + * Make_ExpandUse Expand .USE nodes and return the new list of + * targets. + */ + +#include "make.h" +#include "hash.h" +#include "dir.h" +#include "job.h" + +static Lst toBeMade; /* The current fringe of the graph. These + * are nodes which await examination by + * MakeOODate. It is added to by + * Make_Update and subtracted from by + * MakeStartJobs */ +static int numNodes; /* Number of nodes to be processed. If this + * is non-zero when Job_Empty() returns + * TRUE, there's a cycle in the graph */ + +static int MakeAddChild __P((ClientData, ClientData)); +static int MakeFindChild __P((ClientData, ClientData)); +static int MakeUnmark __P((ClientData, ClientData)); +static int MakeAddAllSrc __P((ClientData, ClientData)); +static int MakeTimeStamp __P((ClientData, ClientData)); +static int MakeHandleUse __P((ClientData, ClientData)); +static Boolean MakeStartJobs __P((void)); +static int MakePrintStatus __P((ClientData, ClientData)); +/*- + *----------------------------------------------------------------------- + * Make_TimeStamp -- + * Set the cmtime field of a parent node based on the mtime stamp in its + * child. Called from MakeOODate via Lst_ForEach. + * + * Results: + * Always returns 0. + * + * Side Effects: + * The cmtime of the parent node will be changed if the mtime + * field of the child is greater than it. + *----------------------------------------------------------------------- + */ +int +Make_TimeStamp (pgn, cgn) + GNode *pgn; /* the current parent */ + GNode *cgn; /* the child we've just examined */ +{ + if (cgn->mtime > pgn->cmtime) { + pgn->cmtime = cgn->mtime; + } + return (0); +} + +static int +MakeTimeStamp (pgn, cgn) + ClientData pgn; /* the current parent */ + ClientData cgn; /* the child we've just examined */ +{ + return Make_TimeStamp((GNode *) pgn, (GNode *) cgn); +} + +/*- + *----------------------------------------------------------------------- + * Make_OODate -- + * See if a given node is out of date with respect to its sources. + * Used by Make_Run when deciding which nodes to place on the + * toBeMade queue initially and by Make_Update to screen out USE and + * EXEC nodes. In the latter case, however, any other sort of node + * must be considered out-of-date since at least one of its children + * will have been recreated. + * + * Results: + * TRUE if the node is out of date. FALSE otherwise. + * + * Side Effects: + * The mtime field of the node and the cmtime field of its parents + * will/may be changed. + *----------------------------------------------------------------------- + */ +Boolean +Make_OODate (gn) + register GNode *gn; /* the node to check */ +{ + Boolean oodate; + + /* + * Certain types of targets needn't even be sought as their datedness + * doesn't depend on their modification time... + */ + if ((gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC)) == 0) { + (void) Dir_MTime (gn); + if (DEBUG(MAKE)) { + if (gn->mtime != 0) { + printf ("modified %s...", Targ_FmtTime(gn->mtime)); + } else { + printf ("non-existent..."); + } + } + } + + /* + * A target is remade in one of the following circumstances: + * its modification time is smaller than that of its youngest child + * and it would actually be run (has commands or type OP_NOP) + * it's the object of a force operator + * it has no children, was on the lhs of an operator and doesn't exist + * already. + * + * Libraries are only considered out-of-date if the archive module says + * they are. + * + * These weird rules are brought to you by Backward-Compatibility and + * the strange people who wrote 'Make'. + */ + if (gn->type & (OP_USE|OP_USEBEFORE)) { + /* + * If the node is a USE node it is *never* out of date + * no matter *what*. + */ + if (DEBUG(MAKE)) { + printf(".USE node..."); + } + oodate = FALSE; + } else if ((gn->type & OP_LIB) && + ((gn->mtime==0) || Arch_IsLib(gn))) { + if (DEBUG(MAKE)) { + printf("library..."); + } + + /* + * always out of date if no children and :: target + * or non-existent. + */ + oodate = (gn->mtime == 0 || Arch_LibOODate (gn) || + (gn->cmtime == 0 && (gn->type & OP_DOUBLEDEP))); + } else if (gn->type & OP_JOIN) { + /* + * A target with the .JOIN attribute is only considered + * out-of-date if any of its children was out-of-date. + */ + if (DEBUG(MAKE)) { + printf(".JOIN node..."); + } + if (DEBUG(MAKE)) { + printf("source %smade...", gn->flags & CHILDMADE ? "" : "not "); + } + oodate = (gn->flags & CHILDMADE) ? 1 : 0; + } else if (gn->type & (OP_FORCE|OP_EXEC|OP_PHONY)) { + /* + * A node which is the object of the force (!) operator or which has + * the .EXEC attribute is always considered out-of-date. + */ + if (DEBUG(MAKE)) { + if (gn->type & OP_FORCE) { + printf("! operator..."); + } else if (gn->type & OP_PHONY) { + printf(".PHONY node..."); + } else { + printf(".EXEC node..."); + } + } + oodate = TRUE; + } else if ((gn->mtime < gn->cmtime) || + ((gn->cmtime == 0) && + ((gn->mtime==0) || (gn->type & OP_DOUBLEDEP)))) + { + /* + * A node whose modification time is less than that of its + * youngest child or that has no children (cmtime == 0) and + * either doesn't exist (mtime == 0) or was the object of a + * :: operator is out-of-date. Why? Because that's the way Make does + * it. + */ + if (DEBUG(MAKE)) { + if (gn->mtime < gn->cmtime) { + printf("modified before source..."); + } else if (gn->mtime == 0) { + printf("non-existent and no sources..."); + } else { + printf(":: operator and no sources..."); + } + } + oodate = TRUE; + } else { + /* + * When a non-existing child with no sources + * (such as a typically used FORCE source) has been made and + * the target of the child (usually a directory) has the same + * timestamp as the timestamp just given to the non-existing child + * after it was considered made. + */ + if (DEBUG(MAKE)) { + if (gn->flags & FORCE) + printf("non existing child..."); + } + oodate = (gn->flags & FORCE) ? 1 : 0; + } + + /* + * If the target isn't out-of-date, the parents need to know its + * modification time. Note that targets that appear to be out-of-date + * but aren't, because they have no commands and aren't of type OP_NOP, + * have their mtime stay below their children's mtime to keep parents from + * thinking they're out-of-date. + */ + if (!oodate) { + Lst_ForEach (gn->parents, MakeTimeStamp, (ClientData)gn); + } + + return (oodate); +} + +/*- + *----------------------------------------------------------------------- + * MakeAddChild -- + * Function used by Make_Run to add a child to the list l. + * It will only add the child if its make field is FALSE. + * + * Results: + * Always returns 0 + * + * Side Effects: + * The given list is extended + *----------------------------------------------------------------------- + */ +static int +MakeAddChild (gnp, lp) + ClientData gnp; /* the node to add */ + ClientData lp; /* the list to which to add it */ +{ + GNode *gn = (GNode *) gnp; + Lst l = (Lst) lp; + + if ((gn->flags & REMAKE) == 0 && !(gn->type & (OP_USE|OP_USEBEFORE))) { + (void)Lst_EnQueue (l, (ClientData)gn); + } + return (0); +} + +/*- + *----------------------------------------------------------------------- + * MakeFindChild -- + * Function used by Make_Run to find the pathname of a child + * that was already made. + * + * Results: + * Always returns 0 + * + * Side Effects: + * The path and mtime of the node and the cmtime of the parent are + * updated + *----------------------------------------------------------------------- + */ +static int +MakeFindChild (gnp, pgnp) + ClientData gnp; /* the node to find */ + ClientData pgnp; +{ + GNode *gn = (GNode *) gnp; + GNode *pgn = (GNode *) pgnp; + + (void) Dir_MTime(gn); + Make_TimeStamp(pgn, gn); + gn->made = UPTODATE; + + return (0); +} + +/*- + *----------------------------------------------------------------------- + * Make_HandleUse -- + * Function called by Make_Run and SuffApplyTransform on the downward + * pass to handle .USE and transformation nodes. A callback function + * for Lst_ForEach, it implements the .USE and transformation + * functionality by copying the node's commands, type flags + * and children to the parent node. Should be called before the + * children are enqueued to be looked at by MakeAddChild. + * + * A .USE node is much like an explicit transformation rule, except + * its commands are always added to the target node, even if the + * target already has commands. + * + * Results: + * returns 0. + * + * Side Effects: + * Children and commands may be added to the parent and the parent's + * type may be changed. + * + *----------------------------------------------------------------------- + */ +int +Make_HandleUse (cgn, pgn) + register GNode *cgn; /* The .USE node */ + register GNode *pgn; /* The target of the .USE node */ +{ + register LstNode ln; /* An element in the children list */ + + if (cgn->type & (OP_USE|OP_USEBEFORE|OP_TRANSFORM)) { + if ((cgn->type & (OP_USE|OP_USEBEFORE)) || Lst_IsEmpty(pgn->commands)) { + if (cgn->type & OP_USEBEFORE) { + /* + * .USEBEFORE -- + * prepend the child's commands to the parent. + */ + Lst cmds = pgn->commands; + pgn->commands = Lst_Duplicate (cgn->commands, NOCOPY); + (void) Lst_Concat (pgn->commands, cmds, LST_CONCNEW); + Lst_Destroy (cmds, NOFREE); + } else { + /* + * .USE or target has no commands -- + * append the child's commands to the parent. + */ + (void) Lst_Concat (pgn->commands, cgn->commands, LST_CONCNEW); + } + } + + if (Lst_Open (cgn->children) == SUCCESS) { + while ((ln = Lst_Next (cgn->children)) != NILLNODE) { + register GNode *tgn, *gn = (GNode *)Lst_Datum (ln); + + /* + * Expand variables in the .USE node's name + * and save the unexpanded form. + * We don't need to do this for commands. + * They get expanded properly when we execute. + */ + if (gn->uname == NULL) { + gn->uname = gn->name; + } else { + if (gn->name) + free(gn->name); + } + gn->name = Var_Subst(NULL, gn->uname, pgn, FALSE); + if (gn->name && gn->uname && strcmp(gn->name, gn->uname) != 0) { + /* See if we have a target for this node. */ + tgn = Targ_FindNode(gn->name, TARG_NOCREATE); + if (tgn != NILGNODE) + gn = tgn; + } + + (void) Lst_AtEnd (pgn->children, gn); + (void) Lst_AtEnd (gn->parents, pgn); + pgn->unmade += 1; + } + Lst_Close (cgn->children); + } + + pgn->type |= cgn->type & ~(OP_OPMASK|OP_USE|OP_USEBEFORE|OP_TRANSFORM); + + /* + * This child node is now "made", so we decrement the count of + * unmade children in the parent... We also remove the child + * from the parent's list to accurately reflect the number of decent + * children the parent has. This is used by Make_Run to decide + * whether to queue the parent or examine its children... + */ + if ((cgn->type & (OP_USE|OP_USEBEFORE)) && + (ln = Lst_Member (pgn->children, (ClientData) cgn)) != NILLNODE) { + Lst_Remove(pgn->children, ln); + pgn->unmade--; + } + } + return (0); +} + +static int +MakeHandleUse (cgnp, pgnp) + ClientData cgnp; /* the child we've just examined */ + ClientData pgnp; /* the current parent */ +{ + GNode *cgn = (GNode *) cgnp; + GNode *pgn = (GNode *) pgnp; + + if (cgn->type & OP_MARK) + return (0); + cgn->type |= OP_MARK; + + return Make_HandleUse(cgn, pgn); +} + + +/*- + *----------------------------------------------------------------------- + * Make_Recheck -- + * Check the modification time of a gnode, and update it as described + * in the comments below. + * + * Results: + * returns 0 if the gnode does not exist, or it's filesystem + * time if it does. + * + * Side Effects: + * the gnode's modification time and path name are affected. + * + *----------------------------------------------------------------------- + */ +time_t +Make_Recheck (gn) + GNode *gn; +{ + time_t mtime = Dir_MTime(gn); + +#ifndef RECHECK + /* + * We can't re-stat the thing, but we can at least take care of rules + * where a target depends on a source that actually creates the + * target, but only if it has changed, e.g. + * + * parse.h : parse.o + * + * parse.o : parse.y + * yacc -d parse.y + * cc -c y.tab.c + * mv y.tab.o parse.o + * cmp -s y.tab.h parse.h || mv y.tab.h parse.h + * + * In this case, if the definitions produced by yacc haven't changed + * from before, parse.h won't have been updated and gn->mtime will + * reflect the current modification time for parse.h. This is + * something of a kludge, I admit, but it's a useful one.. + * XXX: People like to use a rule like + * + * FRC: + * + * To force things that depend on FRC to be made, so we have to + * check for gn->children being empty as well... + */ + if (!Lst_IsEmpty(gn->commands) || Lst_IsEmpty(gn->children)) { + gn->mtime = now; + } +#else + /* + * This is what Make does and it's actually a good thing, as it + * allows rules like + * + * cmp -s y.tab.h parse.h || cp y.tab.h parse.h + * + * to function as intended. Unfortunately, thanks to the stateless + * nature of NFS (by which I mean the loose coupling of two clients + * using the same file from a common server), there are times + * when the modification time of a file created on a remote + * machine will not be modified before the local stat() implied by + * the Dir_MTime occurs, thus leading us to believe that the file + * is unchanged, wreaking havoc with files that depend on this one. + * + * I have decided it is better to make too much than to make too + * little, so this stuff is commented out unless you're sure it's ok. + * -- ardeb 1/12/88 + */ + /* + * Christos, 4/9/92: If we are saving commands pretend that + * the target is made now. Otherwise archives with ... rules + * don't work! + */ + if (NoExecute(gn) || + (gn->type & OP_SAVE_CMDS) || mtime == 0) { + if (DEBUG(MAKE)) { + printf("update time to now: %s\n", Targ_FmtTime(gn->mtime)); + } + gn->mtime = now; + } + else { + if (DEBUG(MAKE)) { + printf("current update time: %s\n", Targ_FmtTime(gn->mtime)); + } + } +#endif + return mtime; +} + +/*- + *----------------------------------------------------------------------- + * Make_Update -- + * Perform update on the parents of a node. Used by JobFinish once + * a node has been dealt with and by MakeStartJobs if it finds an + * up-to-date node. + * + * Results: + * Always returns 0 + * + * Side Effects: + * The unmade field of pgn is decremented and pgn may be placed on + * the toBeMade queue if this field becomes 0. + * + * If the child was made, the parent's flag CHILDMADE field will be + * set true and its cmtime set to now. + * + * If the child is not up-to-date and still does not exist, + * set the FORCE flag on the parents. + * + * If the child wasn't made, the cmtime field of the parent will be + * altered if the child's mtime is big enough. + * + * Finally, if the child is the implied source for the parent, the + * parent's IMPSRC variable is set appropriately. + * + *----------------------------------------------------------------------- + */ +void +Make_Update (cgn) + register GNode *cgn; /* the child node */ +{ + register GNode *pgn; /* the parent node */ + register char *cname; /* the child's name */ + register LstNode ln; /* Element in parents and iParents lists */ + time_t mtime = -1; + char *p1; + + cname = Var_Value (TARGET, cgn, &p1); + if (p1) + free(p1); + + /* + * If the child was actually made, see what its modification time is + * now -- some rules won't actually update the file. If the file still + * doesn't exist, make its mtime now. + */ + if (cgn->made != UPTODATE) { + mtime = Make_Recheck(cgn); + } + + if (Lst_Open (cgn->parents) == SUCCESS) { + while ((ln = Lst_Next (cgn->parents)) != NILLNODE) { + pgn = (GNode *)Lst_Datum (ln); + if (mtime == 0) + pgn->flags |= FORCE; + if (pgn->flags & REMAKE) { + pgn->unmade -= 1; + + if ( ! (cgn->type & (OP_EXEC|OP_USE|OP_USEBEFORE))) { + if (cgn->made == MADE) + pgn->flags |= CHILDMADE; + (void)Make_TimeStamp (pgn, cgn); + } + if (pgn->unmade == 0) { + /* + * Queue the node up -- any unmade predecessors will + * be dealt with in MakeStartJobs. + */ + (void)Lst_EnQueue (toBeMade, (ClientData)pgn); + } else if (pgn->unmade < 0) { + Error ("Graph cycles through %s", pgn->name); + } + } + } + Lst_Close (cgn->parents); + } + /* + * Deal with successor nodes. If any is marked for making and has an unmade + * count of 0, has not been made and isn't in the examination queue, + * it means we need to place it in the queue as it restrained itself + * before. + */ + for (ln = Lst_First(cgn->successors); ln != NILLNODE; ln = Lst_Succ(ln)) { + GNode *succ = (GNode *)Lst_Datum(ln); + + if ((succ->flags & REMAKE) && succ->unmade == 0 && succ->made == UNMADE && + Lst_Member(toBeMade, (ClientData)succ) == NILLNODE) + { + (void)Lst_EnQueue(toBeMade, (ClientData)succ); + } + } + + /* + * Set the .PREFIX and .IMPSRC variables for all the implied parents + * of this node. + */ + if (Lst_Open (cgn->iParents) == SUCCESS) { + char *p1; + char *cpref = Var_Value(PREFIX, cgn, &p1); + + while ((ln = Lst_Next (cgn->iParents)) != NILLNODE) { + pgn = (GNode *)Lst_Datum (ln); + if (pgn->flags & REMAKE) { + Var_Set (IMPSRC, cname, pgn, 0); + if (cpref != NULL) + Var_Set (PREFIX, cpref, pgn, 0); + } + } + if (p1) + free(p1); + Lst_Close (cgn->iParents); + } +} + +/*- + *----------------------------------------------------------------------- + * MakeAddAllSrc -- + * Add a child's name to the ALLSRC and OODATE variables of the given + * node. Called from Make_DoAllVar via Lst_ForEach. A child is added only + * if it has not been given the .EXEC, .USE or .INVISIBLE attributes. + * .EXEC and .USE children are very rarely going to be files, so... + * A child is added to the OODATE variable if its modification time is + * later than that of its parent, as defined by Make, except if the + * parent is a .JOIN node. In that case, it is only added to the OODATE + * variable if it was actually made (since .JOIN nodes don't have + * modification times, the comparison is rather unfair...).. + * + * Results: + * Always returns 0 + * + * Side Effects: + * The ALLSRC variable for the given node is extended. + *----------------------------------------------------------------------- + */ +static int +MakeUnmark (cgnp, pgnp) + ClientData cgnp; + ClientData pgnp; +{ + GNode *cgn = (GNode *) cgnp; + + cgn->type &= ~OP_MARK; + return (0); +} + +static int +MakeAddAllSrc (cgnp, pgnp) + ClientData cgnp; /* The child to add */ + ClientData pgnp; /* The parent to whose ALLSRC variable it should be */ + /* added */ +{ + GNode *cgn = (GNode *) cgnp; + GNode *pgn = (GNode *) pgnp; + + if (cgn->type & OP_MARK) + return (0); + cgn->type |= OP_MARK; + + if ((cgn->type & (OP_EXEC|OP_USE|OP_USEBEFORE|OP_INVISIBLE)) == 0) { + char *child; + char *p1 = NULL; + + if (cgn->type & OP_ARCHV) + child = Var_Value (MEMBER, cgn, &p1); + else + child = cgn->path ? cgn->path : cgn->name; + Var_Append (ALLSRC, child, pgn); + if (pgn->type & OP_JOIN) { + if (cgn->made == MADE) { + Var_Append(OODATE, child, pgn); + } + } else if ((pgn->mtime < cgn->mtime) || + (cgn->mtime >= now && cgn->made == MADE)) + { + /* + * It goes in the OODATE variable if the parent is younger than the + * child or if the child has been modified more recently than + * the start of the make. This is to keep pmake from getting + * confused if something else updates the parent after the + * make starts (shouldn't happen, I know, but sometimes it + * does). In such a case, if we've updated the kid, the parent + * is likely to have a modification time later than that of + * the kid and anything that relies on the OODATE variable will + * be hosed. + * + * XXX: This will cause all made children to go in the OODATE + * variable, even if they're not touched, if RECHECK isn't defined, + * since cgn->mtime is set to now in Make_Update. According to + * some people, this is good... + */ + Var_Append(OODATE, child, pgn); + } + if (p1) + free(p1); + } + return (0); +} + +/*- + *----------------------------------------------------------------------- + * Make_DoAllVar -- + * Set up the ALLSRC and OODATE variables. Sad to say, it must be + * done separately, rather than while traversing the graph. This is + * because Make defined OODATE to contain all sources whose modification + * times were later than that of the target, *not* those sources that + * were out-of-date. Since in both compatibility and native modes, + * the modification time of the parent isn't found until the child + * has been dealt with, we have to wait until now to fill in the + * variable. As for ALLSRC, the ordering is important and not + * guaranteed when in native mode, so it must be set here, too. + * + * Results: + * None + * + * Side Effects: + * The ALLSRC and OODATE variables of the given node is filled in. + * If the node is a .JOIN node, its TARGET variable will be set to + * match its ALLSRC variable. + *----------------------------------------------------------------------- + */ +void +Make_DoAllVar (gn) + GNode *gn; +{ + Lst_ForEach (gn->children, MakeUnmark, (ClientData) gn); + Lst_ForEach (gn->children, MakeAddAllSrc, (ClientData) gn); + + if (!Var_Exists (OODATE, gn)) { + Var_Set (OODATE, "", gn, 0); + } + if (!Var_Exists (ALLSRC, gn)) { + Var_Set (ALLSRC, "", gn, 0); + } + + if (gn->type & OP_JOIN) { + char *p1; + Var_Set (TARGET, Var_Value (ALLSRC, gn, &p1), gn, 0); + if (p1) + free(p1); + } +} + +/*- + *----------------------------------------------------------------------- + * MakeStartJobs -- + * Start as many jobs as possible. + * + * Results: + * If the query flag was given to pmake, no job will be started, + * but as soon as an out-of-date target is found, this function + * returns TRUE. At all other times, this function returns FALSE. + * + * Side Effects: + * Nodes are removed from the toBeMade queue and job table slots + * are filled. + * + *----------------------------------------------------------------------- + */ +static Boolean +MakeStartJobs () +{ + register GNode *gn; + + while (!Lst_IsEmpty (toBeMade)) { + gn = (GNode *) Lst_DeQueue (toBeMade); + if (DEBUG(MAKE)) { + printf ("Examining %s...", gn->name); + } + /* + * Make sure any and all predecessors that are going to be made, + * have been. + */ + if (!Lst_IsEmpty(gn->preds)) { + LstNode ln; + + for (ln = Lst_First(gn->preds); ln != NILLNODE; ln = Lst_Succ(ln)){ + GNode *pgn = (GNode *)Lst_Datum(ln); + + if ((pgn->flags & REMAKE) && pgn->made == UNMADE) { + if (DEBUG(MAKE)) { + printf("predecessor %s not made yet.\n", pgn->name); + } + break; + } + } + /* + * If ln isn't nil, there's a predecessor as yet unmade, so we + * just drop this node on the floor. When the node in question + * has been made, it will notice this node as being ready to + * make but as yet unmade and will place the node on the queue. + */ + if (ln != NILLNODE) { + continue; + } + } + + if (!Job_TokenWithdraw()) { + Lst_AtFront(toBeMade, gn); + break; + } + + numNodes--; + if (Make_OODate (gn)) { + if (DEBUG(MAKE)) { + printf ("out-of-date\n"); + } + if (queryFlag) { + return (TRUE); + } + Make_DoAllVar (gn); + Job_Make (gn); + } else { + if (DEBUG(MAKE)) { + printf ("up-to-date\n"); + } + gn->made = UPTODATE; + if (gn->type & OP_JOIN) { + /* + * Even for an up-to-date .JOIN node, we need it to have its + * context variables so references to it get the correct + * value for .TARGET when building up the context variables + * of its parent(s)... + */ + Make_DoAllVar (gn); + } + Job_TokenReturn(); + Make_Update (gn); + } + } + return (FALSE); +} + +/*- + *----------------------------------------------------------------------- + * MakePrintStatus -- + * Print the status of a top-level node, viz. it being up-to-date + * already or not created due to an error in a lower level. + * Callback function for Make_Run via Lst_ForEach. + * + * Results: + * Always returns 0. + * + * Side Effects: + * A message may be printed. + * + *----------------------------------------------------------------------- + */ +static int +MakePrintStatus(gnp, cyclep) + ClientData gnp; /* Node to examine */ + ClientData cyclep; /* True if gn->unmade being non-zero implies + * a cycle in the graph, not an error in an + * inferior */ +{ + GNode *gn = (GNode *) gnp; + Boolean cycle = *(Boolean *) cyclep; + if (gn->made == UPTODATE) { + printf ("`%s' is up to date.\n", gn->name); + } else if (gn->unmade != 0) { + if (cycle) { + Boolean t = TRUE; + /* + * If printing cycles and came to one that has unmade children, + * print out the cycle by recursing on its children. Note a + * cycle like: + * a : b + * b : c + * c : b + * will cause this to erroneously complain about a being in + * the cycle, but this is a good approximation. + */ + if (gn->made == CYCLE) { + Error("Graph cycles through `%s'", gn->name); + gn->made = ENDCYCLE; + Lst_ForEach(gn->children, MakePrintStatus, (ClientData) &t); + gn->made = UNMADE; + } else if (gn->made != ENDCYCLE) { + gn->made = CYCLE; + Lst_ForEach(gn->children, MakePrintStatus, (ClientData) &t); + } + } else { + printf ("`%s' not remade because of errors.\n", gn->name); + } + } + return (0); +} + + +/*- + *----------------------------------------------------------------------- + * Make_ExpandUse -- + * Expand .USE nodes and create a new targets list + * Results: + * The new list of targets. + * + * Side Effects: + * numNodes is set to the number of elements in the list of targets. + *----------------------------------------------------------------------- + */ +Lst +Make_ExpandUse (targs) + Lst targs; /* the initial list of targets */ +{ + register GNode *gn; /* a temporary pointer */ + register Lst examine; /* List of targets to examine */ + register Lst ntargs; /* List of new targets to be made */ + + ntargs = Lst_Init (FALSE); + + examine = Lst_Duplicate(targs, NOCOPY); + numNodes = 0; + + /* + * Make an initial downward pass over the graph, marking nodes to be made + * as we go down. We call Suff_FindDeps to find where a node is and + * to get some children for it if it has none and also has no commands. + * If the node is a leaf, we stick it on the toBeMade queue to + * be looked at in a minute, otherwise we add its children to our queue + * and go on about our business. + */ + while (!Lst_IsEmpty (examine)) { + gn = (GNode *) Lst_DeQueue (examine); + + if ((gn->type & OP_DOUBLEDEP) && !Lst_IsEmpty (gn->cohorts)) { + Lst new; + new = Lst_Duplicate (gn->cohorts, NOCOPY); + Lst_Concat (new, examine, LST_CONCLINK); + examine = new; + } + + if ((gn->flags & REMAKE) == 0) { + gn->flags |= REMAKE; + numNodes++; + + /* + * Apply any .USE rules before looking for implicit dependencies + * to make sure everything has commands that should... + * Make sure that the TARGET is set, so that we can make + * expansions. + */ + if (gn->type & OP_ARCHV) { + char *eoa, *eon; + eoa = strchr(gn->name, '('); + eon = strchr(gn->name, ')'); + if (eoa == NULL || eon == NULL) + continue; + *eoa = '\0'; + *eon = '\0'; + Var_Set (MEMBER, eoa + 1, gn, 0); + Var_Set (ARCHIVE, gn->name, gn, 0); + *eoa = '('; + *eon = ')'; + } + + (void)Dir_MTime(gn); + Var_Set (TARGET, gn->path ? gn->path : gn->name, gn, 0); + Lst_ForEach (gn->children, MakeUnmark, (ClientData)gn); + Lst_ForEach (gn->children, MakeHandleUse, (ClientData)gn); + Suff_FindDeps (gn); + + if (gn->unmade != 0 && (gn->type & OP_MADE) == 0) { + Lst_ForEach (gn->children, MakeAddChild, (ClientData)examine); + } else { + (void)Lst_EnQueue (ntargs, (ClientData)gn); + if (gn->type & OP_MADE) + Lst_ForEach (gn->children, MakeFindChild, (ClientData)gn); + } + } + } + + Lst_Destroy (examine, NOFREE); + return ntargs; +} + +/*- + *----------------------------------------------------------------------- + * Make_Run -- + * Initialize the nodes to remake and the list of nodes which are + * ready to be made by doing a breadth-first traversal of the graph + * starting from the nodes in the given list. Once this traversal + * is finished, all the 'leaves' of the graph are in the toBeMade + * queue. + * Using this queue and the Job module, work back up the graph, + * calling on MakeStartJobs to keep the job table as full as + * possible. + * + * Results: + * TRUE if work was done. FALSE otherwise. + * + * Side Effects: + * The make field of all nodes involved in the creation of the given + * targets is set to 1. The toBeMade list is set to contain all the + * 'leaves' of these subgraphs. + *----------------------------------------------------------------------- + */ +Boolean +Make_Run (targs) + Lst targs; /* the initial list of targets */ +{ + int errors; /* Number of errors the Job module reports */ + + toBeMade = Make_ExpandUse (targs); + + if (queryFlag) { + /* + * We wouldn't do any work unless we could start some jobs in the + * next loop... (we won't actually start any, of course, this is just + * to see if any of the targets was out of date) + */ + return (MakeStartJobs()); + } else { + /* + * Initialization. At the moment, no jobs are running and until some + * get started, nothing will happen since the remaining upward + * traversal of the graph is performed by the routines in job.c upon + * the finishing of a job. So we fill the Job table as much as we can + * before going into our loop. + */ + (void) MakeStartJobs(); + } + + /* + * Main Loop: The idea here is that the ending of jobs will take + * care of the maintenance of data structures and the waiting for output + * will cause us to be idle most of the time while our children run as + * much as possible. Because the job table is kept as full as possible, + * the only time when it will be empty is when all the jobs which need + * running have been run, so that is the end condition of this loop. + * Note that the Job module will exit if there were any errors unless the + * keepgoing flag was given. + */ + while (!Lst_IsEmpty(toBeMade) || !Job_Empty ()) { + Job_CatchOutput (); + Job_CatchChildren (!usePipes); + (void)MakeStartJobs(); + } + + errors = Job_Finish(); + + /* + * Print the final status of each target. E.g. if it wasn't made + * because some inferior reported an error. + */ + errors = ((errors == 0) && (numNodes != 0)); + Lst_ForEach(targs, MakePrintStatus, (ClientData) &errors); + + return (TRUE); +} diff --git a/bootstrap/bmake/make.h b/bootstrap/bmake/make.h new file mode 100644 index 00000000000..fcd79517711 --- /dev/null +++ b/bootstrap/bmake/make.h @@ -0,0 +1,438 @@ +/* $NetBSD: make.h,v 1.1.1.1 2004/03/11 13:04:10 grant Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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: @(#)make.h 8.3 (Berkeley) 6/13/95 + */ + +/*- + * make.h -- + * The global definitions for pmake + */ + +#ifndef _MAKE_H_ +#define _MAKE_H_ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#ifdef HAVE_STRING_H +#include +#else +#include +#endif +#include +#ifdef HAVE_SYS_CDEFS_H +#include +#else +# ifndef __P +# if defined(__STDC__) || defined(__cplusplus) +# define __P(protos) protos /* full-blown ANSI C */ +# else +# define __P(protos) () /* traditional C preprocessor */ +# endif +# endif +# ifndef __GNUC__ +# ifndef __inline +# define __inline +# endif +# endif +# ifndef __STDC__ +# ifndef const +# define const +# endif +# ifndef volatile +# define volatile +# endif +# endif +#endif +#ifdef __STDC__ +#include +#include +#endif +#include "sprite.h" +#include "lst.h" +#include "hash.h" +#include "make-conf.h" +#include "buf.h" + +/* + * some vendors don't have this --sjg + */ +#if defined(S_IFDIR) && !defined(S_ISDIR) +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif + +#if defined(sun) && (defined(__svr4__) || defined(__SVR4)) +#define POSIX_SIGNALS +#endif + +/*- + * The structure for an individual graph node. Each node has several + * pieces of data associated with it. + * 1) the name of the target it describes + * 2) the location of the target file in the file system. + * 3) the type of operator used to define its sources (qv. parse.c) + * 4) whether it is involved in this invocation of make + * 5) whether the target has been remade + * 6) whether any of its children has been remade + * 7) the number of its children that are, as yet, unmade + * 8) its modification time + * 9) the modification time of its youngest child (qv. make.c) + * 10) a list of nodes for which this is a source + * 11) a list of nodes on which this depends + * 12) a list of nodes that depend on this, as gleaned from the + * transformation rules. + * 13) a list of nodes of the same name created by the :: operator + * 14) a list of nodes that must be made (if they're made) before + * this node can be, but that do no enter into the datedness of + * this node. + * 15) a list of nodes that must be made (if they're made) after + * this node is, but that do not depend on this node, in the + * normal sense. + * 16) a Lst of ``local'' variables that are specific to this target + * and this target only (qv. var.c [$@ $< $?, etc.]) + * 17) a Lst of strings that are commands to be given to a shell + * to create this target. + */ +typedef struct GNode { + char *name; /* The target's name */ + char *uname; /* The unexpanded name of a .USE node */ + char *path; /* The full pathname of the file */ + int type; /* Its type (see the OP flags, below) */ + int order; /* Its wait weight */ + + int flags; +#define REMAKE 0x1 /* this target needs to be remade */ +#define CHILDMADE 0x2 /* children of this target were made */ +#define FORCE 0x4 /* children don't exist, and we pretend made */ + enum { + UNMADE, BEINGMADE, MADE, UPTODATE, ERROR, ABORTED, + CYCLE, ENDCYCLE + } made; /* Set to reflect the state of processing + * on this node: + * UNMADE - Not examined yet + * BEINGMADE - Target is already being made. + * Indicates a cycle in the graph. (compat + * mode only) + * MADE - Was out-of-date and has been made + * UPTODATE - Was already up-to-date + * ERROR - An error occurred while it was being + * made (used only in compat mode) + * ABORTED - The target was aborted due to + * an error making an inferior (compat). + * CYCLE - Marked as potentially being part of + * a graph cycle. If we come back to a + * node marked this way, it is printed + * and 'made' is changed to ENDCYCLE. + * ENDCYCLE - the cycle has been completely + * printed. Go back and unmark all its + * members. + */ + int unmade; /* The number of unmade children */ + + time_t mtime; /* Its modification time */ + time_t cmtime; /* The modification time of its youngest + * child */ + + Lst iParents; /* Links to parents for which this is an + * implied source, if any */ + Lst cohorts; /* Other nodes for the :: operator */ + Lst parents; /* Nodes that depend on this one */ + Lst children; /* Nodes on which this one depends */ + Lst successors; /* Nodes that must be made after this one */ + Lst preds; /* Nodes that must be made before this one */ + + Hash_Table context; /* The local variables */ + Lst commands; /* Creation commands */ + + struct _Suff *suffix; /* Suffix for the node (determined by + * Suff_FindDeps and opaque to everyone + * but the Suff module) */ + char *fname; /* filename where the GNode got defined */ + int lineno; /* line number where the GNode got defined */ +} GNode; + +/* + * Manifest constants + */ +#define NILGNODE ((GNode *) NIL) + +/* + * The OP_ constants are used when parsing a dependency line as a way of + * communicating to other parts of the program the way in which a target + * should be made. These constants are bitwise-OR'ed together and + * placed in the 'type' field of each node. Any node that has + * a 'type' field which satisfies the OP_NOP function was never never on + * the lefthand side of an operator, though it may have been on the + * righthand side... + */ +#define OP_DEPENDS 0x00000001 /* Execution of commands depends on + * kids (:) */ +#define OP_FORCE 0x00000002 /* Always execute commands (!) */ +#define OP_DOUBLEDEP 0x00000004 /* Execution of commands depends on kids + * per line (::) */ +#define OP_OPMASK (OP_DEPENDS|OP_FORCE|OP_DOUBLEDEP) + +#define OP_OPTIONAL 0x00000008 /* Don't care if the target doesn't + * exist and can't be created */ +#define OP_USE 0x00000010 /* Use associated commands for parents */ +#define OP_EXEC 0x00000020 /* Target is never out of date, but always + * execute commands anyway. Its time + * doesn't matter, so it has none...sort + * of */ +#define OP_IGNORE 0x00000040 /* Ignore errors when creating the node */ +#define OP_PRECIOUS 0x00000080 /* Don't remove the target when + * interrupted */ +#define OP_SILENT 0x00000100 /* Don't echo commands when executed */ +#define OP_MAKE 0x00000200 /* Target is a recurrsive make so its + * commands should always be executed when + * it is out of date, regardless of the + * state of the -n or -t flags */ +#define OP_JOIN 0x00000400 /* Target is out-of-date only if any of its + * children was out-of-date */ +#define OP_MADE 0x00000800 /* Assume the node is already made; even if + * it really is out of date */ +#define OP_USEBEFORE 0x00002000 /* Like .USE, only prepend commands */ +#define OP_INVISIBLE 0x00004000 /* The node is invisible to its parents. + * I.e. it doesn't show up in the parents's + * local variables. */ +#define OP_NOTMAIN 0x00008000 /* The node is exempt from normal 'main + * target' processing in parse.c */ +#define OP_PHONY 0x00010000 /* Not a file target; run always */ +#define OP_NOPATH 0x00020000 /* Don't search for file in the path */ +/* Attributes applied by PMake */ +#define OP_TRANSFORM 0x80000000 /* The node is a transformation rule */ +#define OP_MEMBER 0x40000000 /* Target is a member of an archive */ +#define OP_LIB 0x20000000 /* Target is a library */ +#define OP_ARCHV 0x10000000 /* Target is an archive construct */ +#define OP_HAS_COMMANDS 0x08000000 /* Target has all the commands it should. + * Used when parsing to catch multiple + * commands for a target */ +#define OP_SAVE_CMDS 0x04000000 /* Saving commands on .END (Compat) */ +#define OP_DEPS_FOUND 0x02000000 /* Already processed by Suff_FindDeps */ +#define OP_MARK 0x01000000 /* Node found while expanding .ALLSRC */ + +#define NoExecute(gn) ((gn->type & OP_MAKE) ? noRecursiveExecute : noExecute) +/* + * OP_NOP will return TRUE if the node with the given type was not the + * object of a dependency operator + */ +#define OP_NOP(t) (((t) & OP_OPMASK) == 0x00000000) + +#define OP_NOTARGET (OP_NOTMAIN|OP_USE|OP_EXEC|OP_TRANSFORM) + +/* + * The TARG_ constants are used when calling the Targ_FindNode and + * Targ_FindList functions in targ.c. They simply tell the functions what to + * do if the desired node(s) is (are) not found. If the TARG_CREATE constant + * is given, a new, empty node will be created for the target, placed in the + * table of all targets and its address returned. If TARG_NOCREATE is given, + * a NIL pointer will be returned. + */ +#define TARG_CREATE 0x01 /* create node if not found */ +#define TARG_NOCREATE 0x00 /* don't create it */ + +/* + * There are several places where expandable buffers are used (parse.c and + * var.c). This constant is merely the starting point for those buffers. If + * lines tend to be much shorter than this, it would be best to reduce BSIZE. + * If longer, it should be increased. Reducing it will cause more copying to + * be done for longer lines, but will save space for shorter ones. In any + * case, it ought to be a power of two simply because most storage allocation + * schemes allocate in powers of two. + */ +#define MAKE_BSIZE 256 /* starting size for expandable buffers */ + +/* + * These constants are all used by the Str_Concat function to decide how the + * final string should look. If STR_ADDSPACE is given, a space will be + * placed between the two strings. If STR_ADDSLASH is given, a '/' will + * be used instead of a space. If neither is given, no intervening characters + * will be placed between the two strings in the final output. If the + * STR_DOFREE bit is set, the two input strings will be freed before + * Str_Concat returns. + */ +#define STR_ADDSPACE 0x01 /* add a space when Str_Concat'ing */ +#define STR_DOFREE 0x02 /* free source strings after concatenation */ +#define STR_ADDSLASH 0x04 /* add a slash when Str_Concat'ing */ + +/* + * Error levels for parsing. PARSE_FATAL means the process cannot continue + * once the makefile has been parsed. PARSE_WARNING means it can. Passed + * as the first argument to Parse_Error. + */ +#define PARSE_WARNING 2 +#define PARSE_FATAL 1 + +/* + * Values returned by Cond_Eval. + */ +#define COND_PARSE 0 /* Parse the next lines */ +#define COND_SKIP 1 /* Skip the next lines */ +#define COND_INVALID 2 /* Not a conditional statement */ + +/* + * Definitions for the "local" variables. Used only for clarity. + */ +#define TARGET "@" /* Target of dependency */ +#define OODATE "?" /* All out-of-date sources */ +#define ALLSRC ">" /* All sources */ +#define IMPSRC "<" /* Source implied by transformation */ +#define PREFIX "*" /* Common prefix */ +#define ARCHIVE "!" /* Archive in "archive(member)" syntax */ +#define MEMBER "%" /* Member in "archive(member)" syntax */ + +#define FTARGET "@F" /* file part of TARGET */ +#define DTARGET "@D" /* directory part of TARGET */ +#define FIMPSRC " +#else +/* + * It sucks that we have to hard code a path like this. + * But systems that have a sys/cdefs.h that don't use gcc + * should be few. + */ +# include "/usr/include/sys/cdefs.h" +#endif +/* + * We are about to [re]define these + */ +#undef __P +#undef _SYS_CDEFS_H_ +#endif + +#define _SYS_CDEFS_H_ + +#ifdef NetBSD +#include +#endif + +#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 + +#define __const const /* define reserved names to standard */ +#define __signed signed +#define __volatile volatile +#if defined(__cplusplus) +#define __inline inline /* convert to C++ keyword */ +#else +#ifndef __GNUC__ +#define __inline /* delete GCC keyword */ +#endif /* !__GNUC__ */ +#endif /* !__cplusplus */ + +#else /* !(__STDC__ || __cplusplus) */ +#define __P(protos) () /* traditional C preprocessor */ +#define __CONCAT(x,y) x/**/y +#define __STRING(x) "x" + +#ifndef __GNUC__ +#define __const /* delete pseudo-ANSI C keywords */ +#define __inline +#define __signed +#define __volatile +#endif /* !__GNUC__ */ + +/* + * In non-ANSI C environments, new programs will want ANSI-only C keywords + * deleted from the program and old programs will want them left alone. + * Programs using the ANSI C keywords const, inline etc. as normal + * identifiers should define -DNO_ANSI_KEYWORDS. + */ +#ifndef NO_ANSI_KEYWORDS +#define const __const /* convert ANSI C keywords */ +#define inline __inline +#define signed __signed +#define volatile __volatile +#endif /* !NO_ANSI_KEYWORDS */ +#endif /* !(__STDC__ || __cplusplus) */ + +/* + * GCC1 and some versions of GCC2 declare dead (non-returning) and + * pure (no side effects) functions using "volatile" and "const"; + * unfortunately, these then cause warnings under "-ansi -pedantic". + * GCC2 uses a new, peculiar __attribute__((attrs)) style. All of + * these work for GNU C++ (modulo a slight glitch in the C++ grammar + * in the distribution version of 2.5.5). + */ +#if !defined(__GNUC__) || __GNUC__ < 2 || \ + (__GNUC__ == 2 && __GNUC_MINOR__ < 5) +#define __attribute__(x) /* delete __attribute__ if non-gcc or gcc1 */ +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) +#define __dead __volatile +#define __pure __const +#endif +#endif + +#ifdef sun386 +# define __attribute__(x) +#endif + +#ifdef __KPRINTF_ATTRIBUTE__ +#define __kprintf_attribute__(a) __attribute__(a) +#else +#define __kprintf_attribute__(a) +#endif + +/* Delete pseudo-keywords wherever they are not available or needed. */ +#ifndef __dead +#define __dead +#define __pure +#endif + +#define __IDSTRING(name,string) \ + static const char name[] __attribute__((__unused__)) = string + +#ifndef __RCSID +#define __RCSID(s) __IDSTRING(rcsid,s) +#endif + +#ifndef __COPYRIGHT +#define __COPYRIGHT(s) __IDSTRING(copyright,s) +#endif + +#endif /* !_SYS_CDEFS_H_ */ diff --git a/bootstrap/bmake/mk/bsd.README b/bootstrap/bmake/mk/bsd.README new file mode 100644 index 00000000000..ff7c16692d7 --- /dev/null +++ b/bootstrap/bmake/mk/bsd.README @@ -0,0 +1,683 @@ +# $NetBSD: bsd.README,v 1.1.1.1 2004/03/11 13:04:17 grant Exp $ +# @(#)bsd.README 8.2 (Berkeley) 4/2/94 + +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". + +Note, this file is not intended to replace reading through the .mk +files for anything tricky. + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +RANDOM THINGS WORTH KNOWING: + +The files are simply C-style #include files, and pretty much behave like +you'd expect. The syntax is slightly different in that a single '.' is +used instead of the hash mark, i.e. ".include ". + +One difference that will save you lots of debugging time is that inclusion +of the file is normally done at the *end* of the Makefile. The reason for +this is because .mk files often modify variables and behavior based on the +values of variables set in the Makefile. To make this work, remember that +the FIRST target found is the target that is used, i.e. if the Makefile has: + + a: + echo a + a: + echo a number two + +the command "make a" will echo "a". To make things confusing, the SECOND +variable assignment is the overriding one, i.e. if the Makefile has: + + a= foo + a= bar + + b: + echo ${a} + +the command "make b" will echo "bar". This is for compatibility with the +way the V7 make behaved. + +It's fairly difficult to make the BSD .mk files work when you're building +multiple programs in a single directory. It's a lot easier to split up the +programs than to deal with the problem. Most of the agony comes from making +the "obj" directory stuff work right, not because we switched to a new version +of make. So, don't get mad at us, figure out a better way to handle multiple +architectures so we can quit using the symbolic link stuff. (Imake doesn't +count.) + +The file .depend in the source directory is expected to contain dependencies +for the source files. This file is read automatically by make after reading +the Makefile. + +The variable DESTDIR works as before. It's not set anywhere but will change +the tree where the file gets installed. + +The profiled libraries are no longer built in a different directory than +the regular libraries. A new suffix, ".po", is used to denote a profiled +object, and ".so" denotes a shared (position-independent) object. + +The following variables that control how things are made/installed that +are not set by default. These should not be set by Makefiles; they're for +the user to define in MAKECONF (see bsd.own.mk, below) or on the make(1) +command line: + +BUILD If defined, 'make install' checks that the targets in the + source directories are up-to-date and remakes them if they + are out of date, instead of blindly trying to install + out of date or non-existent targets. + +UPDATE If defined, 'make install' only installs targets that are + more recently modified in the source directories that their + installed counterparts. + +UNPRIVILEGED If defined, don't set the owner/group/mode when installing + files or directories. This allows a non-root "make install". + +MKCATPAGES If "no", don't build or install the catman pages. + +MKDOC If "no", don't build or install the documentation. + +MKINFO If "no", don't build or install Info documentation from + Texinfo source files. + +MKLINT If "no", don't build or install the lint libraries. + +MKMAN If "no", don't build or install the man or catman pages. + Also acts as "MKCATPAGES=no" + +MKNLS If "no", don't build or install the NLS files and locale + definition files. + +MKOBJ If "no", don't enable the rule which creates objdirs. + "yes" by default. + +MKOBJDIRS If "no", don't create objdirs during a "make build". + "no" by default. + +MKPIC If "no", don't build or install shared libraries. + +MKPICINSTALL If "no", don't install the *_pic.a libraries. + +MKPROFILE If "no", don't build or install the profiling libraries. + +MKSHARE If "no", act as "MKCATPAGES=no MKDOC=no MKINFO=no MKMAN=no + MKNLS=no". I.e, don't build catman pages, documentation, + Info documentation, man pages, NLS files, ... + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +The include file has the default rules for all makes, in the BSD +environment or otherwise. You probably don't want to touch this file. +If you intend to run a cross build, you will need to supply the following +host tools, and configure the following variables properly: + +OBJCOPY objcopy - copy and translate object files + +STRIP strip - Discard symbols from object files + +CONFIG config - build kernel compilation directories + +RPCGEN rpcgen - Remote Procedure Call (RPC) protocol compiler + +MKLOCALE mklocale - make LC_CTYPE locale files + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +The include file handles installing manual pages and their +links. + +It has a two targets: + + maninstall: + Install the manual page sources and their links. + catinstall: + Install the preformatted manual pages and their links. + +It sets/uses the following variables: + +MANDIR Base path for manual installation. + +MANGRP Manual group. + +MANOWN Manual owner. + +MANMODE Manual mode. + +MANSUBDIR Subdirectory under the manual page section, i.e. "/vax" + or "/tahoe" for machine specific manual pages. + +MAN The manual pages to be installed (use a .1 - .9 suffix). + +MLINKS List of manual page links (using a .1 - .9 suffix). The + linked-to file must come first, the linked file second, + and there may be multiple pairs. The files are soft-linked. + +The include file includes a file named "../Makefile.inc" if +it exists. + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +The include file contains source tree configuration parameters, +such as the owners, groups, etc. for both manual pages and binaries, and +a few global "feature configuration" parameters. + +It has no targets. + +To get system-specific configuration parameters, bsd.own.mk will try to +include the file specified by the "MAKECONF" variable. If MAKECONF is not +set, or no such file exists, the system make configuration file, /etc/mk.conf +is included. These files may define any of the variables described below. + +bsd.own.mk sets the following variables, if they are not already defined +(defaults are in brackets): + +BSDSRCDIR The real path to the system sources, so that 'make obj' + will work correctly. [/usr/src] + +BSDOBJDIR The real path to the system 'obj' tree, so that 'make obj' + will work correctly. [/usr/obj] + +BINGRP Binary group. [wheel] + +BINOWN Binary owner. [root] + +BINMODE Binary mode. [555] + +NONBINMODE Mode for non-executable files. [444] + +MANDIR Base path for manual installation. [/usr/share/man/cat] + +MANGRP Manual group. [wheel] + +MANOWN Manual owner. [root] + +MANMODE Manual mode. [${NONBINMODE}] + +MANINSTALL Manual installation type: maninstall, catinstall, or both + +LDSTATIC Control program linking; if set blank, link everything + dynamically. If set to "-static", link everything statically. + If not set, programs link according to their makefile. + +LIBDIR Base path for library installation. [/usr/lib] + +LINTLIBDIR Base path for lint(1) library installation. [/usr/libdata/lint] + +LIBGRP Library group. [${BINGRP}] + +LIBOWN Library owner. [${BINOWN}] + +LIBMODE Library mode. [${NONBINMODE}] + +DOCDIR Base path for system documentation (e.g. PSD, USD, etc.) + installation. [/usr/share/doc] + +HTMLDOCDIR Base path for html system documentation installation. + [/usr/share/doc/html] + +DOCGRP Documentation group. [wheel] + +DOCOWN Documentation owner. [root] + +DOCMODE Documentation mode. [${NONBINMODE}] + +NLSDIR Base path for National Language Support files installation. + [/usr/share/nls] + +NLSGRP National Language Support files group. [wheel] + +NLSOWN National Language Support files owner. [root] + +NLSMODE National Language Support files mode. [${NONBINMODE}] + +STRIPFLAG 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 knob. [-s] + +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 knob. [-c] + +Additionally, the following variables may be set by bsd.own.mk or in a +make configuration file to modify the behaviour of the system build +process (default values are in brackets along with comments, if set by +bsd.own.mk): + +MKCRYPTO If set to "no", no cryptography support will be built + into the system. Defaults to "yes". + +NOCRYPTO If set, it is equivalent to setting MKCRYPTO to "no". + +MKCRYPTO_IDEA If set to "yes", IDEA support will be built into + libcrypto_idea.a. Defaults to "no". + +MKCRYPTO_RC5 If set to "yes", RC5 support will be built into + libcrypto_rc5.a. Defaults to "no". + +MKKERBEROS If set to "no", disables building Kerberos (v4 or v5) + support into various system utilities that support it. + Defaults to "yes". NOTE: This does not affect the + building of the Kerberos libraries or infrastructure + programs themselves. To completely disable Kerberos, + set MKCRYPTO to "no". + +NOKERBEROS If set, it is equivalent to setting MKKERBEROS to "no". + +SKEY Compile in support for S/key authentication. [yes, set + unconditionally] + +MANZ Compress manual pages at installation time. + +SYS_INCLUDE Copy or symlink kernel include files into /usr/include. + Possible values are "symlinks" or "copies" (which is + the same as the variable being unset). + +NOPROFILE Do not build profiled versions of system libraries + +NOPIC Do not build PIC versions of system libraries, and + do not build shared libraries. [set if ${MACHINE_ARCH} + is "sh3" and ${OBJECT_FMT} is "COFF", unset otherwise.] + +NOLINT Do not build lint libraries. + +OBJECT_FMT Object file format. [set to "ELF" on architectures that + use ELF -- currently if ${MACHINE_ARCH} is "alpha", + "mipsel", "mipseb", "powerpc", "sparc", "sparc64", + "i386" and some m68k machines, or set to "a.out" on + other architectures]. + +MKSOFTFLOAT If "yes", build with options to enable the compiler to + generate output containing library calls for floating + point and possibly soft-float library support. Defaults + to "no". + +bsd.own.mk is generally useful when building your own Makefiles so that +they use the same default owners etc. as the rest of the tree. + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +The include file handles building programs from one or +more source files, along with their manual pages. It has a limited number +of suffixes, consistent with the current needs of the BSD tree. + +It has eight targets: + + all: + build the program and its manual page + clean: + remove the program, any object files and the files a.out, + Errs, errs, mklog, and ${PROG}.core. + cleandir: + remove all of the files removed by the target clean, as + well as .depend, tags, and any manual pages. + `distclean' is a synonym for `cleandir'. + depend: + make the dependencies for the source files, and store + them in the file .depend. + includes: + install any header files. + install: + install the program and its manual pages; if the Makefile + does not itself define the target install, the targets + beforeinstall and afterinstall may also be used to cause + actions immediately before and after the install target + is executed. + lint: + run lint on the source files + tags: + create a tags file for the source files. + +It sets/uses the following variables: + +BINGRP Binary group. + +BINOWN Binary owner. + +BINMODE Binary mode. + +CLEANFILES Additional files to remove for the clean and cleandir targets. + +COPTS Additional flags to the compiler when creating C objects. + +CPPFLAGS Additional flags to the C pre-processor + +LDADD Additional loader objects. Usually used for libraries. + For example, to load with the compatibility and utility + libraries, use: + + LDADD+=-lutil -lcompat + +LDFLAGS Additional loader flags. + +LINKS The list of binary links; should be full pathnames, the + linked-to file coming first, followed by the linked + file. The files are hard-linked. For example, to link + /bin/test and /bin/[, use: + + LINKS= ${DESTDIR}/bin/test ${DESTDIR}/bin/[ + +SYMLINKS The list of symbolic links; should be full pathnames. + Syntax is identical to LINKS. Note that DESTDIR is not + automatically included in the link. + +MAN Manual pages (should end in .1 - .9). If no MAN variable is + defined, "MAN=${PROG}.1" is assumed. + +PROG The name of the program to build. If not supplied, nothing + is built. + +PROGNAME The name that the above program will be installed as, if + different from ${PROG}. + +SRCS List of source files to build the program. If SRCS is not + defined, it's assumed to be ${PROG}.c. + +DPADD Additional dependencies for the program. Usually used for + libraries. For example, to depend on the compatibility and + utility libraries use: + + DPADD+=${LIBCOMPAT} ${LIBUTIL} + + The following libraries are predefined for DPADD: + + LIBCRT0?= ${DESTDIR}/usr/lib/crt0.o + LIBC?= ${DESTDIR}/usr/lib/libc.a + LIBC_PIC?= ${DESTDIR}/usr/lib/libc_pic.a + LIBCOMPAT?= ${DESTDIR}/usr/lib/libcompat.a + LIBCRYPT?= ${DESTDIR}/usr/lib/libcrypt.a + LIBCURSES?= ${DESTDIR}/usr/lib/libcurses.a + LIBDBM?= ${DESTDIR}/usr/lib/libdbm.a + LIBDES?= ${DESTDIR}/usr/lib/libdes.a + LIBEDIT?= ${DESTDIR}/usr/lib/libedit.a + LIBFORM?= ${DESTDIR}/usr/lib/libform.a + LIBGCC?= ${DESTDIR}/usr/lib/libgcc.a + LIBGNUMALLOC?= ${DESTDIR}/usr/lib/libgnumalloc.a + LIBINTL?= ${DESTDIR}/usr/lib/libintl.a + LIBIPSEC?= ${DESTDIR}/usr/lib/libipsec.a + LIBKDB?= ${DESTDIR}/usr/lib/libkdb.a + LIBKRB?= ${DESTDIR}/usr/lib/libkrb.a + LIBKVM?= ${DESTDIR}/usr/lib/libkvm.a + LIBL?= ${DESTDIR}/usr/lib/libl.a + LIBM?= ${DESTDIR}/usr/lib/libm.a + LIBMENU?= ${DESTDIR}/usr/lib/libmenu.a + LIBMP?= ${DESTDIR}/usr/lib/libmp.a + LIBNTP?= ${DESTDIR}/usr/lib/libntp.a + LIBPC?= ${DESTDIR}/usr/lib/libpc.a + LIBPCAP?= ${DESTDIR}/usr/lib/libpcap.a + LIBPLOT?= ${DESTDIR}/usr/lib/libplot.a + LIBPOSIX?= ${DESTDIR}/usr/lib/libposix.a + LIBRESOLV?= ${DESTDIR}/usr/lib/libresolv.a + LIBRPCSVC?= ${DESTDIR}/usr/lib/librpcsvc.a + LIBSKEY?= ${DESTDIR}/usr/lib/libskey.a + LIBTERMCAP?= ${DESTDIR}/usr/lib/libtermcap.a + LIBTELNET?= ${DESTDIR}/usr/lib/libtelnet.a + LIBUTIL?= ${DESTDIR}/usr/lib/libutil.a + LIBWRAP?= ${DESTDIR}/usr/lib/libwrap.a + LIBY?= ${DESTDIR}/usr/lib/liby.a + LIBZ?= ${DESTDIR}/usr/lib/libz.a + + +SHAREDSTRINGS If defined, a new .c.o rule is used that results in shared + strings, using xstr(1). Note that this will not work with + parallel makes. + +STRIPFLAG The flag passed to the install program to cause the binary + to be stripped. + +SUBDIR A list of subdirectories that should be built as well. + Each of the targets will execute the same target in the + subdirectories. + +SCRIPTS A list of interpreter scripts [file.{sh,csh,pl,awk,...}]. + These are installed exactly like programs. + +SCRIPTSNAME The name that the above program will be installed as, if + different from ${SCRIPTS}. These can be further specialized + by setting SCRIPTSNAME_