diff --git a/devel/bmake/files/ChangeLog b/devel/bmake/files/ChangeLog new file mode 100644 index 0000000000000..c865b9486671d --- /dev/null +++ b/devel/bmake/files/ChangeLog @@ -0,0 +1,578 @@ +2005-11-05 Simon J. Gerraty + + * Makefile.in (BMAKE_VERSION): bump to 20051105 + * configure.in: always set default_sys_path + default is ${prefix}/share/mk + - remove prefix_sys_path, anyone wanting more than above + needs to set it manually. + +2005-11-04 Simon J. Gerraty + + * boot-strap: make this a bit easier for pkgsrc folk. + bootstrap still fails on IRIX64 since MACHINE_ARCH gets set to + 'mips' while pkgsrc wants 'mipseb' or 'mipsel' + +2005-11-02 Simon J. Gerraty + + * Makefile.in (BMAKE_VERSION): bump to 20051102 + * job.c (JobFinish): fix likely ancient merge lossage + fix from Todd Vierling. + * boot-strap (srcdir): allow setting mksrc=none + +2005-10-31 Simon J. Gerraty + + * Makefile.in (BMAKE_VERSION): bump to 20051031 + * ranlib.h: skip on OSF too. + (NetBSD PR 31864) + +2005-10-10 Simon J. Gerraty + + * Makefile.in (BMAKE_VERSION): bump to 20051002 + fix a silly typo + +2005-10-09 Simon J. Gerraty + + * Makefile.in (BMAKE_VERSION): bump to 20051001 + support for UnixWare and some other systems, + based on patches from pkgsrc/bootstrap + +2005-09-03 Simon J. Gerraty + + * Makefile.in (BMAKE_VERSION): bump to 20050901 + * Merge with NetBSD make, pick up: + o possible parse error causing us to wander off. + +2005-06-06 Simon J. Gerraty + + * Makefile.in (BMAKE_VERSION): bump to 20050606 + * Merge with NetBSD make, pick up: + o :0x modifier for randomizing a list + o fixes for a number of -Wuninitialized issues. + +2005-05-30 Simon J. Gerraty + + * Makefile.in (BMAKE_VERSION): bump to 20050530 + * Merge with NetBSD make, pick up: + o Handle dependencies for .BEGIN, .END and .INTERRUPT + + * README: was seriously out of date. + +2005-03-22 Simon J. Gerraty + + * Important to use .MAKE rather than MAKE. + +2005-03-15 Simon J. Gerraty + + * Makefile.in (BMAKE_VERSION): bump to 20050315 + * Merge with NetBSD make, pick up: + o don't mistake .elsefoo for .else + o use suffix-specific search path correctly + o bunch of style nits + +2004-05-11 Simon J. Gerraty + + * boot-strap: + o ensure that args to --src and --with-mksrc + are resolved before giving them to configure. + o add -o "objdir" so that builder can control it, + default is $OS as determined by os.sh + o add -q to suppress all the install instructions. + +2004-05-08 Simon J. Gerraty + + * Remove __IDSTRING() + + * Makefile.in (BMAKE_VERSION): bump to 20040508 + * Merge with NetBSD make, pick up: + o posix fixes + - remove '-e' from compat mode + - add support for '+' command-line prefix. + o fix for handling '--' on command-line. + o fix include in lst.lib/lstInt.h to simplify '-I's + o we also picked up replacement of MAKE_BOOTSTRAP + with !MAKE_NATIVE which is a noop, but possibly confusing. + +2004-04-14 Simon J. Gerraty + + * Makefile.in (BMAKE_VERSION): bump to 20040414 + * Merge with NetBSD make, pick up: + o allow quoted strings on lhs of conditionals + o issue warning when extra .else is seen + o print line numer when errors encountered during parsing from + string. + +2004-02-20 Simon J. Gerraty + + * Makefile.in (BMAKE_VERSION): bump to 20040220 + * Merge with NetBSD make, pick up: + o fix for old :M parsing bug. + o re-jigged unit-tests + +2004-02-15 Simon J. Gerraty + + * Makefile.in (accept test): use ${.MAKE:S,^./,${.CURDIR}/,} + so that './bmake -f Makefile test' works. + +2004-02-14 Simon J. Gerraty + + * Makefile.in: (BMAKE_VERSION): bump to 20040214 + * Merge with NetBSD make, pick up: + o search upwards for *.mk + o fix for double free of var substitution buffers + o use of getopt replaced with custom code, since the usage + (re-scanning) isn't posix compatible. + +2004-02-12 Simon J. Gerraty + + * arch.c: don't include ranlib.h on ELF systems + (thanks to Chuck Cranor ). + +2004-01-18 Simon J. Gerraty + + * Makefile.in (BMAKE_VERSION): bump to 20040118 + + * boot-strap (while): export vars we assign to on cmdline + * unit-test/Makefile.in: ternary is .PHONY + +2004-01-08 Simon J. Gerraty + + * Makefile.in (BMAKE_VERSION): bump version to 20040108 + * Merge with NetBSD make, pick up: + o fix for ternary modifier + +2004-01-06 Simon J. Gerraty + + * Makefile.in (BMAKE_VERSION): bump version to 20040105 + * Merge with NetBSD make, pick up: + o fix for cond.c to handle compound expressions better + o variable expansion within sysV style replacements + +2003-12-22 Simon J. Gerraty + + * Make portable snprintf safer - output to /dev/null first to + check space needed. + + * Makefile.in (BMAKE_VERSION): bump version to 20031222 + * Merge with NetBSD make, pick up: + o -dg3 to show input graph when things go wrong. + o explicitly look for makefiles in objdir if not found in curdir so + that errors in .depend etc will be reported accurarely. + o avoid use of -e in shell scripts in jobs mode, use '|| exit $?' + instead as it more accurately reflects the expected behavior and + is more consistently implemented. + o avoid use of asprintf. + +2003-09-28 Simon J. Gerraty + + * util.c: Add asprintf and vasprintf. + + * Makefile.in (BMAKE_VERSION): bump version to 20030928 + * Merge with NetBSD make, pick up: + :[] modifier - allows picking words from a variable. + :tW modifier - allows treating value as one big word. + W flag for :C and :S - allows treating value as one big word. + +2003-09-12 Simon J. Gerraty + + * Merge with NetBSD make + pick up -de flag to enable printing failed command. + don't skip 1st two dir entries (normally . and ..) since + coda does not have them. + +2003-09-09 Simon J. Gerraty + + * Makefile.in (BMAKE_VERSION): bump version to 20030909 + * Merge with NetBSD make, pick up: + - changes for -V '${VAR}' to print fully expanded value + cf. -V VAR + - CompatRunCommand now prints the command that failed. + - several files got updated 3 clause Berkeley license. + +2003-08-02 Simon J. Gerraty + + * boot-strap: Allow setting configure args on command line. + +2003-07-31 Simon J. Gerraty + + * configure.in: add --with-defshell to allow sh or ksh + to be selected as default shell. + + * Makefile.in: bump version to 20030731 + + * Merge with NetBSD make + Pick up .SHELL spec for ksh and associate man page changes. + Also compat mode now uses the same shell specs. + +2003-07-29 Simon J. Gerraty + + * var.c (Var_Parse): ensure delim is initialized. + + * unit-tests/Makefile.in: use single quotes to avoid problems from + some shells. + + * makefile.boot.in: + Run the unit-tests as part of the bootstrap procedure. + +2003-07-28 Simon J. Gerraty + + * unit-tests/Makefile.in: always force complaints from + ${TEST_MAKE} to be from 'make'. + + * configure.in: add check for 'diff -u' + also fix some old autoconf'isms + + * Makefile.in (BMAKE_VERSION): bump version to 20030728. + if using GCC add -Wno-cast-qual to CFLAGS for var.o + + * Merge with NetBSD make + Pick up fix for :ts parsing error in some cases. + Pick unit-tests. + +2003-07-23 Simon J. Gerraty + + * Makefile.in (BMAKE_VERSION): bump version to 20030723. + + * var.c (Var_Parse): fix bug in :ts modifier, after const + correctness fixes, must pass nstr to VarModify. + +2003-07-14 Simon J. Gerraty + + * Makefile.in: BMAKE_VERSION switch to a date based version. + We'll generally use the date of last import from NetBSD. + + * Merge with NetBSD make + Pick up fixes for const-correctness, now passes WARNS=3 on + NetBSD. + Pick up :ts modifier, allows controlling the separator used + between words in variable expansion. + +2003-07-11 Simon J. Gerraty + + * FILES: include boot-strap and os.sh + + * Makefile.in: only set WARNS if we are NetBSD, the effect on + FreeBSD is known to be bad. + + * makefile.boot.in (bootstrap): make this the default target. + + * Makefile.in: bump version to 3.1.19 + + * machine.sh: avoid A-Z with tr as it is bound to lose. + +2003-07-10 Simon J. Gerraty + + * Merge with NetBSD make + Pick up fix for PR/19781 - unhelpful error msg on unclosed ${var:foo + Plus some doc fixes. + +2003-04-27 Simon J. Gerraty + + * Merge with NetBSD make + Pick up fix for PR/1523 - don't count a library as built, if there + is no way to build it + + * Bump version to 3.1.18 + +2003-03-23 Simon J. Gerraty + + * Merge with NetBSD make + Pick up fix for ParseDoSpecialSrc - we only use it if .WAIT + appears in src list. + +2003-03-21 Simon J. Gerraty + + * Merge with NetBSD make (mmm 10th anniversary!) + pick up fix for .WAIT in srcs that refer to $@ or $* (PR#20828) + pick up -X which tells us to not export VAR=val via setenv if + we are already doing so via MAKEFLAGS. This saves valuable env + space on systems like Darwin. + set MAKE_VERSION to 3.1.17 + + * parse.c: pix up fix for suffix rules + +2003-03-06 Simon J. Gerraty + + * Merge with NetBSD make. + pick up fix for propagating -B via MAKEFLAGS. + set MAKE_VERSION to 3.1.16 + + * Apply some patches from pkgsrc-bootstrap/bmake + Originally by Grant Beattie + I may have missed some - since they are based on bmake-3.1.12 + +2002-12-03 Simon J. Gerraty + + * makefile.boot.in (bmake): update install targets for those that + use them, also clear MAKEFLAGS when invoking bmake.boot to avoid + havoc from gmake -w. Thanks to Harlan Stenn . + + * bmake.cat1: update the pre-formatted man page! + +2002-11-30 Simon J. Gerraty + + * Merge with NetBSD make. + pick up fix for premature free of pointer used in call + to Dir_InitCur(). + set MAKE_VERSION to 3.1.15 + +2002-11-26 Simon J. Gerraty + + * configure.in: determine suitable value for MKSRC. + override using --with-mksrc=PATH. + + * machine.sh: use `uname -p` for MACHINE_ARCH on modern SunOS systems. + configs(8) will use 'sun4' as an alias for 'sparc'. + +2002-11-25 Simon J. Gerraty + + * Merge with NetBSD make. + pick up ${.PATH} + pick up fix for finding ../cat.c via .PATH when .CURDIR=.. + set MAKE_VERSION to 3.1.14 + add configure checks for killpg and sys/socket.h + +2002-09-16 Simon J. Gerraty + + * tag bmake-3-1-13 + + * makefile.boot.in (bmake): use install-mk + Also setup ./mk before trying to invoke bmake.boot incase we + needed install-mk to create a sys.mk for us. + + * configure.in: If we need to add -I${srcdir}/missing, make it an + absolute path so that it works for lst.lib too. + + * make.h: always include sys/cdefs.h since we provide one if the + host does not. + + * Makefile.in (install-mk): + use MKSRC/install-mk which will do the right thing. + use uname -p for ARCH if possible. + since install-mk will setup links bsd.prog.mk -> prog.mk if + needed, just .include bsd.prog.mk + + * Merge with NetBSD make (NetBSD-1.6) + Code is ansi-C only now. + Bug in handling of dotLast is fixed. + Can now assign .OBJDIR and make will reset its notions of life. + New modifiers :tu :tl for toUpper and toLower. + +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/devel/bmake/files/FILES b/devel/bmake/files/FILES new file mode 100644 index 0000000000000..a2ceb926dc11a --- /dev/null +++ b/devel/bmake/files/FILES @@ -0,0 +1,94 @@ +FILES +ChangeLog +bmake.cat1 +boot-strap +os.sh +Makefile.in +PSD.doc/Makefile +PSD.doc/tutorial.ms +README +arch.c +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 +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 +unit-tests/Makefile.in +unit-tests/cond1 +unit-tests/comment +unit-tests/modmatch +unit-tests/modorder +unit-tests/modts +unit-tests/modword +unit-tests/posix +unit-tests/ternary +unit-tests/test.exp +unit-tests/varcmd diff --git a/devel/bmake/files/Makefile.in b/devel/bmake/files/Makefile.in new file mode 100644 index 0000000000000..898dbb0c03601 --- /dev/null +++ b/devel/bmake/files/Makefile.in @@ -0,0 +1,147 @@ +# $NetBSD: Makefile.in,v 1.1.1.1 2005/12/02 00:02:59 sjg Exp $ +# @(#)Makefile 5.2 (Berkeley) 12/28/90 + +# $Id: Makefile.in,v 1.1.1.1 2005/12/02 00:02:59 sjg Exp $ + +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 + +# you can use this Makefile if you have an earlier version of bmake. +prefix= @prefix@ +srcdir= @srcdir@ +CC?= @CC@ + +# Base version on src date +BMAKE_VERSION= 20051105 +# knowing when it was built is also handy +BUILD_DATE!= date +%Y%m%d +MAKE_VERSION:= bmake-${BMAKE_VERSION} build-${BUILD_DATE} +MACHINE=@machine@ +MACHINE_ARCH=@machine_arch@ + +CFLAGS+= -D_PATH_DEFSYSPATH=\"@default_sys_path@\" +CFLAGS+= -I. -I${srcdir} @DEFS@ @CPPFLAGS@ ${XDEFS} -DMAKE_NATIVE +CFLAGS+= ${CFLAGS_${.TARGET:T}} +CFLAGS_main.o= "-DMAKE_VERSION=\"${MAKE_VERSION}\"" +LDFLAGS= @LDFLAGS@ +LIBOBJS= @LIBOBJS@ +LDADD= @LIBS@ + +.PATH: ${srcdir} +.PATH: ${srcdir}/lst.lib + +OS!= uname -s +ARCH!= uname -p 2>/dev/null || uname -m + +# list of OS's which are derrived from BSD4.4 +isBSD44= NetBSD FreeBSD OpenBSD DragonFly + +.if ${OS} == "NetBSD" +# Don't set these for anyone else since we don't know what the effect may be. +# On FreeBSD WARNS=2 sets a bunch of -W flags that make does not handle. +WFORMAT= 1 +WARNS=3 +.NOPATH: bmake.cat1 +.if make(install) && exists(${DESTDIR}/usr/share/doc) +SUBDIR= PSD.doc +.endif +.endif + +.if empty(isBSD44:M${OS}) +# 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 +.if make(obj) || make(clean) +SUBDIR+= unit-tests +.endif + +# many systems use gcc these days +CC_IS_GCC=@GCC@ +.if ${CC_IS_GCC} == "yes" +# problem with gcc3 +CFLAGS_var.o+= -Wno-cast-qual +.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 derived 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 +# sigh, FreeBSD at least includes bsd.subdir.mk via bsd.obj.mk +# so the inclusion below, results in complaints about re-defined +# targets. For NetBSD though we need to explicitly include it. +.if defined(SUBDIR) && !target(${SUBDIR:[1]}) +.include +.endif + +# Force these +BINDIR= ${prefix}/bin +MANDIR= ${prefix}/man + +.if "${OS}" != "NetBSD" && !empty(isBSD44:M${OS}) +# freebsd's bsd.man.mk works differently +MAN1=${MAN} +MANDIR= ${prefix}/man/man +MANDEST= ${MANDIR}1 +.NOPATH: bmake.cat1 +.endif +MANDEST?= ${MANDIR} + +arch.o: config.h +# make sure that MAKE_VERSION gets updated. +main.o: ${SRCS} ${MAKEFILE} + +MK?=${prefix}/share/mk +MKSRC?=@mksrc@ +INSTALL?=${srcdir}/install-sh + +beforeinstall: + test -d ${DESTDIR}${BINDIR} || ${INSTALL} -m 775 -d ${DESTDIR}${BINDIR} + test -d ${DESTDIR}${MANDEST} || ${INSTALL} -m 775 -d ${DESTDIR}${MANDEST} + +# latest version of *.mk includes an installer. +# you should not need to set USE_OS +install-mk: +.if exists(${MKSRC}/install-mk) + test -d ${DESTDIR}${MK} || ${INSTALL} -m 775 -d ${DESTDIR}${MK} + ${MKSRC}/install-mk -v -m 644 ${DESTDIR}${MK} ${USE_OS} +.else + @echo need to unpack mk.tar.gz under ${srcdir} or set MKSRC; false +.endif + +# A simple unit-test driver to help catch regressions +accept test: + cd ${.CURDIR}/unit-tests && ${.MAKE:S,^./,${.CURDIR}/,} TEST_MAKE=${TEST_MAKE:U${.OBJDIR}/${PROG:T}} ${.TARGET} diff --git a/devel/bmake/files/PSD.doc/Makefile b/devel/bmake/files/PSD.doc/Makefile new file mode 100644 index 0000000000000..b95efc51201c0 --- /dev/null +++ b/devel/bmake/files/PSD.doc/Makefile @@ -0,0 +1,8 @@ +# $NetBSD: Makefile,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $ +# @(#)Makefile 8.1 (Berkeley) 8/14/93 + +DIR= psd/12.make +SRCS= tutorial.ms +MACROS= -ms + +.include diff --git a/devel/bmake/files/PSD.doc/tutorial.ms b/devel/bmake/files/PSD.doc/tutorial.ms new file mode 100644 index 0000000000000..056da3b23c3dd --- /dev/null +++ b/devel/bmake/files/PSD.doc/tutorial.ms @@ -0,0 +1,3772 @@ +.\" $NetBSD: tutorial.ms,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $ +.\" 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. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" Copyright (c) 1988, 1989 by Adam de Boor +.\" Copyright (c) 1989 by Berkeley Softworks +.\" +.\" 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 happening. 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 solely 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 libraries 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 havoc 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/devel/bmake/files/README b/devel/bmake/files/README new file mode 100644 index 0000000000000..154a5066fc50e --- /dev/null +++ b/devel/bmake/files/README @@ -0,0 +1,48 @@ + bmake + +This directory contains a port of the BSD make tool (from NetBSD) +I have run it on SunOS,Solaris,HP-UX,AIX,IRIX,FreeBSD and Linux. + +Version 3 was 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. + +In 2003 bmake switched to a date based version (first was 20030714) +which generally represents the date it was last merged with NetBSD's +make. Since then, NetBSD's make is imported within a week of any +interesting changes, so that bmake tracks it very closely. + +Building: + +The prefered way to bootstrap bmake is: + +./bmake/boot-strap + +there are a number of args - most of which get passed to configure, +eg. + +./bmake/boot-strap --prefix=/opt + +see the boot-strap script for details. + +To make much use of bmake you will need the bsd.*.mk macros or my +portable *.mk macros. See +http://www.crufty.net/ftp/pub/sjg/bsd-mk.tar.gz +http://www.crufty.net/ftp/pub/sjg/mk.tar.gz +which will be links to the latest versions. + +On a non-BSD system, you would want to unpack mk[-YYYYmmdd].tar.gz in +the same directory as bmake (so ./mk and ./bmake exist), and +./bmake/boot-strap will do the rest. + +If you want to do it all by hand then read boot-strap first to get the +idea. + +Even if you have an earlier version of bmake installed, use boot-strap +to ensure that all goes well. + +--sjg diff --git a/devel/bmake/files/aclocal.m4 b/devel/bmake/files/aclocal.m4 new file mode 100644 index 0000000000000..93e00d51e4b06 --- /dev/null +++ b/devel/bmake/files/aclocal.m4 @@ -0,0 +1,77 @@ +dnl RCSid: +dnl $Id: aclocal.m4,v 1.1.1.1 2005/12/02 00:02:59 sjg 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/devel/bmake/files/arch.c b/devel/bmake/files/arch.c new file mode 100644 index 0000000000000..3119a51b6277e --- /dev/null +++ b/devel/bmake/files/arch.c @@ -0,0 +1,1385 @@ +/* $NetBSD: arch.c,v 1.1.1.1 2005/12/02 00:02:59 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: arch.c,v 1.1.1.1 2005/12/02 00:02:59 sjg 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 2005/12/02 00:02:59 sjg Exp $"); +#endif +#endif /* not lint */ +#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 +#if defined(HAVE_RANLIB_H) && !(defined(__ELF__) || defined(NO_RANLIB)) +#include +#endif +#include +#include +#include +#ifdef HAVE_UTIME_H +#include +#endif + +#include "make.h" +#include "hash.h" +#include "dir.h" + +#ifdef TARGET_MACHINE +#undef MAKE_MACHINE +#define MAKE_MACHINE TARGET_MACHINE +#endif +#ifdef TARGET_MACHINE_ARCH +#undef MAKE_MACHINE_ARCH +#define MAKE_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(ClientData, ClientData); +#ifdef CLEANUP +static void ArchFree(ClientData); +#endif +static struct ar_hdr *ArchStatMember(char *, char *, Boolean); +static FILE *ArchFindMember(char *, char *, struct ar_hdr *, const char *); +#if defined(__svr4__) || defined(__SVR4) || defined(__ELF__) +#define SVR4ARCHIVES +static int ArchSVR4Entry(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(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 != NULL; + entry = Hash_EnumNext(&search)) + free(Hash_GetValue(entry)); + + free(a->name); + if (a->fnametab) + free(a->fnametab); + Hash_DeleteTable(&a->members); + free(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. + * + * Input: + * linePtr Pointer to start of specification + * nodeLst Lst on which to place the nodes + * ctxt Context in which to expand variables + * + * 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(char **linePtr, Lst nodeLst, GNode *ctxt) +{ + 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. + * + * Input: + * ar Current list element + * archName Name we want + * + * Results: + * 0 if it is, non-zero if it isn't. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static int +ArchFindArchive(ClientData ar, ClientData archName) +{ + 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. + * + * Input: + * archive Path to the archive + * member Name of member. If it is a path, only the last + * component is used. + * hash TRUE if archive should be hashed if not already so. + * + * 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(char *archive, char *member, Boolean hash) +{ + 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 != 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 != 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 (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 == NULL) { + return (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 == NULL) { + return (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 (NULL); + } + + ar = 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((unsigned char)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, NULL); + Hash_SetValue(he, emalloc(sizeof(struct ar_hdr))); + memcpy(Hash_GetValue(he), &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 != NULL) { + return ((struct ar_hdr *)Hash_GetValue(he)); + } else { + return (NULL); + } + +badarch: + fclose(arch); + Hash_DeleteTable(&ar->members); + if (ar->fnametab) + free(ar->fnametab); + free(ar); + return (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(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". + * + * Input: + * archive Path to the archive + * member Name of member. If it is a path, only the last + * component is used. + * arhPtr Pointer to header structure to be filled in + * mode The mode for opening the stream + * + * 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(char *archive, char *member, struct ar_hdr *arhPtr, + const char *mode) +{ + 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 == NULL) { + return (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 (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 != 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 (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((unsigned char)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 (NULL); +} + +/*- + *----------------------------------------------------------------------- + * Arch_Touch -- + * Touch a member of an archive. + * + * Input: + * gn Node of member to touch + * + * 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(GNode *gn) +{ + 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 != 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. + * + * Input: + * gn The node of the library to touch + * + * Results: + * None. + * + * Side Effects: + * Both the modification time of the library and of the RANLIBMAG + * member are set to 'now'. + * + *----------------------------------------------------------------------- + */ +void +#if !defined(RANLIBMAG) +Arch_TouchLib(GNode *gn __unused) +#else +Arch_TouchLib(GNode *gn) +#endif +{ +#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, UNCONST(RANLIBMAG), &arh, "r+"); + snprintf(arh.AR_DATE, sizeof(arh.AR_DATE), "%-12ld", (long) now); + + if (arch != 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. + * + * Input: + * gn Node describing archive member + * + * 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(GNode *gn) +{ + 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 != 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(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. + * + * Input: + * gn Node of library to find + * path 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(GNode *gn, Lst path) +{ + char *libName; /* file name for archive */ + size_t sz = strlen(gn->name) + 6 - 2; + + libName = 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 == 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. + * + * Input: + * gn The library's graph node + * + * 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(GNode *gn) +{ + Boolean oodate; + + if (gn->type & OP_PHONY) { + oodate = TRUE; + } else if (OP_NOP(gn->type) && Lst_IsEmpty(gn->children)) { + oodate = FALSE; + } else if ((!Lst_IsEmpty(gn->children) && 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, UNCONST(RANLIBMAG), FALSE); + + if (arhPtr != 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(void) +{ + archives = Lst_Init(FALSE); +} + + + +/*- + *----------------------------------------------------------------------- + * Arch_End -- + * Cleanup things for this module. + * + * Results: + * None. + * + * Side Effects: + * The 'archives' list is freed + * + *----------------------------------------------------------------------- + */ +void +Arch_End(void) +{ +#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(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/devel/bmake/files/bmake.cat1 b/devel/bmake/files/bmake.cat1 new file mode 100644 index 0000000000000..a1c12d8d22f88 --- /dev/null +++ b/devel/bmake/files/bmake.cat1 @@ -0,0 +1,915 @@ +MAKE(1) NetBSD General Commands Manual MAKE(1) + +NNAAMMEE + bbmmaakkee - maintain program dependencies + +SSYYNNOOPPSSIISS + bbmmaakkee [--BBeeiikkNNnnqqrrssttWWXX] [--DD _v_a_r_i_a_b_l_e] [--dd _f_l_a_g_s] [--ff _m_a_k_e_f_i_l_e] + [--II _d_i_r_e_c_t_o_r_y] [--JJ _p_r_i_v_a_t_e] [--jj _m_a_x___j_o_b_s] [--mm _d_i_r_e_c_t_o_r_y] [--TT _f_i_l_e] + [--VV _v_a_r_i_a_b_l_e] [_v_a_r_i_a_b_l_e_=_v_a_l_u_e] [_t_a_r_g_e_t _._._.] + +DDEESSCCRRIIPPTTIIOONN + bbmmaakkee is a program designed to simplify the maintenance of other pro- + grams. Its input is a list of specifications as to the files upon which + programs and other files depend. If the file `_m_a_k_e_f_i_l_e' exists, it is + read for this list of specifications. If it does not exist, the file + `_M_a_k_e_f_i_l_e' is read. If the file `_._d_e_p_e_n_d' exists, it is read (see + mkdep(1)). + + This manual page is intended as a reference document only. For a more + thorough description of bbmmaakkee and makefiles, please refer to _M_a_k_e _- _A + _T_u_t_o_r_i_a_l. + + The options are as follows: + + --BB 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. + + --DD _v_a_r_i_a_b_l_e + Define _v_a_r_i_a_b_l_e to be 1, in the global context. + + --dd _f_l_a_g_s + Turn on debugging, and specify which portions of bbmmaakkee are to + print debugging information. _F_l_a_g_s is one or more of the follow- + ing: + + _A Print all possible debugging information; equivalent to + specifying all of the debugging flags. + + _a Print debugging information about archive searching and + caching. + + _c Print debugging information about conditional evaluation. + + _d Print debugging information about directory searching and + caching. + + _e Print debugging information about failed commands and + targets. + + _f Print debugging information about loop evaluation. + + _g_1 Print the input graph before making anything. + + _g_2 Print the input graph after making everything, or before + exiting on error. + + _g_3 Print the input graph before exiting on error. + + _j Print debugging information about running multiple + shells. + + _m Print debugging information about making targets, includ- + ing modification dates. + + _s Print debugging information about suffix-transformation + rules. + + _t Print debugging information about target list mainte- + nance. + + _v Print debugging information about variable assignment. + + _x Run shell commands with --xx so the actual commands are + printed as they are executed. + + --ee Specify that environment variables override macro assignments + within makefiles. + + --ff _m_a_k_e_f_i_l_e + Specify a makefile to read instead of the default `_m_a_k_e_f_i_l_e'. If + _m_a_k_e_f_i_l_e is `--', standard input is read. Multiple makefiles may + be specified, and are read in the order specified. + + --II _d_i_r_e_c_t_o_r_y + Specify a directory in which to search for makefiles and included + makefiles. The system makefile directory (or directories, see + the --mm option) is automatically included as part of this list. + + --ii Ignore non-zero exit of shell commands in the makefile. Equiva- + lent to specifying `--' before each command line in the makefile. + + --JJ _p_r_i_v_a_t_e + This option should _n_o_t be specified by the user. + + When the _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. + + --jj _m_a_x___j_o_b_s + Specify the maximum number of jobs that bbmmaakkee may have running at + any one time. Turns compatibility mode off, unless the _B flag is + also specified. + + --kk Continue processing after errors are encountered, but only on + those targets that do not depend on the target whose creation + caused the error. + + --mm _d_i_r_e_c_t_o_r_y + Specify a directory in which to search for sys.mk and makefiles + included via the <_f_i_l_e>-style include statement. The --mm option + can be used multiple times to form a search path. This path will + override the default system include path: /usr/share/mk. Fur- + thermore the system include path will be appended to the search + path used for "_f_i_l_e"-style include statements (see the --II + option). + + If a file or directory name in the --mm argument (or the + MAKESYSPATH environment variable) starts with the string ".../" + then bbmmaakkee will search for the specified file or directory named + in the remaining part of the argument string. The search starts + with the current directory of the Makefile and then works upward + towards the root of the filesystem. If the search is successful, + then the resulting directory replaces the ".../" specification in + the --mm argument. If used, this feature allows bbmmaakkee to easily + search in the current source tree for customized sys.mk files + (e.g., by using ".../mk/sys.mk" as an argument). + + --nn Display the commands that would have been executed, but do not + actually execute them unless the target depends on the .MAKE spe- + cial source (see below). + + --NN 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. + + --qq Do not execute any commands, but exit 0 if the specified targets + are up-to-date and 1, otherwise. + + --rr Do not use the built-in rules specified in the system makefile. + + --ss Do not echo any commands as they are executed. Equivalent to + specifying `@@' before each command line in the makefile. + + --TT _t_r_a_c_e_f_i_l_e + When used with the --jj flag, append a trace record to _t_r_a_c_e_f_i_l_e + for each job started and completed. + + --tt 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. + + --VV _v_a_r_i_a_b_l_e + Print bbmmaakkee's idea of the value of _v_a_r_i_a_b_l_e, in the global con- + text. 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. If + _v_a_r_i_a_b_l_e contains a `$' then the value will be expanded before + printing. + + --WW Treat any warnings during makefile parsing as errors. + + --XX Don't export variables passed on the command line to the environ- + ment individually. Variables passed on the command line are + still exported via the _M_A_K_E_F_L_A_G_S environment variable. This + option may be useful on systems which have a small limit on the + size of command arguments. + + _v_a_r_i_a_b_l_e_=_v_a_l_u_e + Set the value of the variable _v_a_r_i_a_b_l_e to _v_a_l_u_e. Normally, all + values passed on the command line are also exported to sub-makes + in the environment. The --XX flag disables this behavior. Vari- + able assignments should follow options for POSIX compatibility + but no ordering is enforced. + + 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. + + In general, lines may be continued from one line to the next by ending + them with a backslash (`\'). The trailing newline character and initial + whitespace on the following line are compressed into a single space. + +FFIILLEE DDEEPPEENNDDEENNCCYY SSPPEECCIIFFIICCAATTIIOONNSS + 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 sep- + arates them. The three operators are as follows: + + :: 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 bbmmaakkee is interrupted. + + !! Targets are always re-created, but not until all sources have been + examined and re-created as necessary. Sources for a target accumu- + late over dependency lines when this operator is used. The target + is removed if bbmmaakkee is interrupted. + + :::: If no sources are specified, the target is always re-created. Oth- + erwise, 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 bbmmaakkee is interrupted. + + Targets and sources may contain the shell wildcard values `?', `*', `[]', + and `{}'. The values `?', `*', and `[]' 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 `{}' need not necessarily be used to describe + existing files. Expansion is in directory order, not alphabetically as + done in the shell. + +SSHHEELLLL CCOOMMMMAANNDDSS + Each target may have associated with it a series of shell commands, nor- + mally used to create the target. Each of the commands in this script + _m_u_s_t 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 `::::' operator is used. + + If the first characters of the command line are any combination of `@@', + `++', or `--', the command is treated specially. A `@@' causes the command + not to be echoed before it is executed. A `++' causes the command to be + executed even when --nn is given. This is similar to the effect of the + .MAKE special source, except that the effect can be limited to a single + line of a script. A `--' causes any non-zero exit status of the command + line to be ignored. + +VVAARRIIAABBLLEE AASSSSIIGGNNMMEENNTTSS + Variables in make are much like variables in the shell, and, by tradi- + tion, consist of all upper-case letters. + + VVaarriiaabbllee aassssiiggnnmmeenntt mmooddiiffiieerrss + The five operators that can be used to assign values to variables are as + follows: + + == Assign the value to the variable. Any previous value is overrid- + den. + + ++== Append the value to the current value of the variable. + + ??== Assign the value to the variable if it is not already defined. + + ::== Assign with expansion, i.e. expand the value before assigning it + to the variable. Normally, expansion is not done until the vari- + able is referenced. + + !!== 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. + + Any white-space before the assigned _v_a_l_u_e is removed; if the value is + being appended, a single space is inserted between the previous contents + of the variable and the appended value. + + Variables are expanded by surrounding the variable name with either curly + braces (`{}') or parentheses (`()') and preceding it with a dollar sign + (`$'). If the variable name contains only a single letter, the surround- + ing braces or parentheses are not required. This shorter form is not + recommended. + + 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. + + VVaarriiaabbllee ccllaasssseess + The four different classes of variables (in order of increasing prece- + dence) are: + + Environment variables + Variables defined as part of bbmmaakkee's environment. + + Global variables + Variables defined in the makefile or in included makefiles. + + Command line variables + Variables defined as part of the command line. + + Local variables + Variables that are defined specific to a certain target. The + seven local variables are as follows: + + _._A_L_L_S_R_C The list of all sources for this target; also known as + `_>'. + + _._A_R_C_H_I_V_E The name of the archive file. + + _._I_M_P_S_R_C The name/path of the source from which the target is to + be transformed (the ``implied'' source); also known as + `_<'. + + _._M_E_M_B_E_R The name of the archive member. + + _._O_O_D_A_T_E The list of sources for this target that were deemed + out-of-date; also known as `_?'. + + _._P_R_E_F_I_X The file prefix of the file, containing only the file + portion, no suffix or preceding directory components; + also known as `_*'. + + _._T_A_R_G_E_T The name of the target; also known as `_@'. + + The shorter forms `_@', `_?', `_<', `_>', and `_*' are permitted for + backward compatibility with historical makefiles and are not rec- + ommended. The six variables `_@_F', `_@_D', `_<_F', `_<_D', `_*_F', and + `_*_D' are permitted for compatibility with AT&T System V UNIX + makefiles and are not recommended. + + Four of the local variables may be used in sources on dependency + lines because they expand to the proper value for each target on + the line. These variables are `_._T_A_R_G_E_T', `_._P_R_E_F_I_X', `_._A_R_C_H_I_V_E', + and `_._M_E_M_B_E_R'. + + AAddddiittiioonnaall iinnbbuuiilltt vvaarriiaabblleess + In addition, bbmmaakkee sets or knows about the following variables: + + _$ A single dollar sign `$', i.e. `$$' expands to a single + dollar sign. + + _._A_L_L_T_A_R_G_E_T_S The list of all targets encountered in the Makefile. If + evaluated during Makefile parsing, lists only those tar- + gets encountered thus far. + + _._C_U_R_D_I_R A path to the directory where bbmmaakkee was executed. + + MAKE The name that bbmmaakkee was executed with (_a_r_g_v_[_0_]). For + compatibily bbmmaakkee also sets _._M_A_K_E with the same value. + The preferred variable to use is the environment variable + MAKE because it is more compatible with other versions of + bbmmaakkee and cannot be confused with the special target with + the same name. + + MAKEFLAGS The environment variable `MAKEFLAGS' may contain anything + that may be specified on bbmmaakkee's command line. Anything + specified on bbmmaakkee's command line is appended to the + `MAKEFLAGS' variable which is then entered into the envi- + ronment for all programs which bbmmaakkee executes. + + _._M_A_K_E_O_V_E_R_R_I_D_E_S This variable is used to record the names of variables + assigned to on the command line, so that they may be + exported as part of `MAKEFLAGS'. This behaviour can be + disabled by assigning an empty value to `_._M_A_K_E_O_V_E_R_R_I_D_E_S' + within a makefile. Extra variables can be exported from + a makefile by appending their names to `_._M_A_K_E_O_V_E_R_R_I_D_E_S'. + `MAKEFLAGS' is re-exported whenever `_._M_A_K_E_O_V_E_R_R_I_D_E_S' is + modified. + + _M_A_K_E___P_R_I_N_T___V_A_R___O_N___E_R_R_O_R + When bbmmaakkee stops due to an error, it prints its name and + the value of `_._C_U_R_D_I_R' as well as the value of any vari- + ables named in `_M_A_K_E___P_R_I_N_T___V_A_R___O_N___E_R_R_O_R'. + + _._n_e_w_l_i_n_e This variable is simply assigned a newline character as + its value. This allows expansions using the ::@@ modifier + to put a newline between iterations of the loop rather + than a space. For example, the printing of + `_M_A_K_E___P_R_I_N_T___V_A_R___O_N___E_R_R_O_R' could be done as + ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@}. + + _._O_B_J_D_I_R A path to the directory where the targets are built. + + _._P_A_R_S_E_D_I_R A path to the directory of the current `_M_a_k_e_f_i_l_e' being + parsed. + + _._P_A_R_S_E_F_I_L_E The basename of the current `_M_a_k_e_f_i_l_e' being parsed. + This variable and `_._P_A_R_S_E_D_I_R' are both set only while the + `_M_a_k_e_f_i_l_e_s' are being parsed. + + _._P_A_T_H A variable that represents the list of directories that + bbmmaakkee will search for files. The search list should be + updated using the target `_._P_A_T_H' rather than the vari- + able. + + PWD Alternate path to the current directory. bbmmaakkee normally + sets `_._C_U_R_D_I_R' to the canonical path given by getcwd(3). + However, if the environment variable `PWD' is set and + gives a path to the current directory, then bbmmaakkee sets + `_._C_U_R_D_I_R' to the value of `PWD' instead. This behaviour + is disabled if `MAKEOBJDIRPREFIX' is set. `PWD' is set + to the value of `_._O_B_J_D_I_R' for all programs which bbmmaakkee + executes. + + VVaarriiaabbllee mmooddiiffiieerrss + Variable expansion may be modified to select or modify each word of the + variable (where a ``word'' is white-space delimited sequence of charac- + ters). The general format of a variable expansion is as follows: + + {variable[:modifier[:...]]} + + Each modifier begins with a colon, which may be escaped with a backslash + (`\'). The supported modifiers are: + + ::EE Replaces each word in the variable with its suffix. + + ::HH Replaces each word in the variable with everything but the last com- + ponent. + + ::MM_p_a_t_t_e_r_n + Select only those words that match _p_a_t_t_e_r_n. The standard shell + wildcard characters (`*', `?', and `[]') may be used. The wildcard + characters may be escaped with a backslash (`\'). + + ::NN_p_a_t_t_e_r_n + This is identical to `::MM', but selects all words which do not match + _p_a_t_t_e_r_n. + + ::OO Order every word in variable alphabetically. To sort words in + reverse order use the `::OO::[[--11....11]]' combination of modifiers. + + ::OOxx Randomize words in variable. The results will be different each + time you are referring to the modified variable; use the assignment + with expansion (`::==') to prevent such behaviour. For example, + + LIST= uno due tre quattro + RANDOM_LIST= ${LIST:Ox} + STATIC_RANDOM_LIST:= ${LIST:Ox} + + all: + @echo "${RANDOM_LIST}" + @echo "${RANDOM_LIST}" + @echo "${STATIC_RANDOM_LIST}" + @echo "${STATIC_RANDOM_LIST}" + may produce output similar to: + + quattro due tre uno + tre due quattro uno + due uno quattro tre + due uno quattro tre + + ::QQ Quotes every shell meta-character in the variable, so that it can be + passed safely through recursive invocations of bbmmaakkee. + + ::RR Replaces each word in the variable with everything but its suffix. + + ::ttll Converts variable to lower-case letters. + + ::ttss_c + Words in the variable are normally separated by a space on expan- + sion. This modifier sets the separator to the character _c. If _c is + omitted, then no separator is used. + + ::ttuu Converts variable to upper-case letters. + + ::ttWW Causes the value to be treated as a single word (possibly containing + embedded white space). See also `::[[**]]'. + + ::ttww Causes the value to be treated as a sequence of words delimited by + white space. See also `::[[@@]]'. + + ::SS/_o_l_d___s_t_r_i_n_g/_n_e_w___s_t_r_i_n_g/[11ggWW] + Modify the first occurrence of _o_l_d___s_t_r_i_n_g in the variable's value, + replacing it with _n_e_w___s_t_r_i_n_g. If a `g' is appended to the last + slash of the pattern, all occurrences in each word are replaced. If + a `1' is appended to the last slash of the pattern, only the first + word is affected. If a `W' is appended to the last slash of the + pattern, then the value is treated as a single word (possibly con- + taining embedded white space). If _o_l_d___s_t_r_i_n_g begins with a caret + (`^'), _o_l_d___s_t_r_i_n_g is anchored at the beginning of each word. If + _o_l_d___s_t_r_i_n_g ends with a dollar sign (`$'), it is anchored at the end + of each word. Inside _n_e_w___s_t_r_i_n_g, an ampersand (`&') is replaced by + _o_l_d___s_t_r_i_n_g (without any `^' or `$'). Any character may be used as a + delimiter for the parts of the modifier string. The anchoring, + ampersand and delimiter characters may be escaped with a backslash + (`\'). + + Variable expansion occurs in the normal fashion inside both + _o_l_d___s_t_r_i_n_g and _n_e_w___s_t_r_i_n_g with the single exception that a backslash + is used to prevent the expansion of a dollar sign (`$'), not a pre- + ceding dollar sign as is usual. + + ::CC/_p_a_t_t_e_r_n/_r_e_p_l_a_c_e_m_e_n_t/[11ggWW] + The ::CC modifier is just like the ::SS modifier except that the old and + new strings, instead of being simple strings, are a regular expres- + sion (see regex(3)) string _p_a_t_t_e_r_n and an ed(1)-style string + _r_e_p_l_a_c_e_m_e_n_t. Normally, the first occurrence of the pattern _p_a_t_t_e_r_n + in each word of the value is substituted with _r_e_p_l_a_c_e_m_e_n_t. The `1' + modifier causes the substitution to apply to at most one word; the + `g' modifier causes the substitution to apply to as many instances + of the search pattern _p_a_t_t_e_r_n as occur in the word or words it is + found in; the `W' modifier causes the value to be treated as a sin- + gle word (possibly containing embedded white space). Note that `1' + and `g' are orthogonal; the former specifies whether multiple words + are potentially affected, the latter whether multiple substitutions + can potentially occur within each affected word. + + ::TT Replaces each word in the variable with its last component. + + ::uu Remove adjacent duplicate words (like uniq(1)). + + ::??_t_r_u_e___s_t_r_i_n_g::_f_a_l_s_e___s_t_r_i_n_g + If the variable (actually an expression; see below) evaluates to + true, return as its value the _t_r_u_e___s_t_r_i_n_g, otherwise return the + _f_a_l_s_e___s_t_r_i_n_g. + + _:_o_l_d___s_t_r_i_n_g_=_n_e_w___s_t_r_i_n_g + This is the AT&T System V UNIX style variable substitution. It must + be the last modifier specified. If _o_l_d___s_t_r_i_n_g or _n_e_w___s_t_r_i_n_g do not + contain the pattern matching character _% then it is assumed that + they are anchored at the end of each word, so only suffixes or + entire words may be replaced. Otherwise _% is the substring of + _o_l_d___s_t_r_i_n_g to be replaced in _n_e_w___s_t_r_i_n_g. + + Variable expansion occurs in the normal fashion inside both + _o_l_d___s_t_r_i_n_g and _n_e_w___s_t_r_i_n_g with the single exception that a backslash + is used to prevent the expansion of a dollar sign (`$'), not a pre- + ceding dollar sign as is usual. + + ::@@_t_e_m_p@@_s_t_r_i_n_g@@ + This is the loop expansion mechanism from the OSF Development Envi- + ronment (ODE) make. Unlike ..ffoorr loops expansion occurs at the time + of reference. Assign _t_e_m_p to each word in the variable and evaluate + _s_t_r_i_n_g. The ODE convention is that _t_e_m_p should start and end with a + period. For example. + ${LINKS:@.LINK.@${LN} ${TARGET} ${.LINK.}@} + + ::UU_n_e_w_v_a_l + If the variable is undefined _n_e_w_v_a_l is the value. If the variable + is defined, the existing value is returned. This is another ODE + make feature. It is handy for setting per-target CFLAGS for + instance: + ${_${.TARGET:T}_CFLAGS:U${DEF_CFLAGS}} + If a value is only required if the variable is undefined, use: + ${VAR:D:Unewval} + + ::DD_n_e_w_v_a_l + If the variable is defined _n_e_w_v_a_l is the value. + + ::LL The name of the variable is the value. + + ::PP The path of the node which has the same name as the variable is the + value. If no such node exists or its path is null, then the name of + the variable is used. + + ::!!_c_m_d!! + The output of running _c_m_d is the value. + + ::sshh If the variable is non-empty it is run as a command and the output + becomes the new value. + + ::::==_s_t_r + The variable is assigned the value _s_t_r after substitution. This + modifier and its variations are useful in obscure situations such as + wanting to apply modifiers to ..ffoorr loop iteration variables which + won't work due to the way ..ffoorr loops are implemented. These assign- + ment modifiers always expand to nothing, so if appearing in a rule + line by themselves should be preceded with something to keep bbmmaakkee + happy. As in: + + use_foo: .USE + .for i in ${.TARGET} ${.TARGET:R}.gz + @: ${t::=$i} + @echo t:R:T=${t:R:T} + .endfor + + The `::::' helps avoid false matches with the AT&T System V UNIX style + ::== modifier and since substitution always occurs the ::::== form is + vaguely appropriate. + + ::::??==_s_t_r + As for ::::== but only if the variable does not already have a value. + + ::::++==_s_t_r + Append _s_t_r to the variable. + + ::::!!==_c_m_d + Assign the output of _c_m_d to the variable. + + ::[[_r_a_n_g_e]] + Selects one or more words from the value, or performs other opera- + tions related to the way in which the value is divided into words. + + Ordinarily, a value is treated as a sequence of words delimited by + white space. Some modifiers suppress this behaviour, causing a + value to be treated as a single word (possibly containing embedded + white space). An empty value, or a value that consists entirely of + white-space, is treated as a single word. For the purposes of the + `::[[]]' modifier, the words are indexed both forwards using positive + integers (where index 1 represents the first word), and backwards + using negative integers (where index -1 represents the last word). + + The _r_a_n_g_e is subjected to variable expansion, and the expanded + result is then interpreted as follows: + + _i_n_d_e_x Selects a single word from the value. + + _s_t_a_r_t...._e_n_d + Selects all words from _s_t_a_r_t to _e_n_d, inclusive. For example, + `::[[22....--11]]' selects all words from the second word to the last + word. If _s_t_a_r_t is greater than _e_n_d, then the words are out- + put in reverse order. For example, `::[[--11....11]]' selects all + the words from last to first. + + ** Causes subsequent modifiers to treat the value as a single + word (possibly containing embedded white space). Analogous + to the effect of "$*" in Bourne shell. + + 0 Means the same as `::[[**]]'. + + @@ Causes subsequent modifiers to treat the value as a sequence + of words delimited by white space. Analogous to the effect + of "$@" in Bourne shell. + + ## Returns the number of words in the value. + +IINNCCLLUUDDEE SSTTAATTEEMMEENNTTSS,, CCOONNDDIITTIIOONNAALLSS AANNDD FFOORR LLOOOOPPSS + Makefile inclusion, conditional structures and for loops reminiscent of + the C programming language are provided in bbmmaakkee. All such structures + are identified by a line beginning with a single dot (`.') character. + Files are included with either ..iinncclluuddee <_f_i_l_e> or ..iinncclluuddee "_f_i_l_e". Vari- + ables between the angle brackets or double quotes are expanded to form + the file name. If angle brackets are used, the included makefile is + expected to be in the system makefile directory. If double quotes are + used, the including makefile's directory and any directories specified + using the --II option are searched before the system makefile directory. + For compatibility with other versions of bbmmaakkee `include file ...' is also + accepted. If the include statement is written as ..--iinncclluuddee or as + ..ssiinncclluuddee then errors locating and/or opening include files are ignored. + + Conditional expressions are also preceded by a single dot as the first + character of a line. The possible conditionals are as follows: + + ..uunnddeeff _v_a_r_i_a_b_l_e + Un-define the specified global variable. Only global variables + may be un-defined. + + ..iiff [!]_e_x_p_r_e_s_s_i_o_n [_o_p_e_r_a_t_o_r _e_x_p_r_e_s_s_i_o_n _._._.] + Test the value of an expression. + + ..iiffddeeff [!]_v_a_r_i_a_b_l_e [_o_p_e_r_a_t_o_r _v_a_r_i_a_b_l_e _._._.] + Test the value of a variable. + + ..iiffnnddeeff [!]_v_a_r_i_a_b_l_e [_o_p_e_r_a_t_o_r _v_a_r_i_a_b_l_e _._._.] + Test the value of a variable. + + ..iiffmmaakkee [!]_t_a_r_g_e_t [_o_p_e_r_a_t_o_r _t_a_r_g_e_t _._._.] + Test the target being built. + + ..iiffnnmmaakkee [!] _t_a_r_g_e_t [_o_p_e_r_a_t_o_r _t_a_r_g_e_t _._._.] + Test the target being built. + + ..eellssee Reverse the sense of the last conditional. + + ..eelliiff [!] _e_x_p_r_e_s_s_i_o_n [_o_p_e_r_a_t_o_r _e_x_p_r_e_s_s_i_o_n _._._.] + A combination of `..eellssee' followed by `..iiff'. + + ..eelliiffddeeff [!]_v_a_r_i_a_b_l_e [_o_p_e_r_a_t_o_r _v_a_r_i_a_b_l_e _._._.] + A combination of `..eellssee' followed by `..iiffddeeff'. + + ..eelliiffnnddeeff [!]_v_a_r_i_a_b_l_e [_o_p_e_r_a_t_o_r _v_a_r_i_a_b_l_e _._._.] + A combination of `..eellssee' followed by `..iiffnnddeeff'. + + ..eelliiffmmaakkee [!]_t_a_r_g_e_t [_o_p_e_r_a_t_o_r _t_a_r_g_e_t _._._.] + A combination of `..eellssee' followed by `..iiffmmaakkee'. + + ..eelliiffnnmmaakkee [!]_t_a_r_g_e_t [_o_p_e_r_a_t_o_r _t_a_r_g_e_t _._._.] + A combination of `..eellssee' followed by `..iiffnnmmaakkee'. + + ..eennddiiff End the body of the conditional. + + The _o_p_e_r_a_t_o_r may be any one of the following: + + |||| Logical OR. + + &&&& Logical AND; of higher precedence than ``||''. + + As in C, bbmmaakkee will only evaluate a conditional as far as is necessary to + determine its value. Parentheses may be used to change the order of + evaluation. The boolean operator `!!' may be used to logically negate an + entire conditional. It is of higher precedence than `&&&&'. + + The value of _e_x_p_r_e_s_s_i_o_n may be any of the following: + + ddeeffiinneedd Takes a variable name as an argument and evaluates to true if + the variable has been defined. + + mmaakkee Takes a target name as an argument and evaluates to true if the + target was specified as part of bbmmaakkee's command line or was + declared the default target (either implicitly or explicitly, + see _._M_A_I_N) before the line containing the conditional. + + eemmppttyy Takes a variable, with possible modifiers, and evaluates to true + if the expansion of the variable would result in an empty + string. + + eexxiissttss Takes a file name as an argument and evaluates to true if the + file exists. The file is searched for on the system search path + (see _._P_A_T_H). + + ttaarrggeett Takes a target name as an argument and evaluates to true if the + target has been defined. + + ccoommmmaannddss + Takes a target name as an argument and evaluates to true if the + target has been defined and has commands associated with it. + + _E_x_p_r_e_s_s_i_o_n may also be an arithmetic or string comparison. Variable + expansion is performed on both sides of the comparison, after which the + integral values are compared. A value is interpreted as hexadecimal if + it is preceded by 0x, otherwise it is decimal; octal numbers are not sup- + ported. The standard C relational operators are all supported. If after + variable expansion, either the left or right hand side of a `====' or `!!==' + operator is not an integral value, then string comparison is performed + between the expanded variables. If no relational operator is given, it + is assumed that the expanded variable is being compared against 0 or an + empty string in the case of a string comparison. + + When bbmmaakkee is evaluating one of these conditional expression, and it + encounters a word it doesn't recognize, either the ``make'' or + ``defined'' expression is applied to it, depending on the form of the + conditional. If the form is `..iiffddeeff' or `..iiffnnddeeff', the ``defined'' + expression is applied. Similarly, if the form is `..iiffmmaakkee' or `..iiffnnmmaakkee, + tthhee ````mmaakkee''''' expression is applied. + + If the conditional evaluates to true the parsing of the makefile contin- + ues as before. If it evaluates to false, the following lines are + skipped. In both cases this continues until a `..eellssee' or `..eennddiiff' is + found. + + For loops are typically used to apply a set of rules to a list of files. + The syntax of a for loop is: + + ..ffoorr _v_a_r_i_a_b_l_e [_v_a_r_i_a_b_l_e _._._.] iinn _e_x_p_r_e_s_s_i_o_n + + ..eennddffoorr + + After the for eexxpprreessssiioonn is evaluated, it is split into words. On each + iteration of the loop, one word is taken and assigned to each vvaarriiaabbllee, + in order, and these vvaarriiaabblleess are substituted into the mmaakkee--rruulleess inside + the body of the for loop. The number of words must come out even; that + is, if there are three iteration variables, the number of words provided + must be a multiple of three. + +CCOOMMMMEENNTTSS + Comments begin with a hash (`#') character, anywhere but in a shell com- + mand line, and continue to the end of the line. + +SSPPEECCIIAALL SSOOUURRCCEESS ((AATTTTRRIIBBUUTTEESS)) + ..EEXXEECC Target is never out of date, but always execute commands any- + way. + + ..IIGGNNOORREE Ignore any errors from the commands associated with this tar- + get, exactly as if they all were preceded by a dash (`-'). + + ..MMAADDEE Mark all sources of this target as being up-to-date. + + ..MMAAKKEE Execute the commands associated with this target even if the --nn + or --tt options were specified. Normally used to mark recursive + bbmmaakkee's. + + ..NNOOPPAATTHH Do not search for the target in the directories specified by + ..PPAATTHH. + + ..NNOOTTMMAAIINN Normally bbmmaakkee selects the first target it encounters as the + default target to be built if no target was specified. This + source prevents this target from being selected. + + ..OOPPTTIIOONNAALL + If a target is marked with this attribute and bbmmaakkee can't fig- + ure out how to create it, it will ignore this fact and assume + the file isn't needed or already exists. + + ..PPHHOONNYY The target does not correspond to an actual file; it is always + considered to be out of date, and will not be created with the + --tt option. + + ..PPRREECCIIOOUUSS + When bbmmaakkee is interrupted, it removes any partially made tar- + gets. This source prevents the target from being removed. + + ..RREECCUURRSSIIVVEE + Synonym for ..MMAAKKEE. + + ..SSIILLEENNTT Do not echo any of the commands associated with this target, + exactly as if they all were preceded by an at sign (`@'). + + ..UUSSEE Turn the target into bbmmaakkee's version of a macro. When the tar- + get is used as a source for another target, the other target + acquires the commands, sources, and attributes (except for + ..UUSSEE) of the source. If the target already has commands, the + ..UUSSEE target's commands are appended to them. + + ..UUSSEEBBEEFFOORREE + Exactly like ..UUSSEE, but prepend the ..UUSSEEBBEEFFOORREE target commands + to the target. + + ..WWAAIITT If ..WWAAIITT appears in a dependency line, the sources that precede + it are made before the sources that succeed it in the line. + Loops are not detected and targets that form loops will be + silently ignored. + +SSPPEECCIIAALL TTAARRGGEETTSS + Special targets may not be included with other targets, i.e. they must be + the only target specified. + + ..BBEEGGIINN Any command lines attached to this target are executed before + anything else is done. + + ..DDEEFFAAUULLTT + This is sort of a ..UUSSEE rule for any target (that was used only + as a source) that bbmmaakkee can't figure out any other way to cre- + ate. Only the shell script is used. The ..IIMMPPSSRRCC variable of a + target that inherits ..DDEEFFAAUULLTT's commands is set to the target's + own name. + + ..EENNDD Any command lines attached to this target are executed after + everything else is done. + + ..IIGGNNOORREE Mark each of the sources with the ..IIGGNNOORREE attribute. If no + sources are specified, this is the equivalent of specifying the + --ii option. + + ..IINNTTEERRRRUUPPTT + If bbmmaakkee is interrupted, the commands for this target will be + executed. + + ..MMAAIINN If no target is specified when bbmmaakkee is invoked, this target + will be built. + + ..MMAAKKEEFFLLAAGGSS + This target provides a way to specify flags for bbmmaakkee when the + makefile is used. The flags are as if typed to the shell, + though the --ff option will have no effect. + + ..NNOOPPAATTHH Apply the ..NNOOPPAATTHH attribute to any specified sources. + + ..NNOOTTPPAARRAALLLLEELL + Disable parallel mode. + + ..NNOO__PPAARRAALLLLEELL + Synonym for ..NNOOTTPPAARRAALLLLEELL, for compatibility with other pmake + variants. + + ..OORRDDEERR The named targets are made in sequence. + + ..PPAATTHH The sources are directories which are to be searched for files + not found in the current directory. If no sources are speci- + fied, any previously specified directories are deleted. If the + source is the special ..DDOOTTLLAASSTT target, then the current working + directory is searched last. + + ..PPHHOONNYY Apply the ..PPHHOONNYY attribute to any specified sources. + + ..PPRREECCIIOOUUSS + Apply the ..PPRREECCIIOOUUSS attribute to any specified sources. If no + sources are specified, the ..PPRREECCIIOOUUSS attribute is applied to + every target in the file. + + ..SSHHEELLLL Sets the shell that bbmmaakkee will use to execute commands. The + sources are a set of _f_i_e_l_d_=_v_a_l_u_e pairs. + + _n_a_m_e This is the minimal specification, used to select + one of the builtin shell specs; _s_h, _k_s_h, and _c_s_h. + + _p_a_t_h Specifies the path to the shell. + + _h_a_s_E_r_r_C_t_l Indicates whether the shell supports exit on error. + + _c_h_e_c_k The command to turn on error checking. + + _i_g_n_o_r_e The command to disable error checking. + + _e_c_h_o The command to turn on echoing of commands executed. + + _q_u_i_e_t The command to turn off echoing of commands exe- + cuted. + + _f_i_l_t_e_r The output to filter after issuing the _q_u_i_e_t com- + mand. It is typically identical to _q_u_i_e_t. + + _e_r_r_F_l_a_g The flag to pass the shell to enable error checking. + + _e_c_h_o_F_l_a_g The flag to pass the shell to enable command echo- + ing. + Example: + + .SHELL: name=ksh path=/bin/ksh hasErrCtl=true \ + check="set -e" ignore="set +e" \ + echo="set -v" quiet="set +v" filter="set +v" \ + echoFlag=v errFlag=e + + ..SSIILLEENNTT Apply the ..SSIILLEENNTT attribute to any specified sources. If no + sources are specified, the ..SSIILLEENNTT attribute is applied to every + command in the file. + + ..SSUUFFFFIIXXEESS + Each source specifies a suffix to bbmmaakkee. If no sources are + specified, any previously specified suffixes are deleted. + +EENNVVIIRROONNMMEENNTT + bbmmaakkee uses the following environment variables, if they exist: MACHINE, + MACHINE_ARCH, MAKE, MAKEFLAGS, MAKEOBJDIR, MAKEOBJDIRPREFIX, MAKESYSPATH, + and PWD. + + If MAKEOBJDIRPREFIX is set, then bbmmaakkee will chdir(2) to ${MAKEOBJDIRPRE- + FIX}${.CURDIR} if it exists. Otherwise if MAKEOBJDIR and the named + directory exists bbmmaakkee will chdir(2) to it. These actions are taken + before any makefiles are read which is why they need to be set in the + environment. + +FFIILLEESS + .depend list of dependencies + Makefile list of dependencies + makefile list of dependencies + sys.mk system makefile + /usr/share/mk system makefile directory + +SSEEEE AALLSSOO + mkdep(1) + +HHIISSTTOORRYY + bbmmaakkee is derived from NetBSD's make(1). It uses autoconf to facilitate + portability to other platforms. + +NetBSD 2.0 June 1, 2005 NetBSD 2.0 diff --git a/devel/bmake/files/boot-strap b/devel/bmake/files/boot-strap new file mode 100755 index 0000000000000..c9c014f5f8cc5 --- /dev/null +++ b/devel/bmake/files/boot-strap @@ -0,0 +1,183 @@ +: +# NAME: +# boot-strap +# +# SYNOPSIS: +# boot-strap [--"configure_arg" ... ][-s "srcdir"][-m "mksrc"]\\ +# ["prefix" ["bmakesrc" ["mksrc"]]] +# +# DESCRIPTION: +# This script is used to configure/build bmake it builds for +# each OS in a subdir to keep the src clean. +# On successful completion it echos commands to put the new +# bmake binary into the /configs tree (if it exists) +# (http://www.crufty.net/FreeWare/configs.html), $prefix/bin +# and a suitable ~/*bin directory. +# +# Possibly useful configure_args: +# +# --with-machine="machine" +# set "machine" to override that determined by +# machine.sh +# +# --with-force-machine="machine" +# force "machine" even if uname(3) provides a value. +# +# --with-machine_arch="machine_arch" +# set "machine_arch" to override that determined by +# machine.sh +# +# --with-default-sys-path="syspath" +# set an explicit default "syspath" which is where bmake +# will look for sys.mk and friends. +# +# --with-prefix-sys-path="syspath" +# prefix the built in default syspath with "syspath" +# +# AUTHOR: +# Simon J. Gerraty + +# RCSid: +# $Id: boot-strap,v 1.1.1.1 2005/12/02 00:02:59 sjg Exp $ +# +# @(#) Copyright (c) 2001 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@crufty.net +# + +Mydir=`dirname $0` +. $Mydir/os.sh +case $Mydir in +/*) ;; +*) Mydir=`cd $Mydir; /bin/pwd`;; +esac + + +Usage() { + [ "$1" ] && echo "ERROR: $@" >&2 + echo "Usage:" >&2 + echo "$0 [-- ...][-s ][-m ][ [[] []]]" >&2 + exit 1 +} + +Error() { + echo "ERROR: $@" >&2 + exit 1 +} + + +CONFIGURE_ARGS= +prefix= +srcdir= +mksrc= +objdir= +quiet=: + +while : +do + case "$1" in + --) shift; break;; + --prefix) prefix=$2; shift 2;; + --prefix=*) prefix=`IFS="="; set -- $1; echo $2`; shift;; + --src=*) srcdir=`IFS="="; set -- $1; echo $2`; shift;; + --with-mksrc=*) mksrc=`IFS="="; set -- $1; echo $2`; shift;; + -s|--src) srcdir=$2; shift 2;; + -m|--mksrc) mksrc=$2; shift 2;; + -o|--objdir) objdir=$2; shift 2;; + -q) quiet=; shift;; + --*) CONFIGURE_ARGS="$CONFIGURE_ARGS $1"; shift;; + *=*) eval "$1"; export `IFS="="; set -- $1; echo $1`; shift;; + *) break;; + esac +done + +AddConfigure() { + case " $CONFIGURE_ARGS " in + *" $1"*) ;; + *) CONFIGURE_ARGS="$CONFIGURE_ARGS $1$2";; + esac +} + +GetDir() { + match=$1 + shift + for dir in $* + do + [ -d $dir ] || continue + case "/$dir/" in + *$match*) ;; + *) continue;; + esac + case "$dir/" in + *./*) cd $dir && /bin/pwd;; + /*) echo $dir;; + *) cd $dir && /bin/pwd;; + esac + break + done +} + +srcdir=`GetDir /bmake $srcdir $2 $Mydir ./bmake* $Mydir/../bmake*` +[ -d ${srcdir:-/dev/null} ] || Usage +case "$mksrc" in +none|-) # we don't want it + mksrc= + ;; +*) # guess we want mksrc... + mksrc=`GetDir /mk $mksrc $3 ./mk* $srcdir/mk* $srcdir/../mk*` + [ -d ${mksrc:-/dev/null} ] || Usage + ;; +esac + +# Ok, get to work... +objdir=${objdir:-$OS} +[ -d $objdir ] || mkdir -p $objdir +[ -d $objdir ] || mkdir $objdir +cd $objdir || exit 1 + +AddConfigure --prefix= $prefix +case "$CONFIGURE_ARGS" in +*--with-prefix-sys-path*) ;; # skip +*) AddConfigure --with-default-sys-path= $prefix/share/mk;; +esac +[ "$mksrc" ] && AddConfigure --with-mksrc= $mksrc +$srcdir/configure $CONFIGURE_ARGS || exit 1 +${MAKE:-make} -f makefile.boot clean +${MAKE:-make} -f makefile.boot bootstrap || exit 1 + +# If -q given, we don't want all the install instructions +$quiet exit 0 + +make_version=`./bmake -m ./mk -m /usr/share/mk -f ./Makefile -V MAKE_VERSION | ( read one two; echo $one )` + +install_prefix() { + bdir=$1/bin + echo + echo Commands to install into $1/ + echo + echo mkdir -p $bdir + echo cp $objdir/bmake $bdir/$make_version + echo rm -f $bdir/bmake + echo ln -s $make_version $bdir/bmake + [ "$mksrc" ] && echo $mksrc/install-mk $1/share/mk +} + +case $prefix/ in +$HOME/*) ;; +*) CONFIGS=${CONFIGS:-/configs} + [ -d $CONFIGS ] && + install_prefix $CONFIGS/$OS/$OSMAJOR.X/$MACHINE_ARCH$prefix + # I like to keep a copy here... + install_prefix $HOME/$HOST_TARGET + ;; +esac + +install_prefix $prefix diff --git a/devel/bmake/files/buf.c b/devel/bmake/files/buf.c new file mode 100644 index 0000000000000..88892914bb26b --- /dev/null +++ b/devel/bmake/files/buf.c @@ -0,0 +1,335 @@ +/* $NetBSD: buf.c,v 1.1.1.1 2005/12/02 00:02:59 sjg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: buf.c,v 1.1.1.1 2005/12/02 00:02:59 sjg 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 2005/12/02 00:02:59 sjg 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(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(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(Buffer bp, int *numBytesPtr) +{ + + if (numBytesPtr != 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(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(Buffer buf) +{ + return (buf->inPtr - buf->outPtr); +} + +/*- + *----------------------------------------------------------------------- + * Buf_Init -- + * Initialize a buffer. If no initial size is given, a reasonable + * default is used. + * + * Input: + * size Initial size for the buffer + * + * 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(int size) +{ + Buffer bp; /* New Buffer */ + + bp = emalloc(sizeof(*bp)); + + if (size <= 0) { + size = BUF_DEF_SIZE; + } + bp->left = bp->size = size; + bp->buffer = emalloc(size); + bp->inPtr = bp->outPtr = bp->buffer; + *bp->inPtr = 0; + + return (bp); +} + +/*- + *----------------------------------------------------------------------- + * Buf_Destroy -- + * Nuke a buffer and all its resources. + * + * Input: + * buf Buffer to destroy + * freeData TRUE if the data should be destroyed + * + * Results: + * None. + * + * Side Effects: + * The buffer is freed. + * + *----------------------------------------------------------------------- + */ +void +Buf_Destroy(Buffer buf, Boolean freeData) +{ + + if (freeData) { + free(buf->buffer); + } + free(buf); +} + +/*- + *----------------------------------------------------------------------- + * Buf_ReplaceLastByte -- + * Replace the last byte in a buffer. + * + * Input: + * buf buffer to augment + * byte byte to be written + * + * 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(Buffer buf, int byte) +{ + if (buf->inPtr == buf->outPtr) + Buf_AddByte(buf, byte); + else + *(buf->inPtr - 1) = byte; +} diff --git a/devel/bmake/files/buf.h b/devel/bmake/files/buf.h new file mode 100644 index 0000000000000..9d2077fd485e1 --- /dev/null +++ b/devel/bmake/files/buf.h @@ -0,0 +1,111 @@ +/* $NetBSD: buf.h,v 1.1.1.1 2005/12/02 00:02:59 sjg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. + * + * 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 + */ + +/* + * 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(Buffer, int); +void Buf_AddBytes(Buffer, int, const Byte *); +Byte *Buf_GetAll(Buffer, int *); +void Buf_Discard(Buffer, int); +int Buf_Size(Buffer); +Buffer Buf_Init(int); +void Buf_Destroy(Buffer, Boolean); +void Buf_ReplaceLastByte(Buffer, int); + +#endif /* _BUF_H */ diff --git a/devel/bmake/files/compat.c b/devel/bmake/files/compat.c new file mode 100644 index 0000000000000..4de01f63a576c --- /dev/null +++ b/devel/bmake/files/compat.c @@ -0,0 +1,701 @@ +/* $NetBSD: compat.c,v 1.1.1.1 2005/12/02 00:02:59 sjg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: compat.c,v 1.1.1.1 2005/12/02 00:02:59 sjg 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 2005/12/02 00:02:59 sjg Exp $"); +#endif +#endif /* not lint */ +#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 "wait.h" + +#include +#include +#include +#include + +#include "make.h" +#include "hash.h" +#include "dir.h" +#include "job.h" +#include "pathnames.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(int); + +static void +Compat_Init(void) +{ + const char *cp; + + Shell_Init(); /* setup default shell */ + + for (cp = "#=|^(){};&<>*?[]:$`\\\n"; *cp != '\0'; cp++) { + meta[(unsigned char) *cp] = 1; + } + /* + * The null character serves as a sentinel in the string. + */ + meta[0] = 1; +} + +/*- + *----------------------------------------------------------------------- + * 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(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", 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) { + Compat_Make(gn, 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. + * + * Input: + * cmdp Command to execute + * gnp Node from which the command came + * + * Results: + * 0 if the command succeeded, 1 if an error occurred. + * + * Side Effects: + * The node's 'made' field may be set to ERROR. + * + *----------------------------------------------------------------------- + */ +int +CompatRunCommand(ClientData cmdp, ClientData gnp) +{ + char *cmdStart; /* Start of expanded command */ + char *cp, *bp; + Boolean silent, /* Don't print command */ + doIt, /* Execute even if -n */ + errCheck; /* Check errors */ + WAIT_T reason; /* Reason for child's death */ + int status; /* Description of child's death */ + int cpid; /* Child actually found */ + ReturnStatus retstat; /* Status of fork */ + LstNode cmdNode; /* Node where current command is located */ + const 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; + (void) &cmd; +#endif + silent = gn->type & OP_SILENT; + errCheck = !(gn->type & OP_IGNORE); + doIt = FALSE; + + 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 == '-') || (*cmd == '+')) { + switch (*cmd) { + case '@': + silent = TRUE; + break; + case '-': + errCheck = FALSE; + break; + case '+': + doIt = TRUE; + if (!meta[0]) /* we came here from jobs */ + Compat_Init(); + break; + } + 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 (!doIt && 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. + */ + static const char *shargv[4]; + + shargv[0] = shellPath; + /* + * The following work for any of the builtin shell specs. + */ + if (DEBUG(SHELL)) + shargv[1] = "-xc"; + else + shargv[1] = "-c"; + shargv[2] = cmd; + shargv[3] = 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 = (const char **)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], (char *const *)UNCONST(av)); + else + (void)execv(av[0], (char *const *)UNCONST(av)); + execError("exec", av[0]); + _exit(1); + } + if (bp) { + free(av); + free(bp); + } + Lst_Replace(cmdNode, (ClientData) NULL); + + /* + * The child is off and running. Now all we can do is wait... + */ + while (1) { + + while ((retstat = wait(&reason)) != cpid) { + if (retstat == -1 && errno != EINTR) { + break; + } + } + + if (retstat > -1) { + if (WIFSTOPPED(reason)) { + status = WSTOPSIG(reason); /* stopped */ + } else if (WIFEXITED(reason)) { + status = WEXITSTATUS(reason); /* exited */ + if (status != 0) { + if (DEBUG(ERROR)) { + printf("\n*** Failed target: %s\n*** Failed command: ", + gn->name); + for (cp = cmd; *cp; ) { + if (isspace((unsigned char)*cp)) { + putchar(' '); + while (isspace((unsigned char)*cp)) + cp++; + } else { + putchar(*cp); + cp++; + } + } + printf("\n"); + } + 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", retstat, strerror(errno)); + /*NOTREACHED*/ + } + } + free(cmdStart); + + return (status); +} + +/*- + *----------------------------------------------------------------------- + * Compat_Make -- + * Make a target. + * + * Input: + * gnp The node to make + * pgnp Parent to abort if necessary + * + * Results: + * 0 + * + * Side Effects: + * If an error is detected and not being ignored, the process exits. + * + *----------------------------------------------------------------------- + */ +int +Compat_Make(ClientData gnp, ClientData pgnp) +{ + GNode *gn = (GNode *)gnp; + GNode *pgn = (GNode *)pgnp; + + if (!meta[0]) /* we came here from jobs */ + Compat_Init(); + if (gn->made == UNMADE && (gn == pgn || (pgn->type & OP_MADE) == 0)) { + /* + * 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; + if ((gn->type & OP_MADE) == 0) + Suff_FindDeps(gn); + Lst_ForEach(gn->children, Compat_Make, (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", 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, Compat_Make, pgnp); + return (0); +} + +/*- + *----------------------------------------------------------------------- + * Compat_Run -- + * Initialize this mode and start making. + * + * Input: + * targs List of target nodes to re-create + * + * Results: + * None. + * + * Side Effects: + * Guess what? + * + *----------------------------------------------------------------------- + */ +void +Compat_Run(Lst targs) +{ + GNode *gn = NULL;/* Current root target */ + int errors; /* Number of targets not remade due to errors */ + + Compat_Init(); + + 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); + } + + ENDNode = Targ_FindNode(".END", TARG_CREATE); + ENDNode->type = OP_SPECIAL; + /* + * 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) { + Compat_Make(gn, 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 Compat_Make on + * it to create the thing. Compat_Make 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); + Compat_Make(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) { + Compat_Make(ENDNode, ENDNode); + if (gn->made == ERROR) { + PrintOnError("\n\nStop."); + exit(1); + } + } +} diff --git a/devel/bmake/files/cond.c b/devel/bmake/files/cond.c new file mode 100644 index 0000000000000..504d907d18831 --- /dev/null +++ b/devel/bmake/files/cond.c @@ -0,0 +1,1395 @@ +/* $NetBSD: cond.c,v 1.1.1.1 2005/12/02 00:02:59 sjg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: cond.c,v 1.1.1.1 2005/12/02 00:02:59 sjg 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 2005/12/02 00:02:59 sjg 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 "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 -> "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(Token); +static int CondGetArg(char **, char **, const char *, Boolean); +static Boolean CondDoDefined(int, char *); +static int CondStrMatch(ClientData, ClientData); +static Boolean CondDoMake(int, char *); +static Boolean CondDoExists(int, char *); +static Boolean CondDoTarget(int, char *); +static Boolean CondDoCommands(int, char *); +static char * CondCvtArg(char *, double *); +static Token CondToken(Boolean); +static Token CondT(Boolean); +static Token CondF(Boolean); +static Token CondE(Boolean); + +static struct If { + const char *form; /* Form of if */ + int formlen; /* Length of form */ + Boolean doNot; /* TRUE if default function should be negated */ + Boolean (*defProc)(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)(int, char *); /* Default function to apply */ +static char *condExpr; /* The expression to parse */ +static Token condPushBack=None; /* Single push-back token used in + * parsing */ + +#define MAXIF 64 /* greatest depth of #if'ing */ + +static Boolean finalElse[MAXIF+1][MAXIF+1]; /* Seen final else (stack) */ +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 */ + +static int +istoken(const char *str, const char *tok, size_t len) +{ + return strncmp(str, tok, len) == 0 && !isalpha((unsigned char)str[len]); +} + +/*- + *----------------------------------------------------------------------- + * CondPushBack -- + * Push back the most recent token read. We only need one level of + * this, so the thing is just stored in 'condPushback'. + * + * Input: + * t Token to push back into the "stream" + * + * Results: + * None. + * + * Side Effects: + * condPushback is overwritten. + * + *----------------------------------------------------------------------- + */ +static void +CondPushBack(Token t) +{ + condPushBack = t; +} + +/*- + *----------------------------------------------------------------------- + * CondGetArg -- + * Find the argument of a built-in function. + * + * Input: + * parens TRUE if arg should be bounded by parens + * + * 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(char **linePtr, char **argPtr, const char *func, Boolean parens) +{ + char *cp; + int argLen; + 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) == 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(int argLen, char *arg) +{ + char savec = arg[argLen]; + char *p1; + Boolean result; + + arg[argLen] = '\0'; + if (Var_Value(arg, VAR_CMD, &p1) != 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(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(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(int argLen, char *arg) +{ + char savec = arg[argLen]; + Boolean result; + char *path; + + arg[argLen] = '\0'; + path = Dir_FindFile(arg, dirSearchPath); + if (path != 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(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(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 NULL if string was fully consumed, + * else returns remaining input. + * + * Side Effects: + * Can change 'value' even if string is not a valid number. + * + * + *----------------------------------------------------------------------- + */ +static char * +CondCvtArg(char *str, double *value) +{ + if ((*str == '0') && (str[1] == 'x')) { + 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 + break; + i = (i << 4) + x; + } + *value = (double) i; + return *str ? str : NULL; + } else { + char *eptr; + *value = strtod(str, &eptr); + return *eptr ? eptr : NULL; + } +} + +/*- + *----------------------------------------------------------------------- + * CondGetString -- + * Get a string from a variable reference or an optionally quoted + * string. This is called for the lhs and rhs of string compares. + * + * Results: + * Sets doFree if needed, + * Sets quoted if string was quoted, + * Returns NULL on error, + * else returns string - absent any quotes. + * + * Side Effects: + * Moves condExpr to end of this token. + * + * + *----------------------------------------------------------------------- + */ +static char * +CondGetString(Boolean doEval, Boolean *quoted, Boolean *doFree) +{ + Buffer buf; + char *cp; + char *str; + int len; + int qt; + char *start; + + buf = Buf_Init(0); + str = NULL; + *quoted = qt = *condExpr == '"' ? 1 : 0; + if (qt) + condExpr++; + for (start = condExpr; *condExpr && str == NULL; condExpr++) { + switch (*condExpr) { + case '\\': + if (condExpr[1] != '\0') { + condExpr++; + Buf_AddByte(buf, (Byte)*condExpr); + } + break; + case '"': + if (qt) { + condExpr++; /* we don't want the quotes */ + goto got_str; + } else + Buf_AddByte(buf, (Byte)*condExpr); /* likely? */ + break; + case ')': + case '!': + case '=': + case '>': + case '<': + case ' ': + case '\t': + if (!qt) + goto got_str; + else + Buf_AddByte(buf, (Byte)*condExpr); + break; + case '$': + /* if we are in quotes, then an undefined variable is ok */ + str = Var_Parse(condExpr, VAR_CMD, (qt ? 0 : doEval), + &len, doFree); + if (str == var_Error) { + /* + * Even if !doEval, we still report syntax errors, which + * is what getting var_Error back with !doEval means. + */ + str = NULL; + goto cleanup; + } + condExpr += len; + /* + * If the '$' was first char (no quotes), and we are + * followed by space, the operator or end of expression, + * we are done. + */ + if ((condExpr == start + len) && + (*condExpr == '\0' || + isspace((unsigned char) *condExpr) || + strchr("!=><)", *condExpr))) { + goto cleanup; + } + /* + * Nope, we better copy str to buf + */ + for (cp = str; *cp; cp++) { + Buf_AddByte(buf, (Byte)*cp); + } + if (*doFree) + free(str); + *doFree = FALSE; + str = NULL; /* not finished yet */ + condExpr--; /* don't skip over next char */ + break; + default: + Buf_AddByte(buf, (Byte)*condExpr); + break; + } + } + got_str: + Buf_AddByte(buf, (Byte)'\0'); + str = (char *)Buf_GetAll(buf, NULL); + *doFree = TRUE; + cleanup: + Buf_Destroy(buf, FALSE); + return str; +} + +/*- + *----------------------------------------------------------------------- + * 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(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 '#': + case '\n': + case '\0': + t = EndOfFile; + break; + case '"': + case '$': { + char *lhs; + char *rhs; + char *op; + Boolean lhsFree; + Boolean rhsFree; + Boolean lhsQuoted; + Boolean rhsQuoted; + + rhs = NULL; + lhsFree = rhsFree = FALSE; + lhsQuoted = rhsQuoted = FALSE; + + /* + * Parse the variable spec and skip over it, saving its + * value in lhs. + */ + t = Err; + lhs = CondGetString(doEval, &lhsQuoted, &lhsFree); + if (!lhs) + return Err; + /* + * 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 = UNCONST("!="); + if (lhsQuoted) + rhs = UNCONST(""); + else + rhs = UNCONST("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 = CondGetString(doEval, &rhsQuoted, &rhsFree); + if (!rhs) + return Err; +do_compare: + if (rhsQuoted || lhsQuoted) { +do_string_compare: + if (((*op != '!') && (*op != '=')) || (op[1] != '=')) { + Parse_Error(PARSE_WARNING, + "String comparison operator should be either == or !="); + goto error; + } + + if (DEBUG(COND)) { + printf("lhs = \"%s\", rhs = \"%s\", op = %.2s\n", + lhs, rhs, op); + } + /* + * Null-terminate rhs and perform the comparison. + * t is set to the result. + */ + if (*op == '=') { + t = strcmp(lhs, rhs) ? False : True; + } else { + t = strcmp(lhs, rhs) ? True : False; + } + } 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 *cp; + + if (CondCvtArg(lhs, &left)) + goto do_string_compare; + if ((cp = CondCvtArg(rhs, &right)) && + cp == rhs) + goto do_string_compare; + + 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 (lhsFree) + free(lhs); + if (rhsFree) + free(rhs); + break; + } + default: { + Boolean (*evalProc)(int, char *); + Boolean invert = FALSE; + char *arg; + int arglen; + + if (istoken(condExpr, "defined", 7)) { + /* + * 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 (istoken(condExpr, "make", 4)) { + /* + * 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 (istoken(condExpr, "exists", 6)) { + /* + * 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 (istoken(condExpr, "empty", 5)) { + /* + * 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, + FALSE, &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 (istoken(condExpr, "target", 6)) { + /* + * 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 (istoken(condExpr, "commands", 8)) { + /* + * 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(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(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(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(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. + * + * Input: + * line Line to parse + * + * 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(char *line) +{ + 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 (istoken(line, "endif", 5)) { + /* + * 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. + */ + finalElse[condTop][skipIfLevel] = FALSE; + 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 != NULL; ifp++) { + if (istoken(ifp->form, line, ifp->formlen)) { + break; + } + } + + if (ifp->form == NULL) { + /* + * 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 && istoken(line, "se", 2)) { + if (finalElse[condTop][skipIfLevel]) { + Parse_Error(PARSE_WARNING, "extra else"); + } else { + finalElse[condTop][skipIfLevel] = TRUE; + } + 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; + if (skipIfLevel >= MAXIF) { + Parse_Error(PARSE_FATAL, "Too many nested if's. %d max.", MAXIF); + return (COND_INVALID); + } + finalElse[condTop][skipIfLevel] = FALSE; + 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; + finalElse[condTop][skipIfLevel] = FALSE; + } 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(void) +{ + if (condTop != MAXIF) { + Parse_Error(PARSE_FATAL, "%d open conditional%s", MAXIF-condTop, + MAXIF-condTop == 1 ? "" : "s"); + } + condTop = MAXIF; +} diff --git a/devel/bmake/files/config.h.in b/devel/bmake/files/config.h.in new file mode 100644 index 0000000000000..96bddc2133e4f --- /dev/null +++ b/devel/bmake/files/config.h.in @@ -0,0 +1,246 @@ +/* config.h.in. Generated from configure.in by autoheader. */ + +/* Shell spec to use by default */ +#undef DEFSHELL + +/* Define to 1 if you have the header file. */ +#undef HAVE_AR_H + +/* Define to 1 if you have the `asprintf' function. */ +#undef HAVE_ASPRINTF + +/* Define to 1 if you have the declaration of `sys_siglist', and to 0 if you + don't. */ +#undef HAVE_DECL_SYS_SIGLIST + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#undef HAVE_DIRENT_H + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +#undef HAVE_DOPRNT + +/* Define to 1 if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define to 1 if you have the `fork' function. */ +#undef HAVE_FORK + +/* Define to 1 if you have the `getcwd' function. */ +#undef HAVE_GETCWD + +/* Define to 1 if you have the `getenv' function. */ +#undef HAVE_GETENV + +/* Define to 1 if you have the `getopt' function. */ +#undef HAVE_GETOPT + +/* Define to 1 if you have the `getwd' function. */ +#undef HAVE_GETWD + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `killpg' function. */ +#undef HAVE_KILLPG + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file, and it defines `DIR'. */ +#undef HAVE_NDIR_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_PATHS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_POLL_H + +/* Define to 1 if you have the `putenv' function. */ +#undef HAVE_PUTENV + +/* Define to 1 if you have the header file. */ +#undef HAVE_RANLIB_H + +/* Define to 1 if you have the `select' function. */ +#undef HAVE_SELECT + +/* Define to 1 if you have the `setenv' function. */ +#undef HAVE_SETENV + +/* Define to 1 if you have the `sigaction' function. */ +#undef HAVE_SIGACTION + +/* Define to 1 if you have the `sigvec' function. */ +#undef HAVE_SIGVEC + +/* Define to 1 if you have the `snprintf' function. */ +#undef HAVE_SNPRINTF + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `strdup' function. */ +#undef HAVE_STRDUP + +/* Define to 1 if you have the `strerror' function. */ +#undef HAVE_STRERROR + +/* Define to 1 if you have the `strftime' function. */ +#undef HAVE_STRFTIME + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `strtod' function. */ +#undef HAVE_STRTOD + +/* Define to 1 if you have the `strtol' function. */ +#undef HAVE_STRTOL + +/* Define to 1 if `st_rdev' is member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_RDEV + +/* Define to 1 if your `struct stat' has `st_rdev'. Deprecated, use + `HAVE_STRUCT_STAT_ST_RDEV' instead. */ +#undef HAVE_ST_RDEV + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#undef HAVE_SYS_DIR_H + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#undef HAVE_SYS_NDIR_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SELECT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_UIO_H + +/* Define to 1 if you have that is POSIX.1 compatible. */ +#undef HAVE_SYS_WAIT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UTIME_H + +/* Define to 1 if you have the `vasprintf' function. */ +#undef HAVE_VASPRINTF + +/* Define to 1 if you have the `vfork' function. */ +#undef HAVE_VFORK + +/* Define to 1 if you have the header file. */ +#undef HAVE_VFORK_H + +/* Define to 1 if you have the `vprintf' function. */ +#undef HAVE_VPRINTF + +/* Define to 1 if you have the `vsnprintf' function. */ +#undef HAVE_VSNPRINTF + +/* Define to 1 if you have the `wait3' function. */ +#undef HAVE_WAIT3 + +/* Define to 1 if you have the `wait4' function. */ +#undef HAVE_WAIT4 + +/* Define to 1 if you have the `waitpid' function. */ +#undef HAVE_WAITPID + +/* Define to 1 if `fork' works. */ +#undef HAVE_WORKING_FORK + +/* Define to 1 if `vfork' works. */ +#undef HAVE_WORKING_VFORK + +/* define if your compiler has __attribute__ */ +#undef HAVE___ATTRIBUTE__ + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define as the return type of signal handlers (`int' or `void'). */ +#undef RETSIGTYPE + +/* Define to 1 if the `S_IS*' macros in do not work properly. */ +#undef STAT_MACROS_BROKEN + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define to 1 if you can safely include both and . */ +#undef TIME_WITH_SYS_TIME + +/* Define to 1 if your declares `struct tm'. */ +#undef TM_IN_SYS_TIME + +/* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +#undef WORDS_BIGENDIAN + +/* Define to 1 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 1 if on MINIX. */ +#undef _MINIX + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +#undef _POSIX_1_SOURCE + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +#undef _POSIX_SOURCE + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `long' if does not define. */ +#undef off_t + +/* Define to `int' if does not define. */ +#undef pid_t + +/* Define to `unsigned' if does not define. */ +#undef size_t + +/* Define as `fork' if `vfork' does not work. */ +#undef vfork diff --git a/devel/bmake/files/configure b/devel/bmake/files/configure new file mode 100755 index 0000000000000..9e461f1f99243 --- /dev/null +++ b/devel/bmake/files/configure @@ -0,0 +1,7052 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.57. +# +# Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002 +# Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi + +# Support unset when possible. +if (FOO=FOO; unset FOO) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -n "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2 + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="sed y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="sed y%*+%pp%;s%[^_$as_cr_alnum]%_%g" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +exec 6>&1 + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_config_libobj_dir=. +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Maximum number of lines to put in a shell here document. +# This variable seems obsolete. It should probably be removed, and +# only ac_max_sed_lines should be used. +: ${ac_max_here_lines=38} + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= + +ac_unique_file="makefile.boot.in" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_SYS_STAT_H +# include +#endif +#if STDC_HEADERS +# include +# include +#else +# if HAVE_STDLIB_H +# include +# endif +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include +# endif +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_INTTYPES_H +# include +#else +# if HAVE_STDINT_H +# include +# endif +#endif +#if HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP EGREP INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA ac_exe_suffix LIBOBJS machine force_machine machine_arch mksrc default_sys_path prefix_sys_path INSTALL GCC diff_u LTLIBOBJS' +ac_subst_files='' + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +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' + +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 + + ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` + + # 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_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$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 ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + eval "enable_$ac_feature=no" ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) 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 | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$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 | -n) + 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_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package| sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "with_$ac_package='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + 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 "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` + eval "$ac_envvar='$ac_optarg'" + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute paths. +for ac_var in exec_prefix prefix +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* | NONE | '' ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# Be sure to have absolute paths. +for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ + localstatedir libdir includedir oldincludedir infodir mandir +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +# 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_confdir=`(dirname "$0") 2>/dev/null || +$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$0" : 'X\(//\)[^/]' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$0" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + 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 "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2 + { (exit 1); exit 1; }; } + else + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } + fi +fi +(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null || + { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2 + { (exit 1); exit 1; }; } +srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` +ac_env_build_alias_set=${build_alias+set} +ac_env_build_alias_value=$build_alias +ac_cv_env_build_alias_set=${build_alias+set} +ac_cv_env_build_alias_value=$build_alias +ac_env_host_alias_set=${host_alias+set} +ac_env_host_alias_value=$host_alias +ac_cv_env_host_alias_set=${host_alias+set} +ac_cv_env_host_alias_value=$host_alias +ac_env_target_alias_set=${target_alias+set} +ac_env_target_alias_value=$target_alias +ac_cv_env_target_alias_set=${target_alias+set} +ac_cv_env_target_alias_value=$target_alias +ac_env_CC_set=${CC+set} +ac_env_CC_value=$CC +ac_cv_env_CC_set=${CC+set} +ac_cv_env_CC_value=$CC +ac_env_CFLAGS_set=${CFLAGS+set} +ac_env_CFLAGS_value=$CFLAGS +ac_cv_env_CFLAGS_set=${CFLAGS+set} +ac_cv_env_CFLAGS_value=$CFLAGS +ac_env_LDFLAGS_set=${LDFLAGS+set} +ac_env_LDFLAGS_value=$LDFLAGS +ac_cv_env_LDFLAGS_set=${LDFLAGS+set} +ac_cv_env_LDFLAGS_value=$LDFLAGS +ac_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_env_CPPFLAGS_value=$CPPFLAGS +ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_cv_env_CPPFLAGS_value=$CPPFLAGS +ac_env_CPP_set=${CPP+set} +ac_env_CPP_value=$CPP +ac_cv_env_CPP_set=${CPP+set} +ac_cv_env_CPP_value=$CPP + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # 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 <<_ACEOF +\`configure' configures this package to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +_ACEOF + + cat <<_ACEOF +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data [PREFIX/share] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --infodir=DIR info documentation [PREFIX/info] + --mandir=DIR man documentation [PREFIX/man] +_ACEOF + + cat <<\_ACEOF +_ACEOF +fi + +if test -n "$ac_init_help"; then + + cat <<\_ACEOF + +Optional Features: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --disable-pwd-override disable \$PWD overriding getcwd() + --disable-check-make-chdir disable make trying to guess + when it should automatically cd \${.CURDIR} + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-defshell=SHELL explicitly set DEFSHELL to either /bin/sh or /bin/ksh + --with-machine=MACHINE explicitly set MACHINE + --with-force-machine=MACHINE set FORCE_MACHINE + --with-force-machine-arch=MACHINE set FORCE_MACHINE_ARCH + --with-machine_arch=MACHINE_ARCH explicitly set MACHINE_ARCH + --with-default-sys-path=PATH:DIR:LIST use an explicit _PATH_DEFSYSPATH + MAKESYSPATH is a ':' separated list of directories + that bmake will search for system .mk files. + _PATH_DEFSYSPATH is its default value. + --with-path-objdirprefix=PATH override _PATH_OBJDIRPREFIX + --with-mksrc=PATH tell makefile.boot where to find mk src + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + CPPFLAGS C/C++ preprocessor flags, e.g. -I if you have + headers in a nonstandard directory + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +_ACEOF +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + ac_popdir=`pwd` + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d $ac_dir || continue + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac +# Don't blindly perform a `cd "$ac_dir"/$ac_foo && pwd` since $ac_foo can be +# absolute. +ac_abs_builddir=`cd "$ac_dir" && cd $ac_builddir && pwd` +ac_abs_top_builddir=`cd "$ac_dir" && cd ${ac_top_builddir}. && pwd` +ac_abs_srcdir=`cd "$ac_dir" && cd $ac_srcdir && pwd` +ac_abs_top_srcdir=`cd "$ac_dir" && cd $ac_top_srcdir && pwd` + + cd $ac_dir + # Check for guested configure; otherwise get Cygnus style configure. + if test -f $ac_srcdir/configure.gnu; then + echo + $SHELL $ac_srcdir/configure.gnu --help=recursive + elif test -f $ac_srcdir/configure; then + echo + $SHELL $ac_srcdir/configure --help=recursive + elif test -f $ac_srcdir/configure.ac || + test -f $ac_srcdir/configure.in; then + echo + $ac_configure --help + else + echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi + cd $ac_popdir + done +fi + +test -n "$ac_init_help" && exit 0 +if $ac_init_version; then + cat <<\_ACEOF + +Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002 +Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit 0 +fi +exec 5>config.log +cat >&5 <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by $as_me, which was +generated by GNU Autoconf 2.57. Invocation command line was + + $ $0 $@ + +_ACEOF +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +hostinfo = `(hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + echo "PATH: $as_dir" +done + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_sep= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$ac_configure_args1 '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" + # Get rid of the leading space. + ac_sep=" " + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Be sure not to use single quotes in there, as some shells, +# such as our DU 5.0 friend, will then `close' the trap. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +{ + (set) 2>&1 | + case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in + *ac_space=\ *) + sed -n \ + "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" + ;; + *) + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------- ## +## Output files. ## +## ------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + sed "/^$/d" confdefs.h | sort + echo + fi + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" + echo "$as_me: exit $exit_status" + } >&5 + rm -f core core.* *.core && + rm -rf conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status + ' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# 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 + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# 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 "$as_me:$LINENO: loading site script $ac_site_file" >&5 +echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { echo "$as_me:$LINENO: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . $cache_file;; + *) . ./$cache_file;; + esac + fi +else + { echo "$as_me:$LINENO: creating cache $cache_file" >&5 +echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in `(set) 2>&1 | + sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val="\$ac_cv_env_${ac_var}_value" + eval ac_new_val="\$ac_env_${ac_var}_value" + case $ac_old_set,$ac_new_set in + set,) + { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + + + + + + + + + + + + + + + + ac_config_headers="$ac_config_headers config.h" + + + +# Check whether --with-defshell or --without-defshell was given. +if test "${with_defshell+set}" = set; then + withval="$with_defshell" + case "${withval}" in +yes) { { echo "$as_me:$LINENO: error: bad value ${withval} given for bmake DEFSHELL" >&5 +echo "$as_me: error: bad value ${withval} given for bmake DEFSHELL" >&2;} + { (exit 1); exit 1; }; } ;; +no) ;; +*) case "/$with_defshell" in + */csh) DEFSHELL=0;; # kidding right? + */sh) DEFSHELL=1;; + */ksh) DEFSHELL=2;; + *) { { echo "$as_me:$LINENO: error: bad value ${withval} given for bmake DEFSHELL - only /bin/sh and /bin/ksh are valid" >&5 +echo "$as_me: error: bad value ${withval} given for bmake DEFSHELL - only /bin/sh and /bin/ksh are valid" >&2;} + { (exit 1); exit 1; }; } ;; + esac + case "$with_defshell" in + /bin/*) ;; + /*) { { echo "$as_me:$LINENO: error: bad value ${withval} - DEFSHELL must reside in /bin" >&5 +echo "$as_me: error: bad value ${withval} - DEFSHELL must reside in /bin" >&2;} + { (exit 1); exit 1; }; };; + esac + +cat >>confdefs.h <<_ACEOF +#define DEFSHELL ${DEFSHELL:-1} +_ACEOF + + ;; +esac +fi; +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +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 "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +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 $# != 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 + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CC" && break +done + + CC=$ac_ct_CC +fi + +fi + + +test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:$LINENO:" \ + "checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 + (eval $ac_compiler --version &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 + (eval $ac_compiler -v &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 + (eval $ac_compiler -V &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +echo "$as_me:$LINENO: checking for C compiler default output" >&5 +echo $ECHO_N "checking for C compiler default output... $ECHO_C" >&6 +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5 + (eval $ac_link_default) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Find the output, starting from the most likely. This scheme is +# not robust to junk in `.', hence go to wildcards (a.*) only as a last +# resort. + +# Be careful to initialize this variable, since it used to be cached. +# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile. +ac_cv_exeext= +# b.out is created by i960 compilers. +for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) + ;; + conftest.$ac_ext ) + # This is the source file. + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + # FIXME: I believe we export ac_cv_exeext for Libtool, + # but it would be cool to find out if it's true. Does anybody + # maintain Libtool? --akim. + export ac_cv_exeext + break;; + * ) + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: C compiler cannot create executables +See \`config.log' for more details." >&5 +echo "$as_me: error: C compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6 + +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether the C compiler works" >&5 +echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6 +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:$LINENO: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +rm -f a.out a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 +echo "$as_me:$LINENO: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6 + +echo "$as_me:$LINENO: checking for suffix of executables" >&5 +echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6 +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6 + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +echo "$as_me:$LINENO: checking for suffix of object files" >&5 +echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6 +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6 +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_compiler_gnu=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +CFLAGS="-g" +echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_prog_cc_g=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_g" >&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 "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 +echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_prog_cc_stdc=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +# Don't try gcc -ansi; that turns off useful extensions and +# breaks some systems' header files. +# AIX -qlanglvl=ansi +# Ultrix and OSF/1 -std1 +# HP-UX 10.20 and later -Ae +# HP-UX older versions -Aa -D_HPUX_SOURCE +# SVR4 -Xc -D__EXTENSIONS__ +for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_stdc=$ac_arg +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.$ac_objext +done +rm -f conftest.$ac_ext conftest.$ac_objext +CC=$ac_save_CC + +fi + +case "x$ac_cv_prog_cc_stdc" in + x|xno) + echo "$as_me:$LINENO: result: none needed" >&5 +echo "${ECHO_T}none needed" >&6 ;; + *) + echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 + CC="$CC $ac_cv_prog_cc_stdc" ;; +esac + +# Some people use a C++ compiler to compile C. Since we use `exit', +# in C++ we need to declare it. In case someone uses the same compiler +# for both compiling C and C++ we need to have the C++ compiler decide +# the declaration of exit, since it's the most demanding environment. +cat >conftest.$ac_ext <<_ACEOF +#ifndef __cplusplus + choke me +#endif +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + for ac_declaration in \ + ''\ + '#include ' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +continue +fi +rm -f conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.$ac_objext conftest.$ac_ext +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 +echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test "${ac_cv_prog_CPP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +echo "$as_me:$LINENO: result: $CPP" >&5 +echo "${ECHO_T}$CPP" >&6 +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&5 +echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +echo "$as_me:$LINENO: checking for egrep" >&5 +echo $ECHO_N "checking for egrep... $ECHO_C" >&6 +if test "${ac_cv_prog_egrep+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if echo a | (grep -E '(a|b)') >/dev/null 2>&1 + then ac_cv_prog_egrep='grep -E' + else ac_cv_prog_egrep='egrep' + fi +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5 +echo "${ECHO_T}$ac_cv_prog_egrep" >&6 + EGREP=$ac_cv_prog_egrep + + +if test $ac_cv_c_compiler_gnu = yes; then + echo "$as_me:$LINENO: checking whether $CC needs -traditional" >&5 +echo $ECHO_N "checking whether $CC needs -traditional... $ECHO_C" >&6 +if test "${ac_cv_prog_gcc_traditional+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_pattern="Autoconf.*'x'" + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +Autoconf TIOCGETP +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "$ac_pattern" >/dev/null 2>&1; then + ac_cv_prog_gcc_traditional=yes +else + ac_cv_prog_gcc_traditional=no +fi +rm -f conftest* + + + if test $ac_cv_prog_gcc_traditional = no; then + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +Autoconf TCGETA +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "$ac_pattern" >/dev/null 2>&1; then + ac_cv_prog_gcc_traditional=yes +fi +rm -f conftest* + + fi +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_gcc_traditional" >&5 +echo "${ECHO_T}$ac_cv_prog_gcc_traditional" >&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 + elif test -f $ac_dir/shtool; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5 +echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;} + { (exit 1); exit 1; }; } +fi +ac_config_guess="$SHELL $ac_aux_dir/config.guess" +ac_config_sub="$SHELL $ac_aux_dir/config.sub" +ac_configure="$SHELL $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 +# AmigaOS /C/install, which installs bootblocks on floppy discs +# 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 "$as_me:$LINENO: checking for a BSD-compatible install" >&5 +echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6 +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in + ./ | .// | /cC/* | \ + /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 + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + done + done + ;; +esac +done + + +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 "$as_me:$LINENO: result: $INSTALL" >&5 +echo "${ECHO_T}$INSTALL" >&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}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + + +echo "$as_me:$LINENO: checking for AIX" >&5 +echo $ECHO_N "checking for AIX... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef _AIX + yes +#endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "yes" >/dev/null 2>&1; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +cat >>confdefs.h <<\_ACEOF +#define _ALL_SOURCE 1 +_ACEOF + +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi +rm -f conftest* + + +echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_stdc=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + 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 <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + 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 <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#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); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -f core core.* *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6 +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. + + + + + + + + + +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_Header=no" +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +if test "${ac_cv_header_minix_config_h+set}" = set; then + echo "$as_me:$LINENO: checking for minix/config.h" >&5 +echo $ECHO_N "checking for minix/config.h... $ECHO_C" >&6 +if test "${ac_cv_header_minix_config_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: $ac_cv_header_minix_config_h" >&5 +echo "${ECHO_T}$ac_cv_header_minix_config_h" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking minix/config.h usability" >&5 +echo $ECHO_N "checking minix/config.h usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking minix/config.h presence" >&5 +echo $ECHO_N "checking minix/config.h presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc in + yes:no ) + { echo "$as_me:$LINENO: WARNING: minix/config.h: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: minix/config.h: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: minix/config.h: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: minix/config.h: proceeding with the preprocessor's result" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------ ## +## Report this to bug-autoconf@gnu.org. ## +## ------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; + no:yes ) + { echo "$as_me:$LINENO: WARNING: minix/config.h: present but cannot be compiled" >&5 +echo "$as_me: WARNING: minix/config.h: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: minix/config.h: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: minix/config.h: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: minix/config.h: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: minix/config.h: proceeding with the preprocessor's result" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------ ## +## Report this to bug-autoconf@gnu.org. ## +## ------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for minix/config.h" >&5 +echo $ECHO_N "checking for minix/config.h... $ECHO_C" >&6 +if test "${ac_cv_header_minix_config_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_header_minix_config_h=$ac_header_preproc +fi +echo "$as_me:$LINENO: result: $ac_cv_header_minix_config_h" >&5 +echo "${ECHO_T}$ac_cv_header_minix_config_h" >&6 + +fi +if test $ac_cv_header_minix_config_h = yes; then + MINIX=yes +else + MINIX= +fi + + +if test "$MINIX" = yes; then + +cat >>confdefs.h <<\_ACEOF +#define _POSIX_SOURCE 1 +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define _POSIX_1_SOURCE 2 +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define _MINIX 1 +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for library containing strerror" >&5 +echo $ECHO_N "checking for library containing strerror... $ECHO_C" >&6 +if test "${ac_cv_search_strerror+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search_strerror=no +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char strerror (); +int +main () +{ +strerror (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_strerror="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search_strerror" = no; then + for ac_lib in cposix; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char strerror (); +int +main () +{ +strerror (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_strerror="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_strerror" >&5 +echo "${ECHO_T}$ac_cv_search_strerror" >&6 +if test "$ac_cv_search_strerror" != no; then + test "$ac_cv_search_strerror" = "none required" || LIBS="$ac_cv_search_strerror $LIBS" + +fi + + + +echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_stdc=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + 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 <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + 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 <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#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); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -f core core.* *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6 +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for sys/wait.h that is POSIX.1 compatible" >&5 +echo $ECHO_N "checking for sys/wait.h that is POSIX.1 compatible... $ECHO_C" >&6 +if test "${ac_cv_header_sys_wait_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#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; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_sys_wait_h=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_sys_wait_h=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_header_sys_wait_h" >&5 +echo "${ECHO_T}$ac_cv_header_sys_wait_h" >&6 +if test $ac_cv_header_sys_wait_h = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SYS_WAIT_H 1 +_ACEOF + +fi + + + + + + +ac_header_dirent=no +for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do + as_ac_Header=`echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_hdr that defines DIR" >&5 +echo $ECHO_N "checking for $ac_hdr that defines DIR... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include <$ac_hdr> + +int +main () +{ +if ((DIR *) 0) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_Header=no" +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_hdr" | $as_tr_cpp` 1 +_ACEOF + +ac_header_dirent=$ac_hdr; break +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 "$as_me:$LINENO: checking for library containing opendir" >&5 +echo $ECHO_N "checking for library containing opendir... $ECHO_C" >&6 +if test "${ac_cv_search_opendir+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search_opendir=no +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char opendir (); +int +main () +{ +opendir (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_opendir="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search_opendir" = no; then + for ac_lib in dir; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char opendir (); +int +main () +{ +opendir (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_opendir="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_opendir" >&5 +echo "${ECHO_T}$ac_cv_search_opendir" >&6 +if test "$ac_cv_search_opendir" != no; then + test "$ac_cv_search_opendir" = "none required" || LIBS="$ac_cv_search_opendir $LIBS" + +fi + +else + echo "$as_me:$LINENO: checking for library containing opendir" >&5 +echo $ECHO_N "checking for library containing opendir... $ECHO_C" >&6 +if test "${ac_cv_search_opendir+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search_opendir=no +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char opendir (); +int +main () +{ +opendir (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_opendir="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search_opendir" = no; then + for ac_lib in x; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char opendir (); +int +main () +{ +opendir (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_opendir="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_opendir" >&5 +echo "${ECHO_T}$ac_cv_search_opendir" >&6 +if test "$ac_cv_search_opendir" != no; then + test "$ac_cv_search_opendir" = "none required" || LIBS="$ac_cv_search_opendir $LIBS" + +fi + +fi + + + + + + + + + + + + + +for ac_header in \ + ar.h \ + fcntl.h \ + paths.h \ + poll.h \ + ranlib.h \ + string.h \ + sys/select.h \ + sys/socket.h \ + sys/time.h \ + sys/uio.h \ + unistd.h \ + utime.h \ + +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc in + yes:no ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------ ## +## Report this to bug-autoconf@gnu.org. ## +## ------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; + no:yes ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------ ## +## Report this to bug-autoconf@gnu.org. ## +## ------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +if test "${ac_cv_header_sys_cdefs_h+set}" = set; then + echo "$as_me:$LINENO: checking for sys/cdefs.h" >&5 +echo $ECHO_N "checking for sys/cdefs.h... $ECHO_C" >&6 +if test "${ac_cv_header_sys_cdefs_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: $ac_cv_header_sys_cdefs_h" >&5 +echo "${ECHO_T}$ac_cv_header_sys_cdefs_h" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking sys/cdefs.h usability" >&5 +echo $ECHO_N "checking sys/cdefs.h usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking sys/cdefs.h presence" >&5 +echo $ECHO_N "checking sys/cdefs.h presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc in + yes:no ) + { echo "$as_me:$LINENO: WARNING: sys/cdefs.h: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: sys/cdefs.h: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: sys/cdefs.h: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: sys/cdefs.h: proceeding with the preprocessor's result" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------ ## +## Report this to bug-autoconf@gnu.org. ## +## ------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; + no:yes ) + { echo "$as_me:$LINENO: WARNING: sys/cdefs.h: present but cannot be compiled" >&5 +echo "$as_me: WARNING: sys/cdefs.h: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: sys/cdefs.h: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: sys/cdefs.h: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: sys/cdefs.h: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: sys/cdefs.h: proceeding with the preprocessor's result" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------ ## +## Report this to bug-autoconf@gnu.org. ## +## ------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for sys/cdefs.h" >&5 +echo $ECHO_N "checking for sys/cdefs.h... $ECHO_C" >&6 +if test "${ac_cv_header_sys_cdefs_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_header_sys_cdefs_h=$ac_header_preproc +fi +echo "$as_me:$LINENO: result: $ac_cv_header_sys_cdefs_h" >&5 +echo "${ECHO_T}$ac_cv_header_sys_cdefs_h" >&6 + +fi +if test $ac_cv_header_sys_cdefs_h = yes; then + echo $ECHO_N "checking whether sys/cdefs.h is compatible... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#ifdef __RCSID +yes +#endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "yes" >/dev/null 2>&1; then + echo yes >&6 +else + echo no >&6; CPPFLAGS="${CPPFLAGS} -I`cd ${srcdir}/missing && pwd` -DNEED_HOST_CDEFS_H" +fi +rm -f conftest* + +else + CPPFLAGS="${CPPFLAGS} -I`cd ${srcdir}/missing && pwd`" +fi + + + + +echo "$as_me:$LINENO: checking for __attribute__" >&5 +echo $ECHO_N "checking for __attribute__... $ECHO_C" >&6 +if test "${ac_cv___attribute__+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include + +int +main () +{ + +static void foo(void) __attribute__ ((noreturn)); + +static void +foo(void) +{ + exit(1); +} + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv___attribute__=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv___attribute__=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi + +if test "$ac_cv___attribute__" = "yes"; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE___ATTRIBUTE__ 1 +_ACEOF + +fi +echo "$as_me:$LINENO: result: $ac_cv___attribute__" >&5 +echo "${ECHO_T}$ac_cv___attribute__" >&6 + +echo "$as_me:$LINENO: checking whether byte ordering is bigendian" >&5 +echo $ECHO_N "checking whether byte ordering is bigendian... $ECHO_C" >&6 +if test "${ac_cv_c_bigendian+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # See if sys/param.h defines the BYTE_ORDER macro. +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include + +int +main () +{ +#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN + bogus endian macros +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + # It does; now see whether it defined to BIG_ENDIAN or not. +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include + +int +main () +{ +#if BYTE_ORDER != BIG_ENDIAN + not big endian +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_bigendian=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_c_bigendian=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +# It does not; compile a test program. +if test "$cross_compiling" = yes; then + # try to guess the endianness by grepping values into an object file + ac_cv_c_bigendian=unknown + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +short ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; +short ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; +void _ascii () { char *s = (char *) ascii_mm; s = (char *) ascii_ii; } +short ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; +short ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; +void _ebcdic () { char *s = (char *) ebcdic_mm; s = (char *) ebcdic_ii; } +int +main () +{ + _ascii (); _ebcdic (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + if grep BIGenDianSyS conftest.$ac_objext >/dev/null ; then + ac_cv_c_bigendian=yes +fi +if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then + if test "$ac_cv_c_bigendian" = unknown; then + ac_cv_c_bigendian=no + else + # finding both strings is unlikely to happen, but who knows? + ac_cv_c_bigendian=unknown + fi +fi +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.$ac_objext conftest.$ac_ext +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +int +main () +{ + /* Are we little or big endian? From Harbison&Steele. */ + union + { + long l; + char c[sizeof (long)]; + } u; + u.l = 1; + exit (u.c[sizeof (long) - 1] == 1); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_bigendian=no +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_c_bigendian=yes +fi +rm -f core core.* *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_c_bigendian" >&5 +echo "${ECHO_T}$ac_cv_c_bigendian" >&6 +case $ac_cv_c_bigendian in + yes) + +cat >>confdefs.h <<\_ACEOF +#define WORDS_BIGENDIAN 1 +_ACEOF + ;; + no) + ;; + *) + { { echo "$as_me:$LINENO: error: unknown endianness +presetting ac_cv_c_bigendian=no (or yes) will help" >&5 +echo "$as_me: error: unknown endianness +presetting ac_cv_c_bigendian=no (or yes) will help" >&2;} + { (exit 1); exit 1; }; } ;; +esac + +echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5 +echo $ECHO_N "checking for an ANSI C-conforming const... $ECHO_C" >&6 +if test "${ac_cv_c_const+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +/* FIXME: Include the comments suggested by Paul. */ +#ifndef __cplusplus + /* Ultrix mips cc rejects this. */ + typedef int charset[2]; + const charset x; + /* SunOS 4.1.1 cc rejects this. */ + char const *const *ccp; + char **p; + /* NEC SVR4.0.2 mips cc rejects this. */ + struct point {int x, y;}; + static struct point const zero = {0,0}; + /* AIX XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in + an arm of an if-expression whose if-part is not a constant + expression */ + const char *g = "string"; + ccp = &g + (g ? g-g : 0); + /* HPUX 7.0 cc rejects these. */ + ++ccp; + p = (char**) ccp; + ccp = (char const *const *) p; + { /* SCO 3.2v4 cc rejects this. */ + char *t; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; + } + { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; + } + { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; + } + { /* AIX XL C 1.02.0.0 rejects this saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; }; + struct s *b; b->j = 5; + } + { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; + } +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_const=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_c_const=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5 +echo "${ECHO_T}$ac_cv_c_const" >&6 +if test $ac_cv_c_const = no; then + +cat >>confdefs.h <<\_ACEOF +#define const +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for off_t" >&5 +echo $ECHO_N "checking for off_t... $ECHO_C" >&6 +if test "${ac_cv_type_off_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((off_t *) 0) + return 0; +if (sizeof (off_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_off_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_off_t=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_off_t" >&5 +echo "${ECHO_T}$ac_cv_type_off_t" >&6 +if test $ac_cv_type_off_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define off_t long +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for pid_t" >&5 +echo $ECHO_N "checking for pid_t... $ECHO_C" >&6 +if test "${ac_cv_type_pid_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((pid_t *) 0) + return 0; +if (sizeof (pid_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_pid_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_pid_t=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_pid_t" >&5 +echo "${ECHO_T}$ac_cv_type_pid_t" >&6 +if test $ac_cv_type_pid_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define pid_t int +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for size_t" >&5 +echo $ECHO_N "checking for size_t... $ECHO_C" >&6 +if test "${ac_cv_type_size_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((size_t *) 0) + return 0; +if (sizeof (size_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_size_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_size_t=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5 +echo "${ECHO_T}$ac_cv_type_size_t" >&6 +if test $ac_cv_type_size_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned +_ACEOF + +fi + +echo "$as_me:$LINENO: checking whether sys_siglist is declared" >&5 +echo $ECHO_N "checking whether sys_siglist is declared... $ECHO_C" >&6 +if test "${ac_cv_have_decl_sys_siglist+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +#ifndef sys_siglist + char *p = (char *) sys_siglist; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_have_decl_sys_siglist=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_have_decl_sys_siglist=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_have_decl_sys_siglist" >&5 +echo "${ECHO_T}$ac_cv_have_decl_sys_siglist" >&6 +if test $ac_cv_have_decl_sys_siglist = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_SYS_SIGLIST 1 +_ACEOF + + +else + cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_SYS_SIGLIST 0 +_ACEOF + + +fi + + + +echo "$as_me:$LINENO: checking whether time.h and sys/time.h may both be included" >&5 +echo $ECHO_N "checking whether time.h and sys/time.h may both be included... $ECHO_C" >&6 +if test "${ac_cv_header_time+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include + +int +main () +{ +if ((struct tm *) 0) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_time=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_time=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_header_time" >&5 +echo "${ECHO_T}$ac_cv_header_time" >&6 +if test $ac_cv_header_time = yes; then + +cat >>confdefs.h <<\_ACEOF +#define TIME_WITH_SYS_TIME 1 +_ACEOF + +fi + +echo "$as_me:$LINENO: checking whether struct tm is in sys/time.h or time.h" >&5 +echo $ECHO_N "checking whether struct tm is in sys/time.h or time.h... $ECHO_C" >&6 +if test "${ac_cv_struct_tm+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include + +int +main () +{ +struct tm *tp; tp->tm_sec; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_struct_tm=time.h +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_struct_tm=sys/time.h +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_struct_tm" >&5 +echo "${ECHO_T}$ac_cv_struct_tm" >&6 +if test $ac_cv_struct_tm = sys/time.h; then + +cat >>confdefs.h <<\_ACEOF +#define TM_IN_SYS_TIME 1 +_ACEOF + +fi + + +echo "$as_me:$LINENO: checking return type of signal handlers" >&5 +echo $ECHO_N "checking return type of signal handlers... $ECHO_C" >&6 +if test "${ac_cv_type_signal+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#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; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_signal=void +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_signal=int +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_signal" >&5 +echo "${ECHO_T}$ac_cv_type_signal" >&6 + +cat >>confdefs.h <<_ACEOF +#define RETSIGTYPE $ac_cv_type_signal +_ACEOF + + + + +for ac_header in unistd.h vfork.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc in + yes:no ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------ ## +## Report this to bug-autoconf@gnu.org. ## +## ------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; + no:yes ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------ ## +## Report this to bug-autoconf@gnu.org. ## +## ------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + +for ac_func in fork vfork +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ +#ifdef __STDC__ +# include +#else +# include +#endif +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +if test "x$ac_cv_func_fork" = xyes; then + echo "$as_me:$LINENO: checking for working fork" >&5 +echo $ECHO_N "checking for working fork... $ECHO_C" >&6 +if test "${ac_cv_func_fork_works+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_fork_works=cross +else + cat >conftest.$ac_ext <<_ACEOF +/* By Ruediger Kuhlmann. */ + #include + #if HAVE_UNISTD_H + # include + #endif + /* Some systems only have a dummy stub for fork() */ + int main () + { + if (fork() < 0) + exit (1); + exit (0); + } +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_fork_works=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_fork_works=no +fi +rm -f core core.* *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_fork_works" >&5 +echo "${ECHO_T}$ac_cv_func_fork_works" >&6 + +else + ac_cv_func_fork_works=$ac_cv_func_fork +fi +if test "x$ac_cv_func_fork_works" = xcross; then + case $host in + *-*-amigaos* | *-*-msdosdjgpp*) + # Override, as these systems have only a dummy fork() stub + ac_cv_func_fork_works=no + ;; + *) + ac_cv_func_fork_works=yes + ;; + esac + { echo "$as_me:$LINENO: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5 +echo "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;} +fi +ac_cv_func_vfork_works=$ac_cv_func_vfork +if test "x$ac_cv_func_vfork" = xyes; then + echo "$as_me:$LINENO: checking for working vfork" >&5 +echo $ECHO_N "checking for working vfork... $ECHO_C" >&6 +if test "${ac_cv_func_vfork_works+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_vfork_works=cross +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Thanks to Paul Eggert for this test. */ +#include +#include +#include +#include +#include +#if HAVE_UNISTD_H +# include +#endif +#if 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 void +#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); + } + } +} + +int +main () +{ + pid_t parent = getpid (); + pid_t child; + + sparc_address_test (0); + + 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 + ); + } +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_vfork_works=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_vfork_works=no +fi +rm -f core core.* *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_vfork_works" >&5 +echo "${ECHO_T}$ac_cv_func_vfork_works" >&6 + +fi; +if test "x$ac_cv_func_fork_works" = xcross; then + ac_cv_func_vfork_works=ac_cv_func_vfork + { echo "$as_me:$LINENO: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5 +echo "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;} +fi + +if test "x$ac_cv_func_vfork_works" = xyes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_WORKING_VFORK 1 +_ACEOF + +else + +cat >>confdefs.h <<\_ACEOF +#define vfork fork +_ACEOF + +fi +if test "x$ac_cv_func_fork_works" = xyes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_WORKING_FORK 1 +_ACEOF + +fi + + +for ac_func in vprintf +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ +#ifdef __STDC__ +# include +#else +# include +#endif +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +echo "$as_me:$LINENO: checking for _doprnt" >&5 +echo $ECHO_N "checking for _doprnt... $ECHO_C" >&6 +if test "${ac_cv_func__doprnt+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char _doprnt (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ +#ifdef __STDC__ +# include +#else +# include +#endif +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char _doprnt (); +/* 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 +char (*f) () = _doprnt; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != _doprnt; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func__doprnt=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_func__doprnt=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func__doprnt" >&5 +echo "${ECHO_T}$ac_cv_func__doprnt" >&6 +if test $ac_cv_func__doprnt = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_DOPRNT 1 +_ACEOF + +fi + +fi +done + + +echo "$as_me:$LINENO: checking for wait3 that fills in rusage" >&5 +echo $ECHO_N "checking for wait3 that fills in rusage... $ECHO_C" >&6 +if test "${ac_cv_func_wait3_rusage+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_wait3_rusage=no +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include +/* HP-UX has wait3 but does not fill in rusage at all. */ +int +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); + break; + case -1: /* What can we do? */ + _exit(0); + break; + default: /* Parent. */ + wait3(&i, 0, &r); + /* Avoid "text file busy" from rm on fast HP-UX machines. */ + sleep(2); + 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); + } +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_wait3_rusage=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_wait3_rusage=no +fi +rm -f core core.* *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_wait3_rusage" >&5 +echo "${ECHO_T}$ac_cv_func_wait3_rusage" >&6 +if test $ac_cv_func_wait3_rusage = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_WAIT3 1 +_ACEOF + +fi + + + + + + + + + + + + + + + + + + + + + + + +for ac_func in \ + asprintf \ + getcwd \ + getenv \ + getopt \ + getwd \ + killpg \ + putenv \ + select \ + setenv \ + sigaction \ + sigvec \ + snprintf \ + strdup \ + strerror \ + strftime \ + strtod \ + strtol \ + vasprintf \ + vsnprintf \ + wait3 \ + wait4 \ + waitpid \ + +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ +#ifdef __STDC__ +# include +#else +# include +#endif +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +for ac_func in getenv +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ +#ifdef __STDC__ +# include +#else +# include +#endif +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + LIBOBJS="$LIBOBJS $ac_func.$ac_objext" +fi +done + + +echo "$as_me:$LINENO: checking whether stat file-mode macros are broken" >&5 +echo $ECHO_N "checking whether stat file-mode macros are broken... $ECHO_C" >&6 +if test "${ac_cv_header_stat_broken+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#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 + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "You lose" >/dev/null 2>&1; then + ac_cv_header_stat_broken=yes +else + ac_cv_header_stat_broken=no +fi +rm -f conftest* + +fi +echo "$as_me:$LINENO: result: $ac_cv_header_stat_broken" >&5 +echo "${ECHO_T}$ac_cv_header_stat_broken" >&6 +if test $ac_cv_header_stat_broken = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STAT_MACROS_BROKEN 1 +_ACEOF + +fi + + +echo "$as_me:$LINENO: checking for struct stat.st_rdev" >&5 +echo $ECHO_N "checking for struct stat.st_rdev... $ECHO_C" >&6 +if test "${ac_cv_member_struct_stat_st_rdev+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static struct stat ac_aggr; +if (ac_aggr.st_rdev) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_member_struct_stat_st_rdev=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static struct stat ac_aggr; +if (sizeof ac_aggr.st_rdev) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_member_struct_stat_st_rdev=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_member_struct_stat_st_rdev=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_member_struct_stat_st_rdev" >&5 +echo "${ECHO_T}$ac_cv_member_struct_stat_st_rdev" >&6 +if test $ac_cv_member_struct_stat_st_rdev = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_STAT_ST_RDEV 1 +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define HAVE_ST_RDEV 1 +_ACEOF + +fi + + +echo $ECHO_N "checking if diff -u works... $ECHO_C" >&6 +if diff -u /dev/null /dev/null > /dev/null 2>&1; then + diff_u=-u + echo yes >&6 +else + diff_u= + echo no >&6 +fi + +if egrep "__EXTENSIONS__" /usr/include/signal.h >/dev/null 2>&1; then + CPPFLAGS="$CPPFLAGS -D__EXTENSIONS__" +fi + +echo "checking for MACHINE & MACHINE_ARCH..." >&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 "$as_me:$LINENO: error: bad value ${withval} given for bmake MACHINE" >&5 +echo "$as_me: error: bad value ${withval} given for bmake MACHINE" >&2;} + { (exit 1); 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; +force_machine_arch= + +# Check whether --with-force_machine_arch or --without-force_machine_arch was given. +if test "${with_force_machine_arch+set}" = set; then + withval="$with_force_machine_arch" + case "${withval}" in +yes) force_machine_arch=FORCE_;; +no) ;; +*) force_machine_arch=FORCE_; machine_arch=$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 "$as_me:$LINENO: error: bad value ${withval} given for bmake MACHINE_ARCH" >&5 +echo "$as_me: error: bad value ${withval} given for bmake MACHINE_ARCH" >&2;} + { (exit 1); exit 1; }; } ;; +no) ;; +*) machine_arch=$with_machine_arch;; +esac +fi; +echo "Using: ${force_machine}MACHINE=$machine, MACHINE_ARCH=$machine_arch" 1>&6 +default_sys_path=\${prefix}/share/mk + +# 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 "$as_me:$LINENO: error: bad value ${withval} given for bmake _PATH_DEFSYSPATH" >&5 +echo "$as_me: error: bad value ${withval} given for bmake _PATH_DEFSYSPATH" >&2;} + { (exit 1); exit 1; }; } ;; +no) ;; +*) default_sys_path="$with_default_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 "$as_me:$LINENO: error: bad value ${withval} given for bmake _PATH_OBJDIRPREFIX" >&5 +echo "$as_me: error: bad value ${withval} given for bmake _PATH_OBJDIRPREFIX" >&2;} + { (exit 1); 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 "$as_me:$LINENO: error: bad value ${enableval} given for pwd-override option" >&5 +echo "$as_me: error: bad value ${enableval} given for pwd-override option" >&2;} + { (exit 1); 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 "$as_me:$LINENO: error: bad value ${enableval} given for check-make-chdir option" >&5 +echo "$as_me: error: bad value ${enableval} given for check-make-chdir option" >&2;} + { (exit 1); exit 1; }; } ;; +esac +fi; + +# Check whether --with-mksrc or --without-mksrc was given. +if test "${with_mksrc+set}" = set; then + withval="$with_mksrc" + case "${withval}" in +""|yes|no) ;; +*) test -s $withval/install-mk && mksrc=$withval || +{ { echo "$as_me:$LINENO: error: bad value ${withval} given for mksrc cannot find install-mk" >&5 +echo "$as_me: error: bad value ${withval} given for mksrc cannot find install-mk" >&2;} + { (exit 1); exit 1; }; } +;; +esac + +fi; +for mksrc in $mksrc $srcdir/mk $srcdir/../mk mk +do + test -s $mksrc/install-mk && break +done +mksrc=`echo $mksrc | sed "s,$srcdir,\\\${srcdir},"` +echo "Using: MKSRC=$mksrc" 1>&6 + + + + + + + + + + ac_config_files="$ac_config_files Makefile makefile.boot lst.lib/makefile.boot unit-tests/Makefile" +cat >confcache <<\_ACEOF +# 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, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# 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 \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} | + sed ' + t clear + : clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + : end' >>confcache +if diff $cache_file confcache >/dev/null 2>&1; then :; else + if test -w $cache_file; then + test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" + cat confcache >$cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/; +s/:*\${srcdir}:*/:/; +s/:*@srcdir@:*/:/; +s/^\([^=]*=[ ]*\):*/\1/; +s/:*$//; +s/^[^=]*=[ ]*$//; +}' +fi + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_i=`echo "$ac_i" | + sed 's/\$U\././;s/\.o$//;s/\.obj$//'` + # 2. Add them. + ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi + +# Support unset when possible. +if (FOO=FOO; unset FOO) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -n "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5 +echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;} + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5 +echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;} + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="sed y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="sed y%*+%pp%;s%[^_$as_cr_alnum]%_%g" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + +exec 6>&1 + +# Open the log real soon, to keep \$[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. Logging --version etc. is OK. +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX +} >&5 +cat >&5 <<_CSEOF + +This file was extended by $as_me, which was +generated by GNU Autoconf 2.57. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +_CSEOF +echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5 +echo >&5 +_ACEOF + +# Files that config.status was made for. +if test -n "$ac_config_files"; then + echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_headers"; then + echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_links"; then + echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_commands"; then + echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS +fi + +cat >>$CONFIG_STATUS <<\_ACEOF + +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Report bugs to ." +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +ac_cs_version="\\ +config.status +configured by $0, generated by GNU Autoconf 2.57, + with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" + +Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." +srcdir=$srcdir +INSTALL="$INSTALL" +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "x$1" : 'x\([^=]*\)='` + ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` + ac_shift=: + ;; + -*) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + *) # This is not an option, so the user has probably given explicit + # arguments. + ac_option=$1 + ac_need_defaults=false;; + esac + + case $ac_option in + # Handling of the options. +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --vers* | -V ) + echo "$ac_cs_version"; exit 0 ;; + --he | --h) + # Conflict between --help and --header + { { echo "$as_me:$LINENO: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + echo "$ac_cs_usage"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + CONFIG_FILES="$CONFIG_FILES $ac_optarg" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" + ac_need_defaults=false;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +if \$ac_cs_recheck; then + echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 + exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion +fi + +_ACEOF + + + + + +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_config_target in $ac_config_targets +do + case "$ac_config_target" in + # Handling of arguments. + "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "makefile.boot" ) CONFIG_FILES="$CONFIG_FILES makefile.boot" ;; + "lst.lib/makefile.boot" ) CONFIG_FILES="$CONFIG_FILES lst.lib/makefile.boot" ;; + "unit-tests/Makefile" ) CONFIG_FILES="$CONFIG_FILES unit-tests/Makefile" ;; + "config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason to put it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Create a temporary directory, and hook for its removal unless debugging. +$debug || +{ + trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} + +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./confstat$$-$RANDOM + (umask 077 && mkdir $tmp) +} || +{ + echo "$me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF + +# +# CONFIG_FILES section. +# + +# No need to generate the scripts if there are no CONFIG_FILES. +# This happens for instance when ./config.status config.h +if test -n "\$CONFIG_FILES"; then + # Protect against being on the right side of a sed subst in config.status. + sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g; + s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF +s,@SHELL@,$SHELL,;t t +s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t +s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t +s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t +s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t +s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t +s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t +s,@exec_prefix@,$exec_prefix,;t t +s,@prefix@,$prefix,;t t +s,@program_transform_name@,$program_transform_name,;t t +s,@bindir@,$bindir,;t t +s,@sbindir@,$sbindir,;t t +s,@libexecdir@,$libexecdir,;t t +s,@datadir@,$datadir,;t t +s,@sysconfdir@,$sysconfdir,;t t +s,@sharedstatedir@,$sharedstatedir,;t t +s,@localstatedir@,$localstatedir,;t t +s,@libdir@,$libdir,;t t +s,@includedir@,$includedir,;t t +s,@oldincludedir@,$oldincludedir,;t t +s,@infodir@,$infodir,;t t +s,@mandir@,$mandir,;t t +s,@build_alias@,$build_alias,;t t +s,@host_alias@,$host_alias,;t t +s,@target_alias@,$target_alias,;t t +s,@DEFS@,$DEFS,;t t +s,@ECHO_C@,$ECHO_C,;t t +s,@ECHO_N@,$ECHO_N,;t t +s,@ECHO_T@,$ECHO_T,;t t +s,@LIBS@,$LIBS,;t t +s,@CC@,$CC,;t t +s,@CFLAGS@,$CFLAGS,;t t +s,@LDFLAGS@,$LDFLAGS,;t t +s,@CPPFLAGS@,$CPPFLAGS,;t t +s,@ac_ct_CC@,$ac_ct_CC,;t t +s,@EXEEXT@,$EXEEXT,;t t +s,@OBJEXT@,$OBJEXT,;t t +s,@CPP@,$CPP,;t t +s,@EGREP@,$EGREP,;t t +s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t +s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t +s,@INSTALL_DATA@,$INSTALL_DATA,;t t +s,@ac_exe_suffix@,$ac_exe_suffix,;t t +s,@LIBOBJS@,$LIBOBJS,;t t +s,@machine@,$machine,;t t +s,@force_machine@,$force_machine,;t t +s,@machine_arch@,$machine_arch,;t t +s,@mksrc@,$mksrc,;t t +s,@default_sys_path@,$default_sys_path,;t t +s,@prefix_sys_path@,$prefix_sys_path,;t t +s,@INSTALL@,$INSTALL,;t t +s,@GCC@,$GCC,;t t +s,@diff_u@,$diff_u,;t t +s,@LTLIBOBJS@,$LTLIBOBJS,;t t +CEOF + +_ACEOF + + cat >>$CONFIG_STATUS <<\_ACEOF + # 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_lines=48 + ac_sed_frag=1 # Number of current file. + ac_beg=1 # First line for current file. + ac_end=$ac_max_sed_lines # 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" $tmp/subs.sed >$tmp/subs.frag + else + sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + fi + if test ! -s $tmp/subs.frag; then + ac_more_lines=false + else + # The purpose of the label and of the branching condition is to + # speed up the sed processing (if there are no `@' at all, there + # is no need to browse any of the substitutions). + # These are the two extra sed commands mentioned above. + (echo ':t + /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" + else + ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" + fi + ac_sed_frag=`expr $ac_sed_frag + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_lines` + fi + done + if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat + fi +fi # test -n "$CONFIG_FILES" + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac +# Don't blindly perform a `cd "$ac_dir"/$ac_foo && pwd` since $ac_foo can be +# absolute. +ac_abs_builddir=`cd "$ac_dir" && cd $ac_builddir && pwd` +ac_abs_top_builddir=`cd "$ac_dir" && cd ${ac_top_builddir}. && pwd` +ac_abs_srcdir=`cd "$ac_dir" && cd $ac_srcdir && pwd` +ac_abs_top_srcdir=`cd "$ac_dir" && cd $ac_top_srcdir && pwd` + + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_builddir$INSTALL ;; + esac + + if test x"$ac_file" != x-; then + { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + rm -f "$ac_file" + fi + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + if test x"$ac_file" = x-; then + configure_input= + else + configure_input="$ac_file. " + fi + configure_input=$configure_input"Generated from `echo $ac_file_in | + sed 's,.*/,,'` by configure." + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo $f;; + *) # Relative + if test -f "$f"; then + # Build tree + echo $f + elif test -f "$srcdir/$f"; then + # Source tree + echo $srcdir/$f + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF + sed "$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s,@configure_input@,$configure_input,;t t +s,@srcdir@,$ac_srcdir,;t t +s,@abs_srcdir@,$ac_abs_srcdir,;t t +s,@top_srcdir@,$ac_top_srcdir,;t t +s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t +s,@builddir@,$ac_builddir,;t t +s,@abs_builddir@,$ac_abs_builddir,;t t +s,@top_builddir@,$ac_top_builddir,;t t +s,@abs_top_builddir@,$ac_abs_top_builddir,;t t +s,@INSTALL@,$ac_INSTALL,;t t +" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out + rm -f $tmp/stdin + if test x"$ac_file" != x-; then + mv $tmp/out $ac_file + else + cat $tmp/out + rm -f $tmp/out + fi + +done +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + +# +# CONFIG_HEADER section. +# + +# 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=' ' +ac_dD=',;t' +# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_uA='s,^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='$,\1#\2define\3' +ac_uC=' ' +ac_uD=',;t' + +for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo $f;; + *) # Relative + if test -f "$f"; then + # Build tree + echo $f + elif test -f "$srcdir/$f"; then + # Source tree + echo $srcdir/$f + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } + # Remove the trailing spaces. + sed 's/[ ]*$//' $ac_file_inputs >$tmp/in + +_ACEOF + +# Transform confdefs.h into two sed scripts, `conftest.defines' and +# `conftest.undefs', that substitutes the proper values into +# config.h.in to produce config.h. The first handles `#define' +# templates, and the second `#undef' templates. +# 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.defines conftest.undefs +# Using a here document instead of a string reduces the quoting nightmare. +# Putting comments in sed scripts is not portable. +# +# `end' is used to avoid that the second main sed command (meant for +# 0-ary CPP macros) applies to n-ary macro definitions. +# See the Autoconf documentation for `clear'. +cat >confdef2sed.sed <<\_ACEOF +s/[\\&,]/\\&/g +s,[\\$`],\\&,g +t clear +: clear +s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*\)\(([^)]*)\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp +t end +s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp +: end +_ACEOF +# If some macros were called several times there might be several times +# the same #defines, which is useless. Nevertheless, we may not want to +# sort them, since we want the *last* AC-DEFINE to be honored. +uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines +sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs +rm -f confdef2sed.sed + +# 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.undefs <<\_ACEOF +s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */, +_ACEOF + +# Break up conftest.defines because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS +echo ' if grep "^[ ]*#[ ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS +echo ' # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS +echo ' :' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.defines >/dev/null +do + # Write a limited-size here document to $tmp/defines.sed. + echo ' cat >$tmp/defines.sed <>$CONFIG_STATUS + # Speed up: don't consider the non `#define' lines. + echo '/^[ ]*#[ ]*define/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/defines.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail + rm -f conftest.defines + mv conftest.tail conftest.defines +done +rm -f conftest.defines +echo ' fi # grep' >>$CONFIG_STATUS +echo >>$CONFIG_STATUS + +# Break up conftest.undefs because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #undef templates' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.undefs >/dev/null +do + # Write a limited-size here document to $tmp/undefs.sed. + echo ' cat >$tmp/undefs.sed <>$CONFIG_STATUS + # Speed up: don't consider the non `#undef' + echo '/^[ ]*#[ ]*undef/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/undefs.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail + rm -f conftest.undefs + mv conftest.tail conftest.undefs +done +rm -f conftest.undefs + +cat >>$CONFIG_STATUS <<\_ACEOF + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + if test x"$ac_file" = x-; then + echo "/* Generated by configure. */" >$tmp/config.h + else + echo "/* $ac_file. Generated by configure. */" >$tmp/config.h + fi + cat $tmp/in >>$tmp/config.h + rm -f $tmp/in + if test x"$ac_file" != x-; then + if diff $ac_file $tmp/config.h >/dev/null 2>&1; then + { echo "$as_me:$LINENO: $ac_file is unchanged" >&5 +echo "$as_me: $ac_file is unchanged" >&6;} + else + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + rm -f $ac_file + mv $tmp/config.h $ac_file + fi + else + cat $tmp/config.h + rm -f $tmp/config.h + fi +done +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi + + +cat <&6 +AC_EGREP_CPP(yes, +[#include +#ifdef __RCSID +yes +#endif +], +echo yes >&6, +echo no >&6; CPPFLAGS="${CPPFLAGS} -I`cd ${srcdir}/missing && pwd` -DNEED_HOST_CDEFS_H"), +CPPFLAGS="${CPPFLAGS} -I`cd ${srcdir}/missing && pwd`") + +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 +dnl Keep this list sorted +AC_CHECK_FUNCS( \ + asprintf \ + getcwd \ + getenv \ + getopt \ + getwd \ + killpg \ + putenv \ + select \ + setenv \ + sigaction \ + sigvec \ + snprintf \ + strdup \ + strerror \ + strftime \ + strtod \ + strtol \ + vasprintf \ + vsnprintf \ + wait3 \ + wait4 \ + waitpid \ + ) +dnl AC_REPLACE_FUNCS(setenv getenv) +AC_REPLACE_FUNCS(getenv) +dnl +dnl Structures +dnl +AC_HEADER_STAT +AC_STRUCT_ST_RDEV +dnl +dnl we want this for unit-tests/Makefile +echo $ECHO_N "checking if diff -u works... $ECHO_C" >&6 +if diff -u /dev/null /dev/null > /dev/null 2>&1; then + diff_u=-u + echo yes >&6 +else + diff_u= + echo no >&6 +fi +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 "checking for MACHINE & MACHINE_ARCH..." >&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 +force_machine_arch= +AC_ARG_WITH(force_machine_arch, +[ --with-force-machine-arch=MACHINE set FORCE_MACHINE_ARCH], +[case "${withval}" in +yes) force_machine_arch=FORCE_;; +no) ;; +*) force_machine_arch=FORCE_; machine_arch=$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 +default_sys_path=\${prefix}/share/mk +AC_ARG_WITH(default-sys-path, +[ --with-default-sys-path=PATH:DIR:LIST use an explicit _PATH_DEFSYSPATH + MAKESYSPATH is a ':' separated list of directories + that bmake will search for system .mk files. + _PATH_DEFSYSPATH is its default value.], +[case "${withval}" in +yes) AC_MSG_ERROR(bad value ${withval} given for bmake _PATH_DEFSYSPATH) ;; +no) ;; +*) default_sys_path="$with_default_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]) +dnl +dnl On non-BSD systems, bootstrap won't work without mk +dnl +AC_ARG_WITH(mksrc, +[ --with-mksrc=PATH tell makefile.boot where to find mk src], +[case "${withval}" in +""|yes|no) ;; +*) test -s $withval/install-mk && mksrc=$withval || +AC_MSG_ERROR(bad value ${withval} given for mksrc cannot find install-mk) +;; +esac +]) +dnl +dnl Now make sure we have a value +dnl +for mksrc in $mksrc $srcdir/mk $srcdir/../mk mk +do + test -s $mksrc/install-mk && break +done +mksrc=`echo $mksrc | sed "s,$srcdir,\\\${srcdir},"` +echo "Using: MKSRC=$mksrc" 1>&6 +dnl +AC_SUBST(machine) +AC_SUBST(force_machine) +AC_SUBST(machine_arch) +AC_SUBST(mksrc) +AC_SUBST(default_sys_path) +AC_SUBST(INSTALL) +AC_SUBST(GCC) +AC_SUBST(diff_u) +AC_OUTPUT(Makefile makefile.boot lst.lib/makefile.boot unit-tests/Makefile) + +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 2005/12/02 00:02:59 sjg Exp $"); +#endif +#endif /* not lint */ +#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_InitCur Set the cur Path. + * + * Dir_InitDot Set the dot Path. + * + * Dir_End Cleanup the module. + * + * Dir_SetPATH Set ${.PATH} to reflect state of dirSearchPath. + * + * 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_FindHereOrAbove Search for a path in the current directory and + * then all the directories above it in turn until + * the path is found or we reach the root ("/"). + * + * 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(ClientData, ClientData); +static int DirMatchFiles(const char *, Path *, Lst); +static void DirExpandCurly(const char *, const char *, Lst, Lst); +static void DirExpandInt(const char *, Lst, Lst); +static int DirPrintWord(ClientData, ClientData); +static int DirPrintDir(ClientData, ClientData); +static char *DirLookup(Path *, const char *, const char *, Boolean); +static char *DirLookupSubdir(Path *, const char *); +static char *DirFindDot(Boolean, const char *, const char *); +static char *DirLookupAbs(Path *, const char *, const char *); + +/*- + *----------------------------------------------------------------------- + * Dir_Init -- + * initialize things for this module + * + * Results: + * none + * + * Side Effects: + * some directories may be opened. + *----------------------------------------------------------------------- + */ +void +Dir_Init(const char *cdname) +{ + dirSearchPath = Lst_Init(FALSE); + openDirectories = Lst_Init(FALSE); + Hash_InitTable(&mtimes, 0); + + Dir_InitCur(cdname); + + dotLast = emalloc(sizeof(Path)); + dotLast->refCount = 1; + dotLast->hits = 0; + dotLast->name = estrdup(".DOTLAST"); + Hash_InitTable(&dotLast->files, -1); +} + +/* + * Called by Dir_Init() and whenever .CURDIR is assigned to. + */ +void +Dir_InitCur(const char *cdname) +{ + Path *p; + + if (cdname != NULL) { + /* + * Our build directory is not the same as our source directory. + * Keep this one around too. + */ + if ((p = Dir_AddDir(NULL, cdname))) { + p->refCount += 1; + if (cur && cur != p) { + /* + * We've been here before, cleanup. + */ + cur->refCount -= 1; + Dir_Destroy((ClientData) cur); + } + cur = p; + } + } +} + +/*- + *----------------------------------------------------------------------- + * Dir_InitDot -- + * (re)initialize "dot" (current/object directory) path hash + * + * Results: + * none + * + * Side Effects: + * some directories may be opened. + *----------------------------------------------------------------------- + */ +void +Dir_InitDot(void) +{ + if (dot != NULL) { + LstNode ln; + + /* Remove old entry from openDirectories, but do not destroy. */ + ln = Lst_Member(openDirectories, (ClientData)dot); + (void)Lst_Remove(openDirectories, ln); + } + + 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; + Dir_SetPATH(); /* initialize */ +} + +/*- + *----------------------------------------------------------------------- + * Dir_End -- + * cleanup things for this module + * + * Results: + * none + * + * Side Effects: + * none + *----------------------------------------------------------------------- + */ +void +Dir_End(void) +{ +#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 +} + +/* + * We want ${.PATH} to indicate the order in which we will actually + * search, so we rebuild it after any .PATH: target. + * This is the simplest way to deal with the effect of .DOTLAST. + */ +void +Dir_SetPATH(void) +{ + LstNode ln; /* a list element */ + Path *p; + Boolean hasLastDot = FALSE; /* true we should search dot last */ + + Var_Delete(".PATH", VAR_GLOBAL); + + if (Lst_Open(dirSearchPath) == SUCCESS) { + if ((ln = Lst_First(dirSearchPath)) != NILLNODE) { + p = (Path *)Lst_Datum(ln); + if (p == dotLast) { + hasLastDot = TRUE; + Var_Append(".PATH", dotLast->name, VAR_GLOBAL); + } + } + + if (!hasLastDot) { + if (dot) + Var_Append(".PATH", dot->name, VAR_GLOBAL); + if (cur) + Var_Append(".PATH", cur->name, VAR_GLOBAL); + } + + while ((ln = Lst_Next(dirSearchPath)) != NILLNODE) { + p = (Path *)Lst_Datum(ln); + if (p == dotLast) + continue; + if (p == dot && hasLastDot) + continue; + Var_Append(".PATH", p->name, VAR_GLOBAL); + } + + if (hasLastDot) { + if (dot) + Var_Append(".PATH", dot->name, VAR_GLOBAL); + if (cur) + Var_Append(".PATH", cur->name, VAR_GLOBAL); + } + Lst_Close(dirSearchPath); + } +} + +/*- + *----------------------------------------------------------------------- + * 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. + * + * Input: + * p Current name + * dname Desired name + * + * Results: + * 0 if it is the same. Non-zero otherwise + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +static int +DirFindName(ClientData p, ClientData dname) +{ + 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! + * + * Input: + * name name to check + * + * Results: + * returns TRUE if the word should be expanded, FALSE otherwise + * + * Side Effects: + * none + *----------------------------------------------------------------------- + */ +Boolean +Dir_HasWildcards(char *name) +{ + 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. + * + * Input: + * pattern Pattern to look for + * p Directory to search + * expansion Place to store the results + * + * 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(const char *pattern, Path *p, Lst expansions) +{ + 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 != 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. + * + * Input: + * word Entire word to expand + * brace First curly brace in it + * path Search path to use + * expansions Place to store the expansions + * + * Results: + * None. + * + * Side Effects: + * The given list is filled with the expansions... + * + *----------------------------------------------------------------------- + */ +static void +DirExpandCurly(const char *word, const char *brace, Lst path, Lst expansions) +{ + const char *end; /* Character after the closing brace */ + const char *cp; /* Current position in brace clause */ + const 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... + * + * Input: + * word Word to expand + * path Path on which to look + * expansions Place to store the result + * + * Results: + * None. + * + * Side Effects: + * Things are added to the expansions list. + * + *----------------------------------------------------------------------- + */ +static void +DirExpandInt(const char *word, Lst path, Lst expansions) +{ + 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(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. + * + * Input: + * word the word to expand + * path the list of directories in which to find the + * resulting files + * expansions the list on which to place the results + * + * 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(const char *word, Lst path, Lst expansions) +{ + const 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]; + ((char *)UNCONST(cp))[1] = '\0'; + dirpath = Dir_FindFile(word, path); + ((char *)UNCONST(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 != 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 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: + * None. + *----------------------------------------------------------------------- + */ +static char * +DirLookup(Path *p, const char *name __unused, const char *cp, + Boolean hasSlash __unused) +{ + char *file; /* the current filename to check */ + + if (DEBUG(DIR)) { + printf(" %s ...\n", p->name); + } + + if (Hash_FindEntry(&p->files, cp) == NULL) + 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; +} + + +/*- + *----------------------------------------------------------------------- + * 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(Path *p, const 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 ...\n", file); + } + + if (stat(file, &stb) == 0) { + /* + * 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, NULL); + Hash_SetValue(entry, (long)stb.st_mtime); + nearmisses += 1; + return (file); + } + free(file); + return NULL; +} + +/*- + *----------------------------------------------------------------------- + * DirLookupAbs -- + * 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 * +DirLookupAbs(Path *p, const char *name, const char *cp) +{ + char *p1; /* pointer into p->name */ + const char *p2; /* pointer into name */ + + if (DEBUG(DIR)) { + printf(" %s ...\n", p->name); + } + + /* + * If the file has a leading path component and that component + * exactly matches the entire name of the current search + * directory, we can attempt another cache lookup. And if we don't + * have a hit, we can safely assume the file does not exist at all. + */ + for (p1 = p->name, p2 = name; *p1 && *p1 == *p2; p1++, p2++) { + continue; + } + if (*p1 != '\0' || p2 != cp - 1) { + return NULL; + } + + if (Hash_FindEntry(&p->files, cp) == NULL) { + if (DEBUG(DIR)) { + printf(" must be here but isn't -- returning\n"); + } + /* Return empty string: terminates search */ + return estrdup(""); + } + + p->hits += 1; + hits += 1; + if (DEBUG(DIR)) { + printf(" returning %s\n", name); + } + return (estrdup(name)); +} + +/*- + *----------------------------------------------------------------------- + * 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(Boolean hasSlash __unused, const char *name, const char *cp) +{ + + if (Hash_FindEntry(&dot->files, cp) != NULL) { + if (DEBUG(DIR)) { + printf(" in '.'\n"); + } + hits += 1; + dot->hits += 1; + return (estrdup(name)); + } + if (cur && + Hash_FindEntry(&cur->files, cp) != NULL) { + if (DEBUG(DIR)) { + printf(" in ${.CURDIR} = %s\n", cur->name); + } + hits += 1; + cur->hits += 1; + return str_concat(cur->name, cp, STR_ADDSLASH); + } + + return NULL; +} + +/*- + *----------------------------------------------------------------------- + * Dir_FindFile -- + * Find the file with the given name along the given search path. + * + * Input: + * name the file to find + * path the Lst of directories to search + * + * 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(const char *name, Lst path) +{ + LstNode ln; /* a list element */ + char *file; /* the current filename to check */ + Path *p; /* current path member */ + const char *cp; /* index of first slash, if any */ + Boolean hasLastDot = 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 (NULL); + } + + if ((ln = Lst_First(path)) != NILLNODE) { + p = (Path *)Lst_Datum(ln); + if (p == dotLast) { + hasLastDot = TRUE; + if (DEBUG(DIR)) + printf("[dot last]..."); + } + } + if (DEBUG(DIR)) { + printf("\n"); + } + + /* + * If there's no leading directory components or if the leading + * directory component is exactly `./', consult the cached contents + * of each of the directories on the search path. + */ + if ((!hasSlash || (cp - name == 2 && *name == '.'))) { + /* + * We look through all the directories on the path seeking one which + * contains the final component of the given name. 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... + * + * No matter what, we always look for the file in the current + * directory before anywhere else (unless we found the magic + * DOTLAST path, in which case we search it last) 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). + */ + if (!hasLastDot && + (file = DirFindDot(hasSlash, name, cp)) != NULL) { + Lst_Close(path); + return file; + } + + 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); + return file; + } + } + + if (hasLastDot && + (file = DirFindDot(hasSlash, name, cp)) != NULL) { + Lst_Close(path); + return file; + } + } + Lst_Close(path); + + /* + * We didn't find the file on any directory in the search path. + * 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). + * [ This no longer applies: 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 (NULL); + } + + if (name[0] != '/') { + Boolean checkedDot = FALSE; + + if (DEBUG(DIR)) { + printf(" Trying subdirectories...\n"); + } + + if (!hasLastDot) { + if (dot) { + checkedDot = TRUE; + if ((file = DirLookupSubdir(dot, name)) != NULL) + return file; + } + if (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) { + if (checkedDot) + continue; + checkedDot = TRUE; + } + if ((file = DirLookupSubdir(p, name)) != NULL) { + Lst_Close(path); + return file; + } + } + Lst_Close(path); + + if (hasLastDot) { + if (dot && !checkedDot) { + checkedDot = TRUE; + if ((file = DirLookupSubdir(dot, name)) != NULL) + return file; + } + if (cur && (file = DirLookupSubdir(cur, name)) != NULL) + return file; + } + + 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); + } + + } else { /* name[0] == '/' */ + + /* + * For absolute names, compare directory path prefix against the + * the directory path of each member on the search path for an exact + * match. If we have an exact match on any member of the search path, + * use the cached contents of that member to lookup the final file + * component. If that lookup fails we can safely assume that the + * file does not exist at all. This is signified by DirLookupAbs() + * returning an empty string. + */ + if (DEBUG(DIR)) { + printf(" Trying exact path matches...\n"); + } + + if (!hasLastDot && cur && (file = DirLookupAbs(cur, name, cp)) != NULL) + return *file?file:NULL; + + (void)Lst_Open(path); + while ((ln = Lst_Next(path)) != NILLNODE) { + p = (Path *)Lst_Datum(ln); + if (p == dotLast) + continue; + if ((file = DirLookupAbs(p, name, cp)) != NULL) { + Lst_Close(path); + return *file?file:NULL; + } + } + Lst_Close(path); + + if (hasLastDot && cur && (file = DirLookupAbs(cur, name, cp)) != NULL) + return *file?file: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 (NULL); + } else { + p = (Path *)Lst_Datum(ln); + } + + if (Hash_FindEntry(&p->files, cp) != NULL) { + return (estrdup(name)); + } else { + return (NULL); + } +#else /* !notdef */ + if (DEBUG(DIR)) { + printf(" Looking for \"%s\" ...\n", name); + } + + bigmisses += 1; + entry = Hash_FindEntry(&mtimes, name); + if (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, 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 (NULL); + } +#endif /* notdef */ +} + + +/*- + *----------------------------------------------------------------------- + * Dir_FindHereOrAbove -- + * search for a path starting at a given directory and then working + * our way up towards the root. + * + * Input: + * here starting directory + * search_path the path we are looking for + * result the result of a successful search is placed here + * rlen the length of the result buffer + * (typically MAXPATHLEN + 1) + * + * Results: + * 0 on failure, 1 on success [in which case the found path is put + * in the result buffer]. + * + * Side Effects: + *----------------------------------------------------------------------- + */ +int +Dir_FindHereOrAbove(char *here, char *search_path, char *result, int rlen) { + + struct stat st; + char dirbase[MAXPATHLEN + 1], *db_end; + char try[MAXPATHLEN + 1], *try_end; + + /* copy out our starting point */ + snprintf(dirbase, sizeof(dirbase), "%s", here); + db_end = dirbase + strlen(dirbase); + + /* loop until we determine a result */ + while (1) { + + /* try and stat(2) it ... */ + snprintf(try, sizeof(try), "%s/%s", dirbase, search_path); + if (stat(try, &st) != -1) { + /* + * success! if we found a file, chop off + * the filename so we return a directory. + */ + if ((st.st_mode & S_IFMT) != S_IFDIR) { + try_end = try + strlen(try); + while (try_end > try && *try_end != '/') + try_end--; + if (try_end > try) + *try_end = 0; /* chop! */ + } + + /* + * done! + */ + snprintf(result, rlen, "%s", try); + return(1); + } + + /* + * nope, we didn't find it. if we used up dirbase we've + * reached the root and failed. + */ + if (db_end == dirbase) + break; /* failed! */ + + /* + * truncate dirbase from the end to move up a dir + */ + while (db_end > dirbase && *db_end != '/') + db_end--; + *db_end = 0; /* chop! */ + + } /* while (1) */ + + /* + * we failed... + */ + return(0); +} + +/*- + *----------------------------------------------------------------------- + * Dir_MTime -- + * Find the modification time of the file described by gn along the + * search path dirSearchPath. + * + * Input: + * gn the file whose modification time is desired + * + * 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(GNode *gn) +{ + 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->type & OP_PHONY) { + gn->mtime = 0; + return 0; + } else if (gn->path == NULL) { + if (gn->type & OP_NOPATH) + fullName = NULL; + else + fullName = Dir_FindFile(gn->name, Suff_FindPath(gn)); + } else { + fullName = gn->path; + } + + if (fullName == NULL) { + fullName = estrdup(gn->name); + } + + entry = Hash_FindEntry(&mtimes, fullName); + if (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 == 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... + * + * Input: + * path the path to which the directory should be + * added + * name the name of the directory to add + * + * Results: + * none + * + * Side Effects: + * A structure is added to the list and the directory is + * read and hashed. + *----------------------------------------------------------------------- + */ +Path * +Dir_AddDir(Lst path, const char *name) +{ + LstNode ln = NILLNODE; /* node in case Path structure is found */ + Path *p = NULL; /* pointer to new Path structure */ + DIR *d; /* for reading directory */ + struct dirent *dp; /* entry in directory */ + + if (strcmp(name, ".DOTLAST") == 0) { + ln = Lst_Find(path, (ClientData)UNCONST(name), DirFindName); + if (ln != NILLNODE) + return (Path *)Lst_Datum(ln); + else { + dotLast->refCount += 1; + (void)Lst_AtFront(path, (ClientData)dotLast); + } + } + + if (path) + ln = Lst_Find(openDirectories, (ClientData)UNCONST(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)) != NULL) { + p = emalloc(sizeof(Path)); + p->name = estrdup(name); + p->hits = 0; + p->refCount = 1; + Hash_InitTable(&p->files, -1); + + while ((dp = readdir(d)) != 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, 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(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. + * + * Input: + * flag flag which should precede each directory + * path list of directories + * + * 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(const char *flag, Lst path) +{ + char *str; /* the string which will be returned */ + char *s1, *s2;/* 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); + s2 = str_concat(flag, p->name, 0); + str = str_concat(s1 = str, s2, STR_ADDSPACE); + free(s1); + free(s2); + } + Lst_Close(path); + } + + return (str); +} + +/*- + *----------------------------------------------------------------------- + * Dir_Destroy -- + * Nuke a directory descriptor, if possible. Callback procedure + * for the suffixes module when destroying a search path. + * + * Input: + * pp The directory descriptor to nuke + * + * Results: + * None. + * + * Side Effects: + * If no other path references this directory (refCount == 0), + * the Path and all its data are freed. + * + *----------------------------------------------------------------------- + */ +void +Dir_Destroy(ClientData pp) +{ + 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(p->name); + free(p); + } +} + +/*- + *----------------------------------------------------------------------- + * Dir_ClearPath -- + * Clear out all elements of the given search path. This is different + * from destroying the list, notice. + * + * Input: + * path Path to clear + * + * Results: + * None. + * + * Side Effects: + * The path is set to the empty list. + * + *----------------------------------------------------------------------- + */ +void +Dir_ClearPath(Lst path) +{ + 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. + * + * Input: + * path1 Dest + * path2 Source + * + * Results: + * None + * + * Side Effects: + * Reference counts for added dirs are upped. + * + *----------------------------------------------------------------------- + */ +void +Dir_Concat(Lst path1, Lst path2) +{ + 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(void) +{ + 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(ClientData p, ClientData dummy) +{ + printf("%s ", ((Path *)p)->name); + return (dummy ? 0 : 0); +} + +void +Dir_PrintPath(Lst path) +{ + Lst_ForEach(path, DirPrintDir, (ClientData)0); +} diff --git a/devel/bmake/files/dir.h b/devel/bmake/files/dir.h new file mode 100644 index 0000000000000..852e32da017c7 --- /dev/null +++ b/devel/bmake/files/dir.h @@ -0,0 +1,108 @@ +/* $NetBSD: dir.h,v 1.1.1.1 2005/12/02 00:02:59 sjg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. + * + * 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 + */ + +/* + * 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(const char *); +void Dir_InitCur(const char *); +void Dir_InitDot(void); +void Dir_End(void); +void Dir_SetPATH(void); +Boolean Dir_HasWildcards(char *); +void Dir_Expand(const char *, Lst, Lst); +char *Dir_FindFile(const char *, Lst); +int Dir_FindHereOrAbove(char *, char *, char *, int); +int Dir_MTime(GNode *); +Path *Dir_AddDir(Lst, const char *); +char *Dir_MakeFlags(const char *, Lst); +void Dir_ClearPath(Lst); +void Dir_Concat(Lst, Lst); +void Dir_PrintDirectories(void); +void Dir_PrintPath(Lst); +void Dir_Destroy(ClientData); +ClientData Dir_CopyDir(ClientData); + +#endif /* _DIR */ diff --git a/devel/bmake/files/find_lib.sh b/devel/bmake/files/find_lib.sh new file mode 100755 index 0000000000000..3c2e4af2f2515 --- /dev/null +++ b/devel/bmake/files/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/devel/bmake/files/for.c b/devel/bmake/files/for.c new file mode 100644 index 0000000000000..a47fb77bcebc4 --- /dev/null +++ b/devel/bmake/files/for.c @@ -0,0 +1,392 @@ +/* $NetBSD: for.c,v 1.1.1.1 2005/12/02 00:02:59 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: for.c,v 1.1.1.1 2005/12/02 00:02:59 sjg 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 2005/12/02 00:02:59 sjg 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(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(const char *data, size_t len) +{ + Buffer buf; + int varlen; + + buf = Buf_Init(0); + Buf_AddBytes(buf, len, (Byte *)UNCONST(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 + * + * Input: + * line Line to parse + * + * 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(char *line) +{ + char *ptr = line, *sub, *in, *wrd; + int level; /* Level at which to report errors. */ + + level = PARSE_FATAL; + + + if (forLevel == 0) { + Buffer buf; + int varlen; + static const char instr[] = "in"; + + 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" */ + /* + * compensate for hp/ux's brain damaged assert macro that + * does not handle double quotes nicely. + */ + assert(!memcmp(ptr, instr, 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(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(int lineno) +{ + 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, lineno); + + 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/devel/bmake/files/getopt.c b/devel/bmake/files/getopt.c new file mode 100644 index 0000000000000..4a82ed7b373a6 --- /dev/null +++ b/devel/bmake/files/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 2005/12/02 00:02:59 sjg 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/devel/bmake/files/hash.c b/devel/bmake/files/hash.c new file mode 100644 index 0000000000000..98b604edd9dad --- /dev/null +++ b/devel/bmake/files/hash.c @@ -0,0 +1,463 @@ +/* $NetBSD: hash.c,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: hash.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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 2005/12/02 00:03:00 sjg 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(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. + * + * Input: + * t Structure to to hold table. + * 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. + * + * Results: + * None. + * + * Side Effects: + * Memory is allocated for the initial bucket area. + * + *--------------------------------------------------------- + */ + +void +Hash_InitTable(Hash_Table *t, int numBuckets) +{ + int i; + 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 = 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(Hash_Table *t) +{ + struct Hash_Entry **hp, *h, *nexth = NULL; + int i; + + for (hp = t->bucketPtr, i = t->size; --i >= 0;) { + for (h = *hp++; h != NULL; h = nexth) { + nexth = h->next; + free(h); + } + } + free(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. + * + * Input: + * t Hash table to search. + * key A hash 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(Hash_Table *t, const char *key) +{ + Hash_Entry *e; + unsigned h; + const 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. + * + * Input: + * t Hash table to search. + * key A hash key. + * newPtr Filled in with TRUE if new entry created, + * FALSE otherwise. + * + * 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(Hash_Table *t, const char *key, Boolean *newPtr) +{ + Hash_Entry *e; + unsigned h; + const 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 = 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(Hash_Table *t, Hash_Entry *e) +{ + 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(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. + * + * Input: + * t Table to be searched. + * searchPtr Area in which to keep state about search. + * + * 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(Hash_Table *t, Hash_Search *searchPtr) +{ + searchPtr->tablePtr = t; + searchPtr->nextIndex = 0; + searchPtr->hashEntryPtr = NULL; + return Hash_EnumNext(searchPtr); +} + +/* + *--------------------------------------------------------- + * + * Hash_EnumNext -- + * This procedure returns successive entries in the hash table. + * + * Input: + * searchPtr Area used to keep state about search. + * + * 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(Hash_Search *searchPtr) +{ + 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(Hash_Table *t) +{ + Hash_Entry *e, *next = NULL, **hp, **xp; + int i, mask; + 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 = 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(oldhp); +} diff --git a/devel/bmake/files/hash.h b/devel/bmake/files/hash.h new file mode 100644 index 0000000000000..ee9610399a098 --- /dev/null +++ b/devel/bmake/files/hash.h @@ -0,0 +1,150 @@ +/* $NetBSD: hash.h,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. + * + * 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 + */ + +/* + * 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(Hash_Table *, int); +void Hash_DeleteTable(Hash_Table *); +Hash_Entry *Hash_FindEntry(Hash_Table *, const char *); +Hash_Entry *Hash_CreateEntry(Hash_Table *, const char *, Boolean *); +void Hash_DeleteEntry(Hash_Table *, Hash_Entry *); +Hash_Entry *Hash_EnumFirst(Hash_Table *, Hash_Search *); +Hash_Entry *Hash_EnumNext(Hash_Search *); + +#endif /* _HASH */ diff --git a/devel/bmake/files/install-sh b/devel/bmake/files/install-sh new file mode 100755 index 0000000000000..6272a6708e685 --- /dev/null +++ b/devel/bmake/files/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 2005/12/02 00:03:00 sjg 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/devel/bmake/files/job.c b/devel/bmake/files/job.c new file mode 100644 index 0000000000000..de48ea09fb5b1 --- /dev/null +++ b/devel/bmake/files/job.c @@ -0,0 +1,3755 @@ +/* $NetBSD: job.c,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: job.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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 2005/12/02 00:03:00 sjg Exp $"); +#endif +#endif /* not lint */ +#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 +#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 +#include +#include +#include +#if defined(HAVE_SYS_SOCKET_H) +# include +#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 = NILGNODE; + /* 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\"\n", "", '#', + "v", "e", +}, + /* + * SH description. Echo control is also possible and, under + * sun UNIX anyway, one can even control error checking. + */ +{ + "sh", + FALSE, "", "", "", 0, + FALSE, "echo \"%s\"\n", "%s\n", "{ %s \n} || exit $?\n", '#', +#ifdef __NetBSD__ + "q", +#else + "", +#endif + "", +}, + /* + * KSH description. + */ +{ + "ksh", + TRUE, "set +v", "set -v", "set +v", 6, + FALSE, "echo \"%s\"\n", "%s\n", "{ %s \n} || exit $?\n", '#', + "v", + "", +}, + /* + * UNKNOWN. + */ +{ + NULL, + FALSE, NULL, NULL, NULL, 0, + FALSE, NULL, NULL, NULL, 0, + NULL, NULL, +} +}; +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 */ +const char *shellPath = NULL, /* full pathname of + * executable image */ + *shellName = NULL; /* last component of shell */ +static const char *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 +static struct pollfd *fds = NULL; +static Job **jobfds = NULL; +static int nfds = 0; +static int maxfds = 0; +static void watchfd(Job *); +static void clearfd(Job *); +static int readyfd(Job *); +#define JBSTART 256 +#define JBFACTOR 2 +#endif + +STATIC GNode *lastNode; /* The node for which output was most recently + * produced. */ +STATIC const 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. */ + +static Job childExitJob; /* child exit pseudo-job */ +int exit_pipe[2] = { -1, -1 }; /* child exit signal pipe. */ + +#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 */ + + +sigset_t caught_signals; /* Set of signals we handle */ + +#if defined(USE_PGRP) +# if defined(HAVE_KILLPG) +# define KILL(pid, sig) killpg((pid), (sig)) +# else +# define KILL(pid, sig) kill(-(pid), (sig)) +# endif +#else +# define KILL(pid, sig) kill((pid), (sig)) +#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(ClientData, ClientData); +static void JobPassSig(int); +static void JobChildSig(int); +#ifdef USE_PGRP +static void JobContinueSig(int); +#endif +static int JobCmpPid(ClientData, ClientData); +static int JobPrintCommand(ClientData, ClientData); +static int JobSaveCommand(ClientData, ClientData); +static void JobClose(Job *); +#ifdef REMOTE +static int JobCmpRmtID(ClientData, ClientData); +# ifdef RMT_WILL_WATCH +static void JobLocalInput(int, Job *); +# endif +#else +static void JobFinish(Job *, WAIT_T); +static void JobExec(Job *, char **); +#endif +static void JobMakeArgv(Job *, char **); +static int JobRestart(Job *); +static int JobStart(GNode *, int, Job *); +static char *JobOutput(Job *, char *, char *, int); +static void JobDoOutput(Job *, Boolean); +static Shell *JobMatchShell(const char *); +static void JobInterrupt(int, int); +static void JobRestartJobs(void); +static void JobTokenAdd(void); +static void JobSigLock(sigset_t *); +static void JobSigUnlock(sigset_t *); +static void JobSigReset(void); + + + +/* + * JobSigLock/JobSigUnlock + * + * Signal lock routines to get exclusive access. Currently used to + * protect `jobs' and `stoppedJobs' list manipulations. + */ +static void JobSigLock(sigset_t *omaskp) +{ + if (sigprocmask(SIG_BLOCK, &caught_signals, omaskp) != 0) { + Punt("JobSigLock: sigprocmask: %s", strerror(errno)); + sigemptyset(omaskp); + } +} + +static void JobSigUnlock(sigset_t *omaskp) +{ + (void)sigprocmask(SIG_SETMASK, omaskp, NULL); +} + +/*- + *----------------------------------------------------------------------- + * JobCondPassSig -- + * Pass a signal to a job if the job is remote or if USE_PGRP + * is defined. + * + * Input: + * jobp Job to biff + * signop Signal to send it + * + * Results: + * === 0 + * + * Side Effects: + * None, except the job may bite it. + * + *----------------------------------------------------------------------- + */ +static int +JobCondPassSig(ClientData jobp, ClientData signop) +{ + 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; +} + +/*- + *----------------------------------------------------------------------- + * JobChldSig -- + * SIGCHLD handler. + * + * Input: + * signo The signal number we've received + * + * Results: + * None. + * + * Side Effects: + * Sends a token on the child exit pipe to wake us up from + * select()/poll(). + * + *----------------------------------------------------------------------- + */ +static void +JobChildSig(int signo __unused) +{ + write(exit_pipe[1], ".", 1); +} + + +#ifdef USE_PGRP +/*- + *----------------------------------------------------------------------- + * JobContinueSig -- + * Resume all stopped jobs. + * + * Input: + * signo The signal number we've received + * + * Results: + * None. + * + * Side Effects: + * Jobs start running again. + * + *----------------------------------------------------------------------- + */ +static void +JobContinueSig(int signo __unused) +{ + JobRestartJobs(); +} +#endif + +/*- + *----------------------------------------------------------------------- + * JobPassSig -- + * Pass a signal on to all remote jobs and to all local jobs if + * USE_PGRP is defined, then die ourselves. + * + * Input: + * signo The signal number we've received + * + * Results: + * None. + * + * Side Effects: + * We die by the same signal. + * + *----------------------------------------------------------------------- + */ +static void +JobPassSig(int signo) +{ + 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); + sigdelset(&nmask, signo); + (void)sigprocmask(SIG_SETMASK, &nmask, &omask); + + act.sa_handler = SIG_DFL; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + (void)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); + } + + /* Restore handler and signal mask */ + act.sa_handler = JobPassSig; + (void)sigaction(signo, &act, NULL); + (void)sigprocmask(SIG_SETMASK, &omask, 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. + * + * Input: + * job job to examine + * pid process id desired + * + * Results: + * 0 if the pid's match + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +static int +JobCmpPid(ClientData job, ClientData pid) +{ + 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. + * + * Input: + * job job to examine + * rmtID remote id desired + * + * Results: + * 0 if the rmtID's match + * + * Side Effects: + * None. + *----------------------------------------------------------------------- + */ +static int +JobCmpRmtID(ClientData job, ClientData rmtID) +{ + return(*(int *)rmtID - ((Job *)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. + * + * Input: + * cmdp command string to print + * jobp job for which to print it + * + * 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(ClientData cmdp, ClientData jobp) +{ + 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 */ + const char *cmdTemplate; /* Template to use when printing the + * command */ + char *cmdStart; /* Start of expanded command */ + char *escCmd = NULL; /* Command with quotes/backticks escaped */ + char *cmd = (char *)cmdp; + Job *job = (Job *)jobp; + char *cp, *tmp; + int i, j; + + 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; + + cmdStart = cmd = Var_Subst(NULL, cmd, job->node, FALSE); + + cmdTemplate = "%s\n"; + + /* + * Check for leading @' and -'s to control echoing and error checking. + */ + while (*cmd == '@' || *cmd == '-' || (*cmd == '+')) { + switch (*cmd) { + case '@': + shutUp = TRUE; + break; + case '-': + errOff = TRUE; + break; + case '+': + if (noSpecials) { + /* + * We're not actually executing anything... + * but this one needs to be - use compat mode just for it. + */ + CompatRunCommand(cmdp, (ClientData)job->node); + return 0; + } + break; + } + cmd++; + } + + while (isspace((unsigned char) *cmd)) + cmd++; + + /* + * If the shell doesn't have error control the alternate echo'ing will + * be done (to avoid showing additional error checking code) + * and this will need the characters '$ ` \ "' escaped + */ + + if (!commandShell->hasErrCtl) { + /* Worst that could happen is every char needs escaping. */ + escCmd = emalloc((strlen(cmd) * 2) + 1); + for (i = 0, j= 0; cmd[i] != '\0'; i++, j++) { + if (cmd[i] == '$' || cmd[i] == '`' || cmd[i] == '\\' || + cmd[i] == '"') + escCmd[j++] = '\\'; + escCmd[j] = cmd[i]; + } + escCmd[j] = 0; + } + + if (shutUp) { + if (!(job->flags & JOB_SILENT) && !noSpecials && + commandShell->hasEchoCtl) { + DBPRINTF("%s\n", commandShell->echoOff); + } else { + if (commandShell->hasErrCtl) + 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) { + if (commandShell->hasEchoCtl) { + DBPRINTF("%s\n", commandShell->echoOff); + } + DBPRINTF(commandShell->errCheck, escCmd); + shutUp = TRUE; + } else { + if (!shutUp) { + DBPRINTF(commandShell->errCheck, escCmd); + } + } + 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; + } + } else { + + /* + * If errors are being checked and the shell doesn't have error control + * but does supply an errOut template, then setup commands to run + * through it. + */ + + if (!commandShell->hasErrCtl && commandShell->errOut && + (*commandShell->errOut != '\0')) { + if (!(job->flags & JOB_SILENT) && !shutUp) { + if (commandShell->hasEchoCtl) { + DBPRINTF("%s\n", commandShell->echoOff); + } + DBPRINTF(commandShell->errCheck, escCmd); + shutUp = TRUE; + } + /* If it's a comment line or blank, treat as an ignored error */ + if ((escCmd[0] == commandShell->commentChar) || + (escCmd[0] == 0)) + cmdTemplate = commandShell->ignErr; + else + cmdTemplate = commandShell->errOut; + 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\n", cp); + } + + DBPRINTF(cmdTemplate, cmd); + free(cmdStart); + if (escCmd) + free(escCmd); + 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 && commandShell->hasEchoCtl) { + DBPRINTF("%s\n", commandShell->echoOn); + } + if (cp != NULL) { + DBPRINTF("test -d %s && ", cp); + DBPRINTF("cd %s\n", Var_Value(".OBJDIR", VAR_GLOBAL, &tmp)); + } + 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(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) +{ + if (usePipes && (job->flags & JOB_FIRST)) { +#ifdef RMT_WILL_WATCH + Rmt_Ignore(job->inPipe); +#else + clearfd(job); +#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. + * + * Input: + * job job to finish + * status sub-why job went away + * + * Results: + * None + * + * Side Effects: + * 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 *job, WAIT_T status) +{ + 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 [%s] exited.\n", + job->pid, job->node->name); + (void)fflush(stdout); + } + if (WEXITSTATUS(status) != 0) { + if (usePipes && job->node != lastNode) { + MESSAGE(out, job->node); + lastNode = job->node; + } + (void)fprintf(out, "*** [%s] Error code %d%s\n", + job->node->name, + 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, "*** [%s] Completed successfully\n", + job->node->name); + } + } else if (WIFSTOPPED(status) && WSTOPSIG(status) != SIGCONT) { + if (DEBUG(JOB)) { + (void)fprintf(stdout, "Process %d (%s) stopped.\n", + job->pid, job->node->name); + (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, "*** [%s] Suspended\n", + job->node->name); + break; + case SIGSTOP: + (void)fprintf(out, "*** [%s] Stopped\n", + job->node->name); + break; + default: + (void)fprintf(out, "*** [%s] Stopped -- signal %d\n", + job->node->name, 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, "*** [%s] Continued\n", job->node->name); + } + if (!(job->flags & JOB_CONTINUING)) { + if (DEBUG(JOB)) { + (void)fprintf(stdout, + "Warning: process %d [%s] was not continuing.\n", + job->pid, job->node->name); + (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, "*** [%s] Signal %d\n", + job->node->name, 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(job); + } else if (WAIT_STATUS(status)) { + errors += 1; + free(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 + * + * Input: + * gn the node of the file to touch + * silent TRUE if should not print message + * + * 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(GNode *gn, Boolean silent) +{ + 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. + * + * Input: + * gn The target whose commands need verifying + * abortProc Function to abort with message + * + * 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(GNode *gn, void (*abortProc)(const char *, ...)) +{ + if (OP_NOP(gn->type) && Lst_IsEmpty(gn->commands) && + ((gn->type & OP_LIB) == 0 || Lst_IsEmpty(gn->children))) { + /* + * No commands. Look for .DEFAULT rule from which we might infer + * commands + */ + if ((DEFAULT != NILGNODE) && !Lst_IsEmpty(DEFAULT->commands) && + (gn->type & OP_SPECIAL) == 0) { + 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 && (gn->type & OP_SPECIAL) == 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 + * + * Input: + * stream Stream that's ready (ignored) + * job Job to which the stream belongs + * + * Results: + * None + * + * Side Effects: + * JobDoOutput is called. + * + *----------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static void +JobLocalInput(int stream, Job *job) +{ + JobDoOutput(job, FALSE); +} +#endif /* RMT_WILL_WATCH */ + +/*- + *----------------------------------------------------------------------- + * JobExec -- + * Execute the shell for the given job. Called from JobStart and + * JobRestart. + * + * Input: + * job Job to execute + * + * Results: + * None. + * + * Side Effects: + * A shell is executed, outputs is altered and the Job structure added + * to the job table. + * + *----------------------------------------------------------------------- + */ +static void +JobExec(Job *job, char **argv) +{ + int cpid; /* ID of new child */ + sigset_t mask; + + 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 */ + + /* No interruptions until this job is on the `jobs' list */ + JobSigLock(&mask); + + if ((cpid = vfork()) == -1) { + Punt("Cannot vfork: %s", strerror(errno)); + } else if (cpid == 0) { + + /* + * Reset all signal handlers; this is necessary because we also + * need to unblock signals before we exec(2). + */ + JobSigReset(); + + /* Now unblock signals */ + sigemptyset(&mask); + JobSigUnlock(&mask); + + /* + * 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) { + execError("dup2", "job->cmdFILE"); + _exit(1); + } + (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) { + execError("dup2", "job->outPipe"); + _exit(1); + } + } 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) { + execError("dup2", "job->outFd"); + _exit(1); + } + } + /* + * 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) { + execError("dup2", "1, 2"); + _exit(1); + } + +#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("exec", shellPath); + } + _exit(1); + } else { + 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 + watchfd(job); +#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 RMT_NO_EXEC +jobExecFinish: +#endif + /* + * Now the job is actually running, add it to the table. + */ + if (DEBUG(JOB)) { + printf("JobExec(%s): pid %d added to jobs table\n", + job->node->name, job->pid); + } + nJobs += 1; + (void)Lst_AtEnd(jobs, (ClientData)job); + JobSigUnlock(&mask); +} + +/*- + *----------------------------------------------------------------------- + * JobMakeArgv -- + * Create the argv needed to execute the shell for a given job. + * + * + * Results: + * + * Side Effects: + * + *----------------------------------------------------------------------- + */ +static void +JobMakeArgv(Job *job, char **argv) +{ + int argc; + static char args[10]; /* For merged arguments */ + + argv[0] = UNCONST(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] = UNCONST(commandShell->exit); + argc++; + } + if (!(job->flags & JOB_SILENT) && commandShell->echo) { + argv[argc] = UNCONST(commandShell->echo); + argc++; + } + } + argv[argc] = NULL; +} + +/*- + *----------------------------------------------------------------------- + * JobRestart -- + * Restart a job that stopped for some reason. + * + * Input: + * job Job to restart + * + * Results: + * 1 if max number of running jobs has been reached, 0 otherwise. + * + *----------------------------------------------------------------------- + */ +static int +JobRestart(Job *job) +{ +#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 1; + } +#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 1; + } 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 ((nJobs != maxJobs) && + ((job->flags & JOB_REMOTE) || + (nLocal < maxLocal) || + ((maxLocal == 0) && + ((job->flags & JOB_SPECIAL) +#ifdef REMOTE + && (job->node->type & OP_NOEXPORT) +#endif + )))) + { + /* + * 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); + return 1; + } + } + return 0; +} + +/*- + *----------------------------------------------------------------------- + * JobStart -- + * Start a target-creation process going for the target described + * by the graph node gn. + * + * Input: + * gn target to create + * flags flags for the job to override normal ones. + * e.g. JOB_SPECIAL or JOB_IGNDOTS + * previous The previous Job structure for this node, if any. + * + * 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(GNode *gn, int flags, Job *previous) +{ + 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 = emalloc(sizeof(Job)); + if (job == NULL) { + Punt("JobStart out of memory"); + } + flags |= JOB_FIRST; + } + if (gn->type & OP_SPECIAL) + flags |= JOB_SPECIAL; + + 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 + job->inPollfd = NULL; +#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)]; + sigset_t mask; + /* + * We're serious here, but if the commands were bogus, we're + * also dead... + */ + if (!cmdsOK) { + DieHorribly(); + } + + JobSigLock(&mask); + (void)strcpy(tfile, TMPPAT); + if ((tfd = mkstemp(tfile)) == -1) + Punt("Could not create temporary file %s", strerror(errno)); + if (!DEBUG(SCRIPT)) + (void)eunlink(tfile); + JobSigUnlock(&mask); + + 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(); + job->node->made = MADE; + Make_Update(job->node); + } + free(job); + return(JOB_FINISHED); + } else { + free(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]; + 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 *job, char *cp, char *endp, int msg) +{ + 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. + * + * Input: + * job the job whose output needs printing + * finish TRUE if this is the last time we'll be called + * for this job + * + * Results: + * None + * + * Side Effects: + * curPos may be shifted as may the contents of outBuf. + *----------------------------------------------------------------------- + */ +STATIC void +JobDoOutput(Job *job, Boolean finish) +{ + Boolean gotNL = FALSE; /* true if got a newline */ + Boolean fbuf; /* true if our buffer filled up */ + int nr; /* number of bytes read */ + int i; /* auxiliary index into outBuf */ + 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) { + 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); + } + } +} + +static void +JobRun(GNode *targ) +{ +#ifdef notyet + /* + * Unfortunately it is too complicated to run .BEGIN, .END, + * and .INTERRUPT job in the parallel job module. This has + * the nice side effect that it avoids a lot of other problems. + */ + Lst lst = Lst_Init(FALSE); + Lst_AtEnd(lst, targ); + (void)Make_Run(lst); + Lst_Destroy(lst, NOFREE); + JobStart(targ, JOB_SPECIAL, NULL); + while (nJobs) { + Job_CatchOutput(); +#ifndef RMT_WILL_WATCH + Job_CatchChildren(!usePipes); +#endif /* RMT_WILL_WATCH */ + } +#else + Compat_Make(targ, targ); + if (targ->made == ERROR) { + PrintOnError("\n\nStop."); + exit(1); + } +#endif +} + +/*- + *----------------------------------------------------------------------- + * Job_CatchChildren -- + * Handle the exit of a child. Called from Make_Make. + * + * Input: + * block TRUE if should block on the wait + * + * 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(Boolean block) +{ + int pid; /* pid of dead child */ + 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(void) +{ + int nready; + LstNode ln; + 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) { + if ((nready = poll((wantToken ? fds : (fds + 1)), + (wantToken ? nfds : (nfds - 1)), POLL_MSEC)) <= 0) { + return; + } else { + sigset_t mask; + JobSigLock(&mask); + if (Lst_Open(jobs) == FAILURE) { + Punt("Cannot open job table"); + } + + if (readyfd(&childExitJob)) { + char token; + (void)read(childExitJob.inPipe, &token, 1); + nready -= 1; + } + + while (nready && (ln = Lst_Next(jobs)) != NILLNODE) { + job = (Job *)Lst_Datum(ln); + if (readyfd(job)) { + JobDoOutput(job, FALSE); + nready -= 1; + } + } + Lst_Close(jobs); + JobSigUnlock(&mask); + } + } +#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(GNode *gn) +{ + (void)JobStart(gn, 0, NULL); +} + +void +Shell_Init() +{ + 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 = ""; + } +} + +/*- + *----------------------------------------------------------------------- + * Job_Init -- + * Initialize the process module + * + * Input: + * maxproc the greatest number of jobs which may be running + * at one time + * maxlocal the greatest number of jobs which may be running + * at once + * + * Results: + * none + * + * Side Effects: + * lists and counters are initialized + *----------------------------------------------------------------------- + */ +void +Job_Init(int maxproc, int maxlocal) +{ + 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; + } + + Shell_Init(); + + if (pipe(exit_pipe) < 0) + Fatal("error in pipe: %s", strerror(errno)); + fcntl(exit_pipe[0], F_SETFD, 1); + fcntl(exit_pipe[1], F_SETFD, 1); + + childExitJob.inPipe = exit_pipe[0]; + + sigemptyset(&caught_signals); + /* + * Install a SIGCHLD handler. + */ + (void)signal(SIGCHLD, JobChildSig); + sigaddset(&caught_signals, SIGCHLD); + +#define ADDSIG(s,h) \ + if (signal(s, SIG_IGN) != SIG_IGN) { \ + sigaddset(&caught_signals, s); \ + (void)signal(s, h); \ + } + + /* + * Catch the four signals that POSIX specifies if they aren't ignored. + * JobPassSig will take care of calling JobInterrupt if appropriate. + */ + ADDSIG(SIGINT, JobPassSig) + ADDSIG(SIGHUP, JobPassSig) + ADDSIG(SIGTERM, JobPassSig) + ADDSIG(SIGQUIT, JobPassSig) + + /* + * 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) + ADDSIG(SIGTSTP, JobPassSig) + ADDSIG(SIGTTOU, JobPassSig) + ADDSIG(SIGTTIN, JobPassSig) + ADDSIG(SIGWINCH, JobPassSig) + ADDSIG(SIGCONT, JobContinueSig) +#endif +#undef ADDSIG + + begin = Targ_FindNode(".BEGIN", TARG_NOCREATE); + + if (begin != NILGNODE) { + JobRun(begin); + if (begin->made == ERROR) { + PrintOnError("\n\nStop."); + exit(1); + } + } + postCommands = Targ_FindNode(".END", TARG_CREATE); +} + +static void JobSigReset(void) +{ +#define DELSIG(s) \ + if (sigismember(&caught_signals, s)) { \ + (void)signal(SIGINT, SIG_DFL); \ + } + + DELSIG(SIGINT) + DELSIG(SIGHUP) + DELSIG(SIGQUIT) + DELSIG(SIGTERM) +#if defined(RMT_WANTS_SIGNALS) || defined(USE_PGRP) + DELSIG(SIGTSTP) + DELSIG(SIGTTOU) + DELSIG(SIGTTIN) + DELSIG(SIGWINCH) + DELSIG(SIGCONT) +#endif +#undef DELSIG + (void)signal(SIGCHLD, SIG_DFL); +} + +/*- + *----------------------------------------------------------------------- + * 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(void) +{ + 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 shell in 'shells' given its name. + * + * Results: + * A pointer to the Shell structure. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static Shell * +JobMatchShell(const char *name) +{ + Shell *sh; + + for (sh = shells; sh->name != NULL; sh++) { + if (strcmp(name, sh->name) == 0) + return (sh); + } + return (NULL); +} + +/*- + *----------------------------------------------------------------------- + * Job_ParseShell -- + * Parse a shell specification and set up commandShell, shellPath + * and shellName appropriately. + * + * Input: + * line The shell spec + * + * 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. + * 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(char *line) +{ + char **words; + char **argv; + int argc; + char *path; + Shell newShell; + Boolean fullSpec = FALSE; + Shell *sh; + + while (isspace((unsigned char)*line)) { + line++; + } + + if (shellArgv) + free(UNCONST(shellArgv)); + + memset(&newShell, 0, sizeof(newShell)); + + /* + * Parse the specification by keyword + */ + words = brk_string(line, &argc, TRUE, &path); + shellArgv = path; + + for (path = NULL, 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 if (strncmp(*argv, "errout=", 7) == 0) { + newShell.errOut = &argv[0][7]; + } else if (strncmp(*argv, "comment=", 8) == 0) { + newShell.commentChar = argv[0][8]; + } 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"); + free(words); + return(FAILURE); + } else { + if ((sh = JobMatchShell(newShell.name)) == NULL) { + Parse_Error(PARSE_WARNING, "%s: No matching shell", + newShell.name); + free(words); + return(FAILURE); + } + commandShell = sh; + 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 = UNCONST(shellPath); + } else { + path += 1; + } + if (newShell.name != NULL) { + shellName = newShell.name; + } else { + shellName = path; + } + if (!fullSpec) { + if ((sh = JobMatchShell(shellName)) == NULL) { + Parse_Error(PARSE_WARNING, "%s: No matching shell", + shellName); + free(words); + return(FAILURE); + } + commandShell = sh; + } else { + commandShell = 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. + * + * Input: + * runINTERRUPT Non-zero if commands for the .INTERRUPT target + * should be executed + * signo signal received + * + * Results: + * None + * + * Side Effects: + * All children are killed. Another job will be started if the + * .INTERRUPT target was given. + *----------------------------------------------------------------------- + */ +static void +JobInterrupt(int runINTERRUPT, int signo) +{ + LstNode ln; /* element in job table */ + Job *job; /* job descriptor in that element */ + GNode *interrupt; /* the node describing the .INTERRUPT target */ + sigset_t mask; + + aborting = ABORT_INTERRUPT; + + JobSigLock(&mask); + + (void)Lst_Open(jobs); + while ((ln = Lst_Next(jobs)) != NILLNODE) { + GNode *gn; + + job = (Job *)Lst_Datum(ln); + gn = job->node; + + if ((gn->type & (OP_JOIN|OP_PHONY)) == 0 && !Targ_Precious(gn)) { + char *file = (gn->path == NULL ? gn->name : gn->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 %d to child %d.\n", + signo, job->pid); + (void)fflush(stdout); + } + KILL(job->pid, signo); + } +#endif /* RMT_WANTS_SIGNALS */ + } + Lst_Close(jobs); + +#ifdef REMOTE + (void)Lst_Open(stoppedJobs); + while ((ln = Lst_Next(stoppedJobs)) != NILLNODE) { + GNode *gn; + + job = (Job *)Lst_Datum(ln); + gn = job->node; + + 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 ((gn->type & (OP_JOIN|OP_PHONY)) == 0 && !Targ_Precious(gn)) { + char *file = (gn->path == NULL ? gn->name : gn->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 */ + } + Lst_Close(stoppedJobs); +#endif /* REMOTE */ + + JobSigUnlock(&mask); + + if (runINTERRUPT && !touchFlag) { + interrupt = Targ_FindNode(".INTERRUPT", TARG_NOCREATE); + if (interrupt != NILGNODE) { + ignoreErrors = FALSE; + JobRun(interrupt); + } + } + 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(void) +{ + if (postCommands != NILGNODE && !Lst_IsEmpty(postCommands->commands)) { + if (errors) { + Error("Errors reported so .END ignored"); + } else { + JobRun(postCommands); + } + } + Job_TokenFlush(); + return(errors); +} + +/*- + *----------------------------------------------------------------------- + * Job_End -- + * Cleanup any memory used by the jobs module + * + * Results: + * None. + * + * Side Effects: + * Memory is freed + *----------------------------------------------------------------------- + */ +void +Job_End(void) +{ +#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(void) +{ + 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(void) +{ + LstNode ln; /* element in job table */ + Job *job; /* the job descriptor in that element */ + WAIT_T foo; + sigset_t mask; + + aborting = ABORT_ERROR; + + if (nJobs) { + + JobSigLock(&mask); + (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) { + (void)Rmt_Signal(job, SIGINT); + (void)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 */ + } + Lst_Close(jobs); + JobSigUnlock(&mask); + } + + /* + * 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. + * + * Input: + * hostID ID of host we used, for matching children + * + * Results: + * none. + * + * Side Effects: + * The job descriptor is flagged for remigration. + * + *----------------------------------------------------------------------- + */ +void +JobFlagForMigration(int hostID) +{ + 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(void) +{ + sigset_t mask; + + JobSigLock(&mask); + while (!Lst_IsEmpty(stoppedJobs)) { + if (DEBUG(JOB)) { + (void)fprintf(stdout, "Restarting a stopped job.\n"); + (void)fflush(stdout); + } + if (JobRestart((Job *)Lst_DeQueue(stoppedJobs)) != 0) + break; + } + JobSigUnlock(&mask); +} + +#ifndef RMT_WILL_WATCH +static void +watchfd(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++; + + fds[1].fd = exit_pipe[0]; + fds[1].events = POLLIN; + jobfds[1] = &childExitJob; + childExitJob.inPollfd = &fds[1]; + 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) +{ + 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) +{ + if (job->inPollfd == NULL) + Punt("Polling unwatched job"); + return (job->inPollfd->revents & POLLIN) != 0; +} +#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(void) +{ + + 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(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(void) +{ + 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(void) +{ + char tok; + int count; + + wantToken = FALSE; + + if (aborting) + return FALSE; + + if (jobTokensRunning == 0) { + if (DEBUG(JOB)) + printf("first one's free\n"); + jobTokensRunning++; + return TRUE; + } + if (jobTokensFree > 0) { + jobTokensFree--; + jobTokensRunning++; + 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; + } + jobTokensRunning++; + if (DEBUG(JOB)) + printf("withdrew token\n"); + return TRUE; +} + +/*- + *----------------------------------------------------------------------- + * Job_TokenFlush -- + * Return free tokens to the pool. + * + *----------------------------------------------------------------------- + */ + +void +Job_TokenFlush(void) +{ + if (compatMake) return; + + while (jobTokensFree > 0) { + JobTokenAdd(); + jobTokensFree--; + } +} + +#ifdef USE_SELECT +int +emul_poll(struct pollfd *fd, int nfd, int timeout) +{ + fd_set rfds, wfds; + int i, maxfd, nselect, npoll; + struct timeval tv, *tvp; + long usecs; + + FD_ZERO(&rfds); + FD_ZERO(&wfds); + + maxfd = -1; + for (i = 0; i < nfd; i++) { + fd[i].revents = 0; + + if (fd[i].events & POLLIN) + FD_SET(fd[i].fd, &rfds); + + if (fd[i].events & POLLOUT) + FD_SET(fd[i].fd, &wfds); + + if (fd[i].fd > maxfd) + maxfd = fd[i].fd; + } + + if (maxfd >= FD_SETSIZE) { + Punt("Ran out of fd_set slots; " + "recompile with a larger FD_SETSIZE."); + } + + if (timeout < 0) { + tvp = NULL; + } else { + usecs = timeout * 1000; + tv.tv_sec = usecs / 1000000; + tv.tv_usec = usecs % 1000000; + tvp = &tv; + } + + nselect = select(maxfd + 1, &rfds, &wfds, 0, tvp); + + if (nselect <= 0) + return nselect; + + npoll = 0; + for (i = 0; i < nfd; i++) { + if (FD_ISSET(fd[i].fd, &rfds)) + fd[i].revents |= POLLIN; + + if (FD_ISSET(fd[i].fd, &wfds)) + fd[i].revents |= POLLOUT; + + if (fd[i].revents) + npoll++; + } + + return npoll; +} +#endif /* USE_SELECT */ diff --git a/devel/bmake/files/job.h b/devel/bmake/files/job.h new file mode 100644 index 0000000000000..4e2f53efd4764 --- /dev/null +++ b/devel/bmake/files/job.h @@ -0,0 +1,317 @@ +/* $NetBSD: job.h,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 + */ + +/* + * 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 +/* + * Emulate poll() in terms of select(). This is not a complete + * emulation but it is sufficient for make's purposes. + */ + +#define poll emul_poll +#define pollfd emul_pollfd + +struct emul_pollfd { + int fd; + short events; + short revents; +}; + +#define POLLIN 0x0001 +#define POLLOUT 0x0004 + +int +emul_poll(struct pollfd *fd, int nfd, int timeout); +#endif + +/* + * 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 + + +/*- + * 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 +struct pollfd; +#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 + struct pollfd *op_inPollfd; /* pollfd associated with inPipe */ +#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. Finally errOut + * is a printf template for running the command and causing the shell to + * exit on error. If any of these strings are empty when hasErrCtl is FALSE, + * the command will be executed anyway as is and if it causes an error, so be + * it. Any templates setup to echo the command will escape any '$ ` \ "'i + * characters in the command string to avoid common problems with + * echo "%s\n" as a template. + */ +typedef struct Shell { + const 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 */ + const char *echoOff; /* command to turn off echo */ + const char *echoOn; /* command to turn it back on again */ + const 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 */ + const char *errCheck; /* string to turn error checking on */ + const char *ignErr; /* string to turn off error checking */ + const char *errOut; /* string to use for testing exit code */ + char commentChar; /* character used by shell for comment lines */ + + /* + * command-line flags + */ + const char *echo; /* echo commands */ + const char *exit; /* exit on error */ +} Shell; + +extern const char *shellPath; +extern const char *shellName; + +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 Shell_Init(void); +void Job_Touch(GNode *, Boolean); +Boolean Job_CheckCommands(GNode *, void (*abortProc )(const char *, ...)); +void Job_CatchChildren(Boolean); +void Job_CatchOutput(void); +void Job_Make(GNode *); +void Job_Init(int, int); +Boolean Job_Full(void); +Boolean Job_Empty(void); +ReturnStatus Job_ParseShell(char *); +int Job_Finish(void); +void Job_End(void); +void Job_Wait(void); +void Job_AbortAll(void); +void JobFlagForMigration(int); +void Job_TokenReturn(void); +void Job_TokenFlush(void); +Boolean Job_TokenWithdraw(void); +void Job_ServerStart(int); + +#endif /* _JOB_H_ */ diff --git a/devel/bmake/files/lst.h b/devel/bmake/files/lst.h new file mode 100644 index 0000000000000..223d159703e43 --- /dev/null +++ b/devel/bmake/files/lst.h @@ -0,0 +1,198 @@ +/* $NetBSD: lst.h,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 + */ + +/* + * 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 +#include + +#include "sprite.h" + +/* + * basic typedef. This is what the Lst_ functions handle + */ + +typedef struct Lst *Lst; +typedef struct LstNode *LstNode; + +typedef ClientData DuplicateProc(ClientData); +typedef void FreeProc(ClientData); + +#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 ((FreeProc *)NULL) +#define NOCOPY ((DuplicateProc *)NULL) + +#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(Boolean); +/* Duplicate an existing list */ +Lst Lst_Duplicate(Lst, DuplicateProc *); +/* Destroy an old one */ +void Lst_Destroy(Lst, FreeProc *); +/* True if list is empty */ +Boolean Lst_IsEmpty(Lst); + +/* + * Functions to modify a list + */ +/* Insert an element before another */ +ReturnStatus Lst_Insert(Lst, LstNode, ClientData); +/* Insert an element after another */ +ReturnStatus Lst_Append(Lst, LstNode, ClientData); +/* Place an element at the front of a lst. */ +ReturnStatus Lst_AtFront(Lst, ClientData); +/* Place an element at the end of a lst. */ +ReturnStatus Lst_AtEnd(Lst, ClientData); +/* Remove an element */ +ReturnStatus Lst_Remove(Lst, LstNode); +/* Replace a node with a new value */ +ReturnStatus Lst_Replace(LstNode, ClientData); +/* Concatenate two lists */ +ReturnStatus Lst_Concat(Lst, Lst, int); + +/* + * Node-specific functions + */ +/* Return first element in list */ +LstNode Lst_First(Lst); +/* Return last element in list */ +LstNode Lst_Last(Lst); +/* Return successor to given element */ +LstNode Lst_Succ(LstNode); +/* Get datum from LstNode */ +ClientData Lst_Datum(LstNode); + +/* + * Functions for entire lists + */ +/* Find an element in a list */ +LstNode Lst_Find(Lst, ClientData, int (*)(ClientData, ClientData)); +/* Find an element starting from somewhere */ +LstNode Lst_FindFrom(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(Lst, ClientData); +/* Apply a function to all elements of a lst */ +void Lst_ForEach(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(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(Lst); +/* Next element please */ +LstNode Lst_Next(Lst); +/* Done yet? */ +Boolean Lst_IsAtEnd(Lst); +/* Finish table access */ +void Lst_Close(Lst); + +/* + * for using the list as a queue + */ +/* Place an element at tail of queue */ +ReturnStatus Lst_EnQueue(Lst, ClientData); +/* Remove an element from head of queue */ +ClientData Lst_DeQueue(Lst); + +#endif /* _LST_H_ */ diff --git a/devel/bmake/files/lst.lib/Makefile b/devel/bmake/files/lst.lib/Makefile new file mode 100644 index 0000000000000..eec86e6665b99 --- /dev/null +++ b/devel/bmake/files/lst.lib/Makefile @@ -0,0 +1,10 @@ +# $NetBSD: Makefile,v 1.1.1.1 2005/12/02 00:03:00 sjg 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/devel/bmake/files/lst.lib/lstAppend.c b/devel/bmake/files/lst.lib/lstAppend.c new file mode 100644 index 0000000000000..f033c14a79a43 --- /dev/null +++ b/devel/bmake/files/lst.lib/lstAppend.c @@ -0,0 +1,122 @@ +/* $NetBSD: lstAppend.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstAppend.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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 2005/12/02 00:03:00 sjg 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. + * + * Input: + * l affected list + * ln node after which to append the datum + * d said datum + * + * 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(Lst l, LstNode ln, ClientData d) +{ + List list; + ListNode lNode; + 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/devel/bmake/files/lst.lib/lstAtEnd.c b/devel/bmake/files/lst.lib/lstAtEnd.c new file mode 100644 index 0000000000000..8f870caa64daf --- /dev/null +++ b/devel/bmake/files/lst.lib/lstAtEnd.c @@ -0,0 +1,79 @@ +/* $NetBSD: lstAtEnd.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstAtEnd.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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 2005/12/02 00:03:00 sjg 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 + * + * Input: + * l List to which to add the datum + * d Datum to add + * + * Results: + * SUCCESS if life is good. + * + * Side Effects: + * A new ListNode is created and added to the list. + * + *----------------------------------------------------------------------- + */ +ReturnStatus +Lst_AtEnd(Lst l, ClientData d) +{ + LstNode end; + + end = Lst_Last(l); + return (Lst_Append(l, end, d)); +} diff --git a/devel/bmake/files/lst.lib/lstAtFront.c b/devel/bmake/files/lst.lib/lstAtFront.c new file mode 100644 index 0000000000000..58abff7ad407e --- /dev/null +++ b/devel/bmake/files/lst.lib/lstAtFront.c @@ -0,0 +1,76 @@ +/* $NetBSD: lstAtFront.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstAtFront.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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 2005/12/02 00:03:00 sjg 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(Lst l, ClientData d) +{ + LstNode front; + + front = Lst_First(l); + return (Lst_Insert(l, front, d)); +} diff --git a/devel/bmake/files/lst.lib/lstClose.c b/devel/bmake/files/lst.lib/lstClose.c new file mode 100644 index 0000000000000..2e58dfa234d8e --- /dev/null +++ b/devel/bmake/files/lst.lib/lstClose.c @@ -0,0 +1,86 @@ +/* $NetBSD: lstClose.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstClose.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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 2005/12/02 00:03:00 sjg 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. + * + * Input: + * l The list to close + * + * Results: + * None. + * + * Side Effects: + * The list is closed. + * + *----------------------------------------------------------------------- + */ +void +Lst_Close(Lst l) +{ + List list = (List) l; + + if (LstValid(l) == TRUE) { + list->isOpen = FALSE; + list->atEnd = Unknown; + } +} + diff --git a/devel/bmake/files/lst.lib/lstConcat.c b/devel/bmake/files/lst.lib/lstConcat.c new file mode 100644 index 0000000000000..1561148f64c5f --- /dev/null +++ b/devel/bmake/files/lst.lib/lstConcat.c @@ -0,0 +1,185 @@ +/* $NetBSD: lstConcat.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstConcat.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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 2005/12/02 00:03:00 sjg 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. + * + * Input: + * l1 The list to which l2 is to be appended + * l2 The list to append to l1 + * flags LST_CONCNEW if LstNode's should be duplicated + * LST_CONCLINK if should just be relinked + * + * Results: + * SUCCESS if all went well. FAILURE otherwise. + * + * Side Effects: + * New elements are created and appended the first list. + *----------------------------------------------------------------------- + */ +ReturnStatus +Lst_Concat(Lst l1, Lst l2, int flags) +{ + ListNode ln; /* original LstNode */ + ListNode nln; /* new LstNode */ + ListNode last; /* the last element in the list. Keeps + * bookkeeping until the end */ + List list1 = (List)l1; + 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(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/devel/bmake/files/lst.lib/lstDatum.c b/devel/bmake/files/lst.lib/lstDatum.c new file mode 100644 index 0000000000000..697766788f560 --- /dev/null +++ b/devel/bmake/files/lst.lib/lstDatum.c @@ -0,0 +1,77 @@ +/* $NetBSD: lstDatum.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstDatum.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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 2005/12/02 00:03:00 sjg 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(LstNode ln) +{ + if (ln != NILLNODE) { + return (((ListNode)ln)->datum); + } else { + return ((ClientData) NIL); + } +} + diff --git a/devel/bmake/files/lst.lib/lstDeQueue.c b/devel/bmake/files/lst.lib/lstDeQueue.c new file mode 100644 index 0000000000000..e506806ef2150 --- /dev/null +++ b/devel/bmake/files/lst.lib/lstDeQueue.c @@ -0,0 +1,87 @@ +/* $NetBSD: lstDeQueue.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstDeQueue.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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 2005/12/02 00:03:00 sjg 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(Lst l) +{ + ClientData rd; + 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/devel/bmake/files/lst.lib/lstDestroy.c b/devel/bmake/files/lst.lib/lstDestroy.c new file mode 100644 index 0000000000000..1152a9fcc836d --- /dev/null +++ b/devel/bmake/files/lst.lib/lstDestroy.c @@ -0,0 +1,107 @@ +/* $NetBSD: lstDestroy.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstDestroy.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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 2005/12/02 00:03:00 sjg 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(Lst l, FreeProc *freeProc) +{ + ListNode ln; + ListNode tln = NilListNode; + 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(l); + return; + } + + if (freeProc) { + for (ln = list->firstPtr; ln != NilListNode; ln = tln) { + tln = ln->nextPtr; + freeProc(ln->datum); + free(ln); + } + } else { + for (ln = list->firstPtr; ln != NilListNode; ln = tln) { + tln = ln->nextPtr; + free(ln); + } + } + + free(l); +} diff --git a/devel/bmake/files/lst.lib/lstDupl.c b/devel/bmake/files/lst.lib/lstDupl.c new file mode 100644 index 0000000000000..14a5dd8509733 --- /dev/null +++ b/devel/bmake/files/lst.lib/lstDupl.c @@ -0,0 +1,107 @@ +/* $NetBSD: lstDupl.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstDupl.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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 2005/12/02 00:03:00 sjg 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. + * + * Input: + * l the list to duplicate + * copyProc A function to duplicate each ClientData + * + * Results: + * The new Lst structure or NILLST if failure. + * + * Side Effects: + * A new list is created. + *----------------------------------------------------------------------- + */ +Lst +Lst_Duplicate(Lst l, DuplicateProc *copyProc) +{ + Lst nl; + ListNode ln; + 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/devel/bmake/files/lst.lib/lstEnQueue.c b/devel/bmake/files/lst.lib/lstEnQueue.c new file mode 100644 index 0000000000000..0ecf5b368f01e --- /dev/null +++ b/devel/bmake/files/lst.lib/lstEnQueue.c @@ -0,0 +1,78 @@ +/* $NetBSD: lstEnQueue.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstEnQueue.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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 2005/12/02 00:03:00 sjg 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(Lst l, ClientData d) +{ + if (LstValid (l) == FALSE) { + return (FAILURE); + } + + return (Lst_Append(l, Lst_Last(l), d)); +} + diff --git a/devel/bmake/files/lst.lib/lstFind.c b/devel/bmake/files/lst.lib/lstFind.c new file mode 100644 index 0000000000000..e45aeb5f7e221 --- /dev/null +++ b/devel/bmake/files/lst.lib/lstFind.c @@ -0,0 +1,74 @@ +/* $NetBSD: lstFind.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstFind.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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 2005/12/02 00:03:00 sjg 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(Lst l, ClientData d, int (*cProc)(ClientData, ClientData)) +{ + return (Lst_FindFrom(l, Lst_First(l), d, cProc)); +} + diff --git a/devel/bmake/files/lst.lib/lstFindFrom.c b/devel/bmake/files/lst.lib/lstFindFrom.c new file mode 100644 index 0000000000000..c0fe531e98e4e --- /dev/null +++ b/devel/bmake/files/lst.lib/lstFindFrom.c @@ -0,0 +1,98 @@ +/* $NetBSD: lstFindFrom.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstFindFrom.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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 2005/12/02 00:03:00 sjg 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(Lst l, LstNode ln, ClientData d, + int (*cProc)(ClientData, ClientData)) +{ + 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/devel/bmake/files/lst.lib/lstFirst.c b/devel/bmake/files/lst.lib/lstFirst.c new file mode 100644 index 0000000000000..14d1a59e535c4 --- /dev/null +++ b/devel/bmake/files/lst.lib/lstFirst.c @@ -0,0 +1,77 @@ +/* $NetBSD: lstFirst.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstFirst.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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 2005/12/02 00:03:00 sjg 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(Lst l) +{ + if (!LstValid (l) || LstIsEmpty (l)) { + return (NILLNODE); + } else { + return ((LstNode)((List)l)->firstPtr); + } +} + diff --git a/devel/bmake/files/lst.lib/lstForEach.c b/devel/bmake/files/lst.lib/lstForEach.c new file mode 100644 index 0000000000000..92be4a7388c40 --- /dev/null +++ b/devel/bmake/files/lst.lib/lstForEach.c @@ -0,0 +1,76 @@ +/* $NetBSD: lstForEach.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstForEach.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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 2005/12/02 00:03:00 sjg 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(Lst l, int (*proc)(ClientData, ClientData), ClientData d) +{ + Lst_ForEachFrom(l, Lst_First(l), proc, d); +} + diff --git a/devel/bmake/files/lst.lib/lstForEachFrom.c b/devel/bmake/files/lst.lib/lstForEachFrom.c new file mode 100644 index 0000000000000..59fba8b53caea --- /dev/null +++ b/devel/bmake/files/lst.lib/lstForEachFrom.c @@ -0,0 +1,124 @@ +/* $NetBSD: lstForEachFrom.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstForEachFrom.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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 2005/12/02 00:03:00 sjg 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(Lst l, LstNode ln, int (*proc)(ClientData, ClientData), + ClientData d) +{ + ListNode tln = (ListNode)ln; + List list = (List)l; + 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; + + /* + * We're done with the traversal if + * - the next node to examine is the first in the queue or + * doesn't exist and + * - nothing's been added after the current node (check this + * after proc() has been called). + */ + done = (next == NilListNode || next == list->firstPtr); + + (void) tln->useCount++; + result = (*proc) (tln->datum, d); + (void) tln->useCount--; + + /* + * Now check whether a node has been added. + * Note: this doesn't work if this node was deleted before + * the new node was added. + */ + if (next != tln->nextPtr) { + next = tln->nextPtr; + done = 0; + } + + if (tln->flags & LN_DELETED) { + free((char *)tln); + } + tln = next; + } while (!result && !LstIsEmpty(list) && !done); + +} + diff --git a/devel/bmake/files/lst.lib/lstInit.c b/devel/bmake/files/lst.lib/lstInit.c new file mode 100644 index 0000000000000..d63642a35b4d2 --- /dev/null +++ b/devel/bmake/files/lst.lib/lstInit.c @@ -0,0 +1,85 @@ +/* $NetBSD: lstInit.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstInit.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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 2005/12/02 00:03:00 sjg Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * init.c -- + * Initialize a new linked list. + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Init -- + * Create and initialize a new list. + * + * Input: + * circ TRUE if the list should be made circular + * + * Results: + * The created list. + * + * Side Effects: + * A list is created, what else? + * + *----------------------------------------------------------------------- + */ +Lst +Lst_Init(Boolean circ) +{ + 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/devel/bmake/files/lst.lib/lstInsert.c b/devel/bmake/files/lst.lib/lstInsert.c new file mode 100644 index 0000000000000..a63bbb4de70a1 --- /dev/null +++ b/devel/bmake/files/lst.lib/lstInsert.c @@ -0,0 +1,122 @@ +/* $NetBSD: lstInsert.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstInsert.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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 2005/12/02 00:03:00 sjg 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. + * + * Input: + * l list to manipulate + * ln node before which to insert d + * d datum to be inserted + * + * Results: + * SUCCESS or FAILURE. + * + * Side Effects: + * the firstPtr field will be changed if ln is the first node in the + * list. + * + *----------------------------------------------------------------------- + */ +ReturnStatus +Lst_Insert(Lst l, LstNode ln, ClientData d) +{ + ListNode nLNode; /* new lnode for d */ + ListNode lNode = (ListNode)ln; + 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/devel/bmake/files/lst.lib/lstInt.h b/devel/bmake/files/lst.lib/lstInt.h new file mode 100644 index 0000000000000..69ac2e1b3db7a --- /dev/null +++ b/devel/bmake/files/lst.lib/lstInt.h @@ -0,0 +1,109 @@ +/* $NetBSD: lstInt.h,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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/devel/bmake/files/lst.lib/lstIsAtEnd.c b/devel/bmake/files/lst.lib/lstIsAtEnd.c new file mode 100644 index 0000000000000..385fd82f15298 --- /dev/null +++ b/devel/bmake/files/lst.lib/lstIsAtEnd.c @@ -0,0 +1,87 @@ +/* $NetBSD: lstIsAtEnd.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstIsAtEnd.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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 2005/12/02 00:03:00 sjg 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(Lst l) +{ + List list = (List) l; + + return (!LstValid (l) || !list->isOpen || + (list->atEnd == Head) || (list->atEnd == Tail)); +} + diff --git a/devel/bmake/files/lst.lib/lstIsEmpty.c b/devel/bmake/files/lst.lib/lstIsEmpty.c new file mode 100644 index 0000000000000..87aad7ae4e5e7 --- /dev/null +++ b/devel/bmake/files/lst.lib/lstIsEmpty.c @@ -0,0 +1,75 @@ +/* $NetBSD: lstIsEmpty.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstIsEmpty.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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 2005/12/02 00:03:00 sjg 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(Lst l) +{ + return ( ! LstValid (l) || LstIsEmpty(l)); +} + diff --git a/devel/bmake/files/lst.lib/lstLast.c b/devel/bmake/files/lst.lib/lstLast.c new file mode 100644 index 0000000000000..fc88aa5285140 --- /dev/null +++ b/devel/bmake/files/lst.lib/lstLast.c @@ -0,0 +1,77 @@ +/* $NetBSD: lstLast.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstLast.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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 2005/12/02 00:03:00 sjg 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(Lst l) +{ + if (!LstValid(l) || LstIsEmpty (l)) { + return (NILLNODE); + } else { + return ((LstNode)((List)l)->lastPtr); + } +} + diff --git a/devel/bmake/files/lst.lib/lstMember.c b/devel/bmake/files/lst.lib/lstMember.c new file mode 100644 index 0000000000000..8356e3b4c21f8 --- /dev/null +++ b/devel/bmake/files/lst.lib/lstMember.c @@ -0,0 +1,74 @@ +/* $NetBSD: lstMember.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstMember.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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 2005/12/02 00:03:00 sjg Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * lstMember.c -- + * See if a given datum is on a given list. + */ + +#include "lstInt.h" + +LstNode +Lst_Member(Lst l, ClientData d) +{ + List list = (List) l; + 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/devel/bmake/files/lst.lib/lstNext.c b/devel/bmake/files/lst.lib/lstNext.c new file mode 100644 index 0000000000000..e20ade15ab44b --- /dev/null +++ b/devel/bmake/files/lst.lib/lstNext.c @@ -0,0 +1,120 @@ +/* $NetBSD: lstNext.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstNext.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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 2005/12/02 00:03:00 sjg 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(Lst l) +{ + ListNode tln; + 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/devel/bmake/files/lst.lib/lstOpen.c b/devel/bmake/files/lst.lib/lstOpen.c new file mode 100644 index 0000000000000..ee0b3e390e8ef --- /dev/null +++ b/devel/bmake/files/lst.lib/lstOpen.c @@ -0,0 +1,87 @@ +/* $NetBSD: lstOpen.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstOpen.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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 2005/12/02 00:03:00 sjg 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(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/devel/bmake/files/lst.lib/lstRemove.c b/devel/bmake/files/lst.lib/lstRemove.c new file mode 100644 index 0000000000000..8a0a14cb811f9 --- /dev/null +++ b/devel/bmake/files/lst.lib/lstRemove.c @@ -0,0 +1,136 @@ +/* $NetBSD: lstRemove.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstRemove.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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 2005/12/02 00:03:00 sjg 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(Lst l, LstNode ln) +{ + List list = (List) l; + 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(ln); + } else { + lNode->flags |= LN_DELETED; + } + + return (SUCCESS); +} + diff --git a/devel/bmake/files/lst.lib/lstReplace.c b/devel/bmake/files/lst.lib/lstReplace.c new file mode 100644 index 0000000000000..a728145d03f7a --- /dev/null +++ b/devel/bmake/files/lst.lib/lstReplace.c @@ -0,0 +1,78 @@ +/* $NetBSD: lstReplace.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstReplace.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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 2005/12/02 00:03:00 sjg 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(LstNode ln, ClientData d) +{ + if (ln == NILLNODE) { + return (FAILURE); + } else { + ((ListNode) ln)->datum = d; + return (SUCCESS); + } +} + diff --git a/devel/bmake/files/lst.lib/lstSucc.c b/devel/bmake/files/lst.lib/lstSucc.c new file mode 100644 index 0000000000000..6d668a0f42ba8 --- /dev/null +++ b/devel/bmake/files/lst.lib/lstSucc.c @@ -0,0 +1,79 @@ +/* $NetBSD: lstSucc.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstSucc.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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 2005/12/02 00:03:00 sjg Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstSucc.c -- + * return the successor to a given node + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Succ -- + * Return the successor 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(LstNode ln) +{ + if (ln == NILLNODE) { + return (NILLNODE); + } else { + return ((LstNode) ((ListNode) ln)->nextPtr); + } +} + diff --git a/devel/bmake/files/lst.lib/makefile.boot.in b/devel/bmake/files/lst.lib/makefile.boot.in new file mode 100644 index 0000000000000..8d55f920f0037 --- /dev/null +++ b/devel/bmake/files/lst.lib/makefile.boot.in @@ -0,0 +1,44 @@ +# RCSid: +# $Id: makefile.boot.in,v 1.1.1.1 2005/12/02 00:03:00 sjg 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} + +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/devel/bmake/files/machine.sh b/devel/bmake/files/machine.sh new file mode 100755 index 0000000000000..76698e34bd6f8 --- /dev/null +++ b/devel/bmake/files/machine.sh @@ -0,0 +1,94 @@ +: +# derrived from /etc/rc_d/os.sh + +# RCSid: +# $Id: machine.sh,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $ +# +# @(#) Copyright (c) 1994-2002 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@crufty.net +# + +OS=`uname` +OSREL=`uname -r` +OSMAJOR=`IFS=.; set $OSREL; echo $1` +machine=`uname -p 2>/dev/null || uname -m` +MACHINE= + +case "$machine" in +unknown) + machine=`uname -m` + ;; +esac + +# Great! Solaris keeps moving arch(1) +# we need this here, and it is not always available... +Which() { + # some shells cannot correctly handle `IFS` + # in conjunction with the for loop. + _dirs=`IFS=:; echo ${2:-$PATH}` + for d in $_dirs + do + test -x $d/$1 && { echo $d/$1; break; } + done +} + +case $OS in +OpenBSD) + MACHINE=$OS$OSMAJOR.$machine + arch=`Which arch /usr/bin:/usr/ucb:$PATH` + MACHINE_ARCH=`$arch -s`; + ;; +*BSD) + MACHINE=$OS$OSMAJOR.$machine + ;; +SunOS) + arch=`Which arch /usr/bin:/usr/ucb:$PATH` + test "$arch" && machine_arch=`$arch` + + case "$OSREL" in + 4.0*) MACHINE_ARCH=$machine_arch MACHINE=$machine_arch;; + 4*) MACHINE_ARCH=$machine_arch;; + esac + ;; +HP-UX) + MACHINE_ARCH=`IFS="/-."; set $machine; echo $1` + ;; +Interix) + MACHINE=i386 + MACHINE_ARCH=i386 + ;; +UnixWare) + OSREL=`uname -v` + OSMAJOR=`IFS=.; set $OSREL; echo $1` + MACHINE_ARCH=`uname -m` + ;; +Linux) + case "$machine" in + i?86) MACHINE_ARCH=i386;;# does anyone really care about 686 vs 586? + esac + ;; +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/devel/bmake/files/main.c b/devel/bmake/files/main.c new file mode 100644 index 0000000000000..e270d43544326 --- /dev/null +++ b/devel/bmake/files/main.c @@ -0,0 +1,1822 @@ +/* $NetBSD: main.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: main.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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 2005/12/02 00:03:00 sjg Exp $"); +#endif +#endif /* not lint */ +#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 +#ifdef MAKE_NATIVE +#include +#endif +#include "wait.h" + +#include +#include +#include +#include +#include +#include + +#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 */ +int debug; /* -d argument */ +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 */ +Boolean varNoExportEnv; /* -X 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(int, char **, int); +#endif +static void MainParseArgs(int, char **); +static int ReadMakefile(ClientData, ClientData); +static void usage(void); + +static char curdir[MAXPATHLEN + 1]; /* startup directory */ +static char objdir[MAXPATHLEN + 1]; /* 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(int argc, char **argv) +{ + char *p; + int c; + int arginc; + char *argvalue; + const char *getopt_def; + char *optscan; + Boolean inOption, dashDash = FALSE; + char found_path[MAXPATHLEN + 1]; /* for searching for sys.mk */ + +#ifdef REMOTE +# define OPTFLAGS "BD:I:J:L:NPST:V:WXd:ef:ij:km:nqrst" +#else +# define OPTFLAGS "BD:I:J:NPST:V:WXd:ef:ij:km:nqrst" +#endif +#undef optarg +#define optarg argvalue +/* Can't actually use getopt(3) because rescanning is not portable */ + + getopt_def = OPTFLAGS; +rearg: + inOption = FALSE; + optscan = NULL; + while(argc > 1) { + char *getopt_spec; + if(!inOption) + optscan = argv[1]; + c = *optscan++; + arginc = 0; + if(inOption) { + if(c == '\0') { + ++argv; + --argc; + inOption = FALSE; + continue; + } + } else { + if (c != '-' || dashDash) + break; + inOption = TRUE; + c = *optscan++; + } + /* '-' found at some earlier point */ + getopt_spec = strchr(getopt_def, c); + if(c != '\0' && getopt_spec != NULL && getopt_spec[1] == ':') { + /* - found, and should have an arg */ + inOption = FALSE; + arginc = 1; + argvalue = optscan; + if(*argvalue == '\0') { + if (argc < 3) { + (void)fprintf(stderr, + "%s: option requires " + "an argument -- %c\n", + progname, c); + usage(); + } + argvalue = argv[2]; + arginc = 2; + } + } else { + argvalue = NULL; + } + switch(c) { + case '\0': + arginc = 1; + inOption = FALSE; + break; + case 'B': + compatMake = TRUE; + Var_Append(MAKEFLAGS, "-B", VAR_GLOBAL); + break; + 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; +#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 'V': + printVars = TRUE; + (void)Lst_AtEnd(variables, (ClientData)optarg); + Var_Append(MAKEFLAGS, "-V", VAR_GLOBAL); + Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL); + break; + case 'W': + parseWarnFatal = TRUE; + break; + case 'X': + varNoExportEnv = TRUE; + Var_Append(MAKEFLAGS, "-X", VAR_GLOBAL); + 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 'e': + debug |= DEBUG_ERROR; + 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; + } + else if (modules[1] == '3') { + debug |= DEBUG_GRAPH3; + ++modules; + } + break; + case 'j': + debug |= DEBUG_JOB; + break; + case 'm': + debug |= DEBUG_MAKE; + break; + case 'n': + debug |= DEBUG_SCRIPT; + 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': + /* look for magic parent directory search string */ + if (strncmp(".../", optarg, 4) == 0) { + if (!Dir_FindHereOrAbove(curdir, optarg+4, + found_path, sizeof(found_path))) + break; /* nothing doing */ + (void)Dir_AddDir(sysIncPath, found_path); + + } else { + (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; + case '-': + dashDash = TRUE; + break; + default: + case '?': +#ifndef MAKE_NATIVE + fprintf(stderr, "getopt(%s) -> %d (%c)\n", + OPTFLAGS, c, c); +#endif + usage(); + } + argv += arginc; + argc -= arginc; + } + + 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 (; argc > 1; ++argv, --argc) + if (Parse_IsVar(argv[1])) { + Parse_DoVar(argv[1], VAR_CMD); + } else { + if (!*argv[1]) + Punt("illegal (null) argument."); + if (*argv[1] == '-' && !dashDash) + goto rearg; + (void)Lst_AtEnd(create, (ClientData)estrdup(argv[1])); + } +} + +/*- + * 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. + * + * Input: + * line Line to fracture + * + * Results: + * None + * + * Side Effects: + * Only those that come from the various arguments. + */ +void +Main_ParseArgLine(char *line) +{ + 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); +} + +Boolean +Main_SetObjdir(const char *path) +{ + struct stat sb; + char *p = NULL; + char buf[MAXPATHLEN + 1]; + Boolean rc = FALSE; + + /* expand variable substitutions */ + if (strchr(path, '$') != 0) { + snprintf(buf, MAXPATHLEN, "%s", path); + path = p = Var_Subst(NULL, buf, VAR_GLOBAL, 0); + } + + if (path[0] != '/') { + snprintf(buf, MAXPATHLEN, "%s/%s", curdir, path); + path = buf; + } + + /* look for the directory and try to chdir there */ + if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) { + if (chdir(path)) { + (void)fprintf(stderr, "make warning: %s: %s.\n", + path, strerror(errno)); + } else { + strncpy(objdir, path, MAXPATHLEN); + Var_Set(".OBJDIR", objdir, VAR_GLOBAL, 0); + setenv("PWD", objdir, 1); + Dir_InitDot(); + rc = TRUE; + } + } + + if (p) + free(p); + return rc; +} + + +/*- + * 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(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, *pwd; + char mdpath[MAXPATHLEN]; +#ifdef FORCE_MACHINE + char *machine = FORCE_MACHINE; +#else + char *machine = getenv("MACHINE"); +#endif + const 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; + char found_path[MAXPATHLEN + 1]; /* for searching for sys.mk */ + struct timeval rightnow; /* to initialize random seed */ + + /* + * Set the seed to produce a different random sequences + * on each program execution. + */ + gettimeofday(&rightnow, NULL); + srandom(rightnow.tv_sec + rightnow.tv_usec); + + 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. + */ + 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 or MAKEOBJDIR contains a transform. + */ +#ifndef NO_PWD_OVERRIDE + if ((pwd = getenv("PWD")) != NULL && getenv("MAKEOBJDIRPREFIX") == NULL) { + const char *makeobjdir = getenv("MAKEOBJDIR"); + + if (makeobjdir == NULL || !strchr(makeobjdir, '$')) { + if (stat(pwd, &sb) == 0 && sa.st_ino == sb.st_ino && + sa.st_dev == sb.st_dev) + (void)strncpy(curdir, pwd, MAXPATHLEN); + } + } +#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) { +#ifdef MAKE_NATIVE + struct utsname utsname; + + if (uname(&utsname) == -1) { + (void)fprintf(stderr, "%s: uname failed (%s).\n", progname, + strerror(errno)); + exit(2); + } + machine = utsname.machine; +#else +#ifdef MAKE_MACHINE + machine = MAKE_MACHINE; +#else + machine = "unknown"; +#endif +#endif + } + + if (!machine_arch) { +#ifndef MACHINE_ARCH +#ifdef MAKE_MACHINE_ARCH + machine_arch = MAKE_MACHINE_ARCH; +#else + machine_arch = "unknown"; +#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 */ + + /* + * Find the .OBJDIR. If MAKEOBJDIRPREFIX, or failing that, + * MAKEOBJDIR is set in the environment, try only that value + * and fall back to .CURDIR if it does not exist. + * + * Otherwise, try _PATH_OBJDIR.MACHINE, _PATH_OBJDIR, and + * finally _PATH_OBJDIRPREFIX`pwd`, in that order. If none + * of these paths exist, just use .CURDIR. + */ + Dir_Init(curdir); + (void)Main_SetObjdir(curdir); + + if ((path = getenv("MAKEOBJDIRPREFIX")) != NULL) { + (void)snprintf(mdpath, MAXPATHLEN, "%s%s", path, curdir); + (void)Main_SetObjdir(mdpath); + } else if ((path = getenv("MAKEOBJDIR")) != NULL) { + (void)Main_SetObjdir(path); + } else { + (void)snprintf(mdpath, MAXPATHLEN, "%s.%s", _PATH_OBJDIR, machine); + if (!Main_SetObjdir(mdpath) && !Main_SetObjdir(_PATH_OBJDIR)) { + (void)snprintf(mdpath, MAXPATHLEN, "%s%s", + _PATH_OBJDIRPREFIX, curdir); + (void)Main_SetObjdir(mdpath); + } + } + + 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 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. + */ + Parse_Init(); + 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); + Var_Set(".ALLTARGETS", "", 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 (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 == ':') { + *cp++ = '\0'; + } + /* look for magic parent directory search string */ + if (strncmp(".../", start, 4) != 0) { + (void)Dir_AddDir(defIncPath, start); + } else { + if (Dir_FindHereOrAbove(curdir, start+4, + found_path, sizeof(found_path))) { + (void)Dir_AddDir(defIncPath, found_path); + } + } + } + if (syspath != defsyspath) + free(syspath); + + /* + * Read in the built-in rules first, followed by the specified + * makefile, if it was (makefile != 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, + Lst_IsEmpty(sysIncPath) ? defIncPath : 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(UNCONST("makefile"), NULL)) + (void)ReadMakefile(UNCONST("Makefile"), NULL); + + (void)ReadMakefile(UNCONST(".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); + + Main_ExportMAKEFLAGS(TRUE); /* 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, 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 == ':'); + free(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 *var = (char *)Lst_Datum(ln); + char *value; + + if (strchr(var, '$')) { + value = p1 = Var_Subst(NULL, var, VAR_GLOBAL, 0); + } else { + value = Var_Value(var, 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, (FreeProc *)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(ClientData p, ClientData q __unused) +{ + 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 { +#ifdef __INTERIX + /* + * XXX Hack from tv: + * This system has broken filesystem support - can't + * always distinguish b/w [Mm]akefile. + */ + setMAKEFILE = FALSE; +#else + setMAKEFILE = strcmp(fname, ".depend"); +#endif + + /* if we've chdir'd, rebuild the path name */ + if (strcmp(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; + } + + /* If curdir failed, try objdir (ala .depend) */ + plen = strlen(objdir) + strlen(fname) + 2; + if (len < plen) + path = erealloc(path, len = 2 * plen); + (void)snprintf(path, len, "%s/%s", objdir, 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, + Lst_IsEmpty(sysIncPath) ? defIncPath : 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(int ac, char **av, int copy) +{ + static char *make[4]; + static char *cur_dir = NULL; + char **mp; + char *cp; + 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; + cur_dir = 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, cur_dir); +#endif + return cur_dir; + } + } + } +#ifdef check_cwd_debug + fprintf(stderr, "\n"); +#endif + } + return NULL; +} + +char * +Check_Cwd_Cmd(const char *cmd) +{ + char *cp, *bp; + char **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(const 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, UNCONST(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(const char *cmd, const char **err) +{ + const 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 */ + WAIT_T status; /* command exit status */ + Buffer buf; /* buffer to store the result */ + char *cp; + int cc; + + + *err = NULL; + + if (!shellName) + Shell_Init(); + /* + * Set up arguments for shell + */ + args[0] = shellName; + 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(shellPath, UNCONST(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 (WAIT_STATUS(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 +Error(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + 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 +Fatal(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + 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) || DEBUG(GRAPH3)) + 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 +Punt(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + (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(void) +{ + 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(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(size_t len) +{ + void *p; + + if ((p = malloc(len)) == NULL) + enomem(); + return(p); +} + +/* + * estrdup -- + * strdup, but die on error. + */ +char * +estrdup(const char *str) +{ + char *p; + + if ((p = strdup(str)) == NULL) + enomem(); + return(p); +} + +/* + * erealloc -- + * realloc, but die on error. + */ +void * +erealloc(void *ptr, size_t size) +{ + if ((ptr = realloc(ptr, size)) == NULL) + enomem(); + return(ptr); +} + +/* + * enomem -- + * die when out of memory. + */ +void +enomem(void) +{ + (void)fprintf(stderr, "%s: %s.\n", progname, strerror(errno)); + exit(2); +} + +/* + * enunlink -- + * Remove a file carefully, avoiding directories. + */ +int +eunlink(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(const char *af, const char *av) +{ +#ifdef USE_IOVEC + int i = 0; + struct iovec iov[8]; +#define IOADD(s) \ + (void)(iov[i].iov_base = UNCONST(s), \ + iov[i].iov_len = strlen(iov[i].iov_base), \ + i++) +#else +#define IOADD(void)write(2, s, strlen(s)) +#endif + + IOADD(progname); + IOADD(": "); + IOADD(af); + IOADD("("); + IOADD(av); + IOADD(") failed ("); + IOADD(strerror(errno)); + IOADD(")\n"); + +#ifdef USE_IOVEC + (void)writev(2, iov, 8); +#endif +} + +/* + * usage -- + * exit with usage message + */ +static void +usage(void) +{ + (void)fprintf(stderr, +"usage: %s [-BeikNnqrstWX] [-D variable] [-d flags] [-f makefile]\n\ + [-I directory] [-J private] [-j max_jobs] [-m directory] [-T file]\n\ + [-V variable] [variable=value] [target ...]\n", progname); + exit(2); +} + + +int +PrintAddr(ClientData a, ClientData b) +{ + printf("%lx ", (unsigned long) a); + return b ? 0 : 0; +} + + + +void +PrintOnError(const 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 +Main_ExportMAKEFLAGS(Boolean 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/devel/bmake/files/make-conf.h b/devel/bmake/files/make-conf.h new file mode 100644 index 0000000000000..5fbb4f15594bd --- /dev/null +++ b/devel/bmake/files/make-conf.h @@ -0,0 +1,167 @@ +/* $NetBSD: make-conf.h,v 1.1.1.1 2005/12/02 00:02:59 sjg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. + * + * 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 + */ + +/* + * 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 + */ + +#ifndef DEFSHELL +#define DEFSHELL 1 /* Bourne shell */ +#endif + +/* + * 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(MAKE_NATIVE) && !defined(__ELF__) +# ifndef RANLIBMAG +# define RANLIBMAG "__.SYMDEF" +# endif +#endif diff --git a/devel/bmake/files/make.1 b/devel/bmake/files/make.1 new file mode 100644 index 0000000000000..f0ef8bedc0ba8 --- /dev/null +++ b/devel/bmake/files/make.1 @@ -0,0 +1,1608 @@ +.\" $NetBSD: make.1,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (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 June 27, 2005 +.Dt MAKE 1 +.Os +.Sh NAME +.Nm make +.Nd maintain program dependencies +.Sh SYNOPSIS +.Nm +.Op Fl BeikNnqrstWX +.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 private +.Ek +.Bk -words +.Op Fl j Ar max_jobs +.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 e +Print debugging information about failed commands and targets. +.It Ar f +Print debugging information about loop evaluation. +.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 "g3" +Print the input graph 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 n +Don't delete the temporary command scripts created in +.Pa /tmp +when running commands. +These are created via +.Xr mkstemp 3 +and have names of the form +.Pa /tmp/makeXXXXX . +.Em NOTE: +This can create many file in /tmp so use with care. +.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 environment variables override macro assignments within +makefiles. +.It Fl f Ar makefile +Specify a makefile to read instead of the default +.Ql Pa makefile . +If +.Ar makefile +is +.Ql Fl , +standard input is read. +Multiple makefiles 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 +.Ao Ar file Ac Ns -style +include statement. +The +.Fl m +option can be used multiple times 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 +.Qo Ar file Qc Ns -style +include statements (see the +.Fl I +option). +.Pp +If a file or directory name in the +.Fl m +argument (or the +.Ev MAKESYSPATH +environment variable) starts with the string +.Qq \&.../ +then +.Nm +will search for the specified file or directory named in the remaining part +of the argument string. +The search starts with the current directory of +the Makefile and then works upward towards the root of the filesystem. +If the search is successful, then the resulting directory replaces the +.Qq \&.../ +specification in the +.Fl m +argument. +If used, this feature allows +.Nm +to easily search in the current source tree for customized sys.mk files +(e.g., by using +.Qq \&.../mk/sys.mk +as an argument). +.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. +If +.Ar variable +contains a +.Ql \&$ +then the value will be expanded before printing. +.It Fl W +Treat any warnings during makefile parsing as errors. +.It Fl X +Don't export variables passed on the command line to the environment +individually. +Variables passed on the command line are still exported +via the +.Va MAKEFLAGS +environment variable. +This option may be useful on systems which have a small limit on the +size of command arguments. +.It Ar variable=value +Set the value of the variable +.Ar variable +to +.Ar value . +Normally, all values passed on the command line are also exported to +sub-makes in the environment. +The +.Fl X +flag disables this behavior. +Variable assignments should follow options for POSIX compatibility +but no ordering is enforced. +.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 characters of the command line are any combination of +.Ql Ic @ , +.Ql Ic + , +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 the command to be executed even when +.Fl n +is given. +This is similar to the effect of the .MAKE special source, +except that the effect can be limited to a single line of a script. +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. +.Ss Variable assignment modifiers +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. +.Ss Variable classes +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 \&\*[Gt] . +.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 \&\*[Lt] . +.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 \&\*[Lt] , +.Ql Va \&\*[Gt] , +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 "\*[Lt]F" , +.Ql Va "\*[Lt]D" , +.Ql Va "*F" , +and +.Ql Va "*D" +are permitted for compatibility with +.At V +makefiles and are not recommended. +.Pp +Four of the local variables may be used in sources on dependency lines +because they expand to the proper value for each target on the line. +These variables are +.Ql Va .TARGET , +.Ql Va .PREFIX , +.Ql Va .ARCHIVE , +and +.Ql Va .MEMBER . +.El +.Ss Additional inbuilt variables +In addition, +.Nm +sets or knows about the following variables: +.Bl -tag -width .MAKEOVERRIDES +.It Va \&$ +A single dollar sign +.Ql \&$ , +i.e. +.Ql \&$$ +expands to a single dollar +sign. +.It Va .ALLTARGETS +The list of all targets encountered in the Makefile. +If evaluated during +Makefile parsing, lists only those targets encountered thus far. +.It Va .CURDIR +A path to the directory where +.Nm +was executed. +Refer to the description of +.Ql Ev PWD +for more details. +.It Ev MAKE +The name that +.Nm +was executed with +.Pq Va argv[0] . +For compatibily +.Nm +also sets +.Va .MAKE +with the same value. +The preferred variable to use is the environment variable +.Ev MAKE +because it is more compatible with other versions of +.Nm +and cannot be confused with the special target with the same name. +.It Ev MAKEFLAGS +The environment variable +.Ql Ev MAKEFLAGS +may contain anything that +may be specified on +.Nm Ns 's +command line. +Anything specified on +.Nm Ns 's +command line is appended to the +.Ql Ev MAKEFLAGS +variable which is then +entered into the environment for all programs which +.Nm +executes. +.It Va .MAKEOVERRIDES +This variable is used to record the names of variables assigned to +on the command line, so that they may be exported as part of +.Ql Ev MAKEFLAGS . +This behaviour can be disabled by assigning an empty value to +.Ql Va .MAKEOVERRIDES +within a makefile. +Extra variables can be exported from a makefile +by appending their names to +.Ql Va .MAKEOVERRIDES . +.Ql Ev MAKEFLAGS +is re-exported whenever +.Ql Va .MAKEOVERRIDES +is modified. +.It Va MAKE_PRINT_VAR_ON_ERROR +When +.Nm +stops due to an error, it prints its name and the value of +.Ql Va .CURDIR +as well as the value of any variables named in +.Ql Va MAKE_PRINT_VAR_ON_ERROR . +.It Va .newline +This variable is simply assigned a newline character as its value. +This allows expansions using the +.Cm \&:@ +modifier to put a newline between +iterations of the loop rather than a space. +For example, the printing of +.Ql Va MAKE_PRINT_VAR_ON_ERROR +could be done as ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@}. +.It Va .OBJDIR +A path to the directory where the targets are built. +Its value is determined by trying to +.Xr chdir 2 +to the following directories in order and using the first match: +.Bl -enum +.It +.Ev ${MAKEOBJDIRPREFIX}${.CURDIR} +.Pp +(Only if +.Ql Ev MAKEOBJDIRPREFIX +is set in the environment or on the command line.) +.It +.Ev ${MAKEOBJDIR} +.Pp +(Only if +.Ql Ev MAKEOBJDIR +is set in the environment or on the command line.) +.It +.Ev ${.CURDIR} Ns Pa /obj. Ns Ev ${MACHINE} +.It +.Ev ${.CURDIR} Ns Pa /obj +.It +.Pa /usr/obj/ Ns Ev ${.CURDIR} +.It +.Ev ${.CURDIR} +.El +.Pp +Variable expansion is performed on the value before it's used, +so expressions such as +.Dl ${.CURDIR:C,^/usr/src,/var/obj,} +may be used. +.Pp +.Ql Va .OBJDIR +may be modified in the makefile as a global variable. +In all cases, +.Nm +will +.Xr chdir 2 +to +.Ql Va .OBJDIR +and set +.Ql Ev PWD +to that directory before executing any targets. +. +.It Va .PARSEDIR +A path to the directory of the current +.Ql Pa Makefile +being parsed. +.It Va .PARSEFILE +The basename of the current +.Ql Pa Makefile +being parsed. +This variable and +.Ql Va .PARSEDIR +are both set only while the +.Ql Pa Makefiles +are being parsed. +.It Va .PATH +A variable that represents the list of directories that +.Nm +will search for files. +The search list should be updated using the target +.Ql Va .PATH +rather than the variable. +.It Ev PWD +Alternate path to the current directory. +.Nm +normally sets +.Ql Va .CURDIR +to the canonical path given by +.Xr getcwd 3 . +However, if the environment variable +.Ql Ev PWD +is set and gives a path to the current directory, then +.Nm +sets +.Ql Va .CURDIR +to the value of +.Ql Ev PWD +instead. +This behaviour is disabled if +.Ql Ev MAKEOBJDIRPREFIX +is set or +.Ql Ev MAKEOBJDIR +contains a variable transform. +.Ql Ev PWD +is set to the value of +.Ql Va .OBJDIR +for all programs which +.Nm +executes. +.El +.Ss Variable modifiers +Variable expansion may be modified to select or modify each word of the +variable (where a ``word'' is white-space delimited sequence of characters). +The general format of a variable expansion is as follows: +.Pp +.Dl {variable[:modifier[:...]]} +.Pp +Each modifier begins with a colon, +which may be escaped with a backslash +.Pq Ql \e . +The supported modifiers are: +.Bl -tag -width EEE +.It Cm \&:E +Replaces each word in the variable with its suffix. +.It Cm \&:H +Replaces each word in the variable with everything but the last component. +.It Cm \&:M Ns Ar pattern +Select only those words that match +.Ar pattern . +The standard shell wildcard characters +.Pf ( Ql * , +.Ql \&? , +and +.Ql Op ) +may +be used. +The wildcard characters may be escaped with a backslash +.Pq Ql \e . +.It Cm \&:N Ns Ar pattern +This is identical to +.Ql Cm \&:M , +but selects all words which do not match +.Ar pattern . +.It Cm \&:O +Order every word in variable alphabetically. +To sort words in +reverse order use the +.Ql Cm \&:O:[-1..1] +combination of modifiers. +.It Cm \&:Ox +Randomize words in variable. +The results will be different each time you are referring to the +modified variable; use the assignment with expansion +.Pq Ql Cm \&:= +to prevent such behaviour. +For example, +.Bd -literal -offset indent +LIST= uno due tre quattro +RANDOM_LIST= ${LIST:Ox} +STATIC_RANDOM_LIST:= ${LIST:Ox} + +all: + @echo "${RANDOM_LIST}" + @echo "${RANDOM_LIST}" + @echo "${STATIC_RANDOM_LIST}" + @echo "${STATIC_RANDOM_LIST}" +.Ed +may produce output similar to: +.Bd -literal -offset indent +quattro due tre uno +tre due quattro uno +due uno quattro tre +due uno quattro tre +.Ed +.It Cm \&:Q +Quotes every shell meta-character in the variable, so that it can be passed +safely through recursive invocations of +.Nm . +.It Cm \&:R +Replaces each word in the variable with everything but its suffix. +.It Cm \&:tl +Converts variable to lower-case letters. +.It Cm \&:ts Ns Ar c +Words in the variable are normally separated by a space on expansion. +This modifier sets the separator to the character +.Ar c . +If +.Ar c +is omitted, then no separator is used. +.It Cm \&:tu +Converts variable to upper-case letters. +.It Cm \&:tW +Causes the value to be treated as a single word +(possibly containing embedded white space). +See also +.Ql Cm \&:[*] . +.It Cm \&:tw +Causes the value to be treated as a sequence of +words delimited by white space. +See also +.Ql Cm \&:[@] . +.Sm off +.It Cm \&:S No \&/ Ar old_string Xo +.No \&/ Ar new_string +.No \&/ Op Cm 1gW +.Xc +.Sm on +Modify the first occurrence of +.Ar old_string +in the variable's value, replacing it with +.Ar new_string . +If a +.Ql g +is appended to the last slash of the pattern, all occurrences +in each word are replaced. +If a +.Ql 1 +is appended to the last slash of the pattern, only the first word +is affected. +If a +.Ql W +is appended to the last slash of the pattern, +then the value is treated as a single word +(possibly containing embedded white space). +If +.Ar old_string +begins with a caret +.Pq Ql ^ , +.Ar old_string +is anchored at the beginning of each word. +If +.Ar old_string +ends with a dollar sign +.Pq Ql \&$ , +it is anchored at the end of each word. +Inside +.Ar new_string , +an ampersand +.Pq Ql \*[Am] +is replaced by +.Ar old_string +(without any +.Ql ^ +or +.Ql \&$ ) . +Any character may be used as a delimiter for the parts of the modifier +string. +The anchoring, ampersand and delimiter characters may be escaped with a +backslash +.Pq Ql \e . +.Pp +Variable expansion occurs in the normal fashion inside both +.Ar old_string +and +.Ar new_string +with the single exception that a backslash is used to prevent the expansion +of a dollar sign +.Pq Ql \&$ , +not a preceding dollar sign as is usual. +.Sm off +.It Cm \&:C No \&/ Ar pattern Xo +.No \&/ Ar replacement +.No \&/ Op Cm 1gW +.Xc +.Sm on +The +.Cm \&:C +modifier is just like the +.Cm \&:S +modifier except that the old and new strings, instead of being +simple strings, are a regular expression (see +.Xr regex 3 ) +string +.Ar pattern +and an +.Xr ed 1 Ns \-style +string +.Ar replacement . +Normally, the first occurrence of the pattern +.Ar pattern +in each word of the value is substituted with +.Ar replacement . +The +.Ql 1 +modifier causes the substitution to apply to at most one word; the +.Ql g +modifier causes the substitution to apply to as many instances of the +search pattern +.Ar pattern +as occur in the word or words it is found in; the +.Ql W +modifier causes the value to be treated as a single word +(possibly containing embedded white space). +Note that +.Ql 1 +and +.Ql g +are orthogonal; the former specifies whether multiple words are +potentially affected, the latter whether multiple substitutions can +potentially occur within each affected word. +.It Cm \&:T +Replaces each word in the variable with its last component. +.It Cm \&:u +Remove adjacent duplicate words (like +.Xr uniq 1 ) . +.Sm off +.It Cm \&:\&? Ar true_string Cm \&: Ar false_string +.Sm on +If the variable (actually an expression; see below) +evaluates to true, return as its value the +.Ar true_string , +otherwise return the +.Ar false_string . +.It Ar :old_string=new_string +This is the +.At V +style variable substitution. +It must be the last modifier specified. +If +.Ar old_string +or +.Ar new_string +do not contain the pattern matching character +.Ar % +then it is assumed that they are +anchored at the end of each word, so only suffixes or entire +words may be replaced. +Otherwise +.Ar % +is the substring of +.Ar old_string +to be replaced in +.Ar new_string . +.Pp +Variable expansion occurs in the normal fashion inside both +.Ar old_string +and +.Ar new_string +with the single exception that a backslash is used to prevent the +expansion of a dollar sign +.Pq Ql \&$ , +not a preceding dollar sign as is usual. +.Sm off +.It Cm \&:@ Ar temp Cm @ Xo +.Ar string Cm @ +.Sm on +.Xc +This is the loop expansion mechanism from the OSF Development +Environment (ODE) make. +Unlike +.Cm \&.for +loops expansion occurs at the time of +reference. +Assign +.Ar temp +to each word in the variable and evaluate +.Ar string . +The ODE convention is that +.Ar temp +should start and end with a period. +For example. +.Dl ${LINKS:@.LINK.@${LN} ${TARGET} ${.LINK.}@} +.It Cm \&:U Ns Ar newval +If the variable is undefined +.Ar newval +is the value. +If the variable is defined, the existing value is returned. +This is another ODE make feature. +It is handy for setting per-target CFLAGS for instance: +.Dl ${_${.TARGET:T}_CFLAGS:U${DEF_CFLAGS}} +If a value is only required if the variable is undefined, use: +.Dl ${VAR:D:Unewval} +.It Cm \&:D Ns Ar newval +If the variable is defined +.Ar newval +is the value. +.It Cm \&:L +The name of the variable is the value. +.It Cm \&:P +The path of the node which has the same name as the variable +is the value. +If no such node exists or its path is null, then the +name of the variable is used. +.Sm off +.It Cm \&:\&! Ar cmd Cm \&! +.Sm on +The output of running +.Ar cmd +is the value. +.It Cm \&:sh +If the variable is non-empty it is run as a command and the output +becomes the new value. +.It Cm \&::= Ns Ar str +The variable is assigned the value +.Ar str +after substitution. +This modifier and its variations are useful in +obscure situations such as wanting to apply modifiers to +.Cm \&.for +loop iteration variables which won't work due to the way +.Cm \&.for +loops are implemented. +These assignment modifiers always expand to +nothing, so if appearing in a rule line by themselves should be +preceded with something to keep +.Nm +happy. +As in: +.Bd -literal +use_foo: \&.USE +\&.for i in ${\&.TARGET} ${\&.TARGET:R}\&.gz + @: ${t::=$i} + @echo t:R:T=${t:R:T} +\&.endfor + +.Ed +The +.Ql Cm \&:: +helps avoid false matches with the +.At V +style +.Cm \&:= +modifier and since substitution always occurs the +.Cm \&::= +form is vaguely appropriate. +.It Cm \&::?= Ns Ar str +As for +.Cm \&::= +but only if the variable does not already have a value. +.It Cm \&::+= Ns Ar str +Append +.Ar str +to the variable. +.It Cm \&::!= Ns Ar cmd +Assign the output of +.Ar cmd +to the variable. +.It Cm \&:\&[ Ns Ar range Ns Cm \&] +Selects one or more words from the value, +or performs other operations related to the way in which the +value is divided into words. +.Pp +Ordinarily, a value is treated as a sequence of words +delimited by white space. +Some modifiers suppress this behaviour, +causing a value to be treated as a single word +(possibly containing embedded white space). +An empty value, or a value that consists entirely of white-space, +is treated as a single word. +For the purposes of the +.Ql Cm \&:[] +modifier, the words are indexed both forwards using positive integers +(where index 1 represents the first word), +and backwards using negative integers +(where index -1 represents the last word). +.Pp +The +.Ar range +is subjected to variable expansion, and the expanded result is +then interpreted as follows: +.Bl -tag -width index +.\" :[n] +.It Ar index +Selects a single word from the value. +.\" :[start..end] +.It Ar start Ns Cm \&.. Ns Ar end +Selects all words from +.Ar start +to +.Ar end , +inclusive. +For example, +.Ql Cm \&:[2..-1] +selects all words from the second word to the last word. +If +.Ar start +is greater than +.Ar end , +then the words are output in reverse order. +For example, +.Ql Cm \&:[-1..1] +selects all the words from last to first. +.\" :[*] +.It Cm \&* +Causes subsequent modifiers to treat the value as a single word +(possibly containing embedded white space). +Analogous to the effect of +\&"$*\&" +in Bourne shell. +.\" :[0] +.It 0 +Means the same as +.Ql Cm \&:[*] . +.\" :[*] +.It Cm \&@ +Causes subsequent modifiers to treat the value as a sequence of words +delimited by white space. +Analogous to the effect of +\&"$@\&" +in Bourne shell. +.\" :[#] +.It Cm \&# +Returns the number of words in the value. +.El \" :[range] +.El +.Sh INCLUDE STATEMENTS, CONDITIONALS AND FOR LOOPS +Makefile inclusion, conditional structures and for loops reminiscent +of the C programming language are provided in +.Nm . +All such structures are identified by a line beginning with a single +dot +.Pq Ql \&. +character. +Files are included with either +.Cm \&.include Aq Ar file +or +.Cm \&.include Pf \*q Ar file Ns \*q . +Variables between the angle brackets or double quotes are expanded +to form the file name. +If angle brackets are used, the included makefile is expected to be in +the system makefile directory. +If double quotes are used, the including makefile's directory and any +directories specified using the +.Fl I +option are searched before the system +makefile directory. +For compatibility with other versions of +.Nm +.Ql include file ... +is also accepted. +If the include statement is written as +.Cm .-include +or as +.Cm .sinclude +then errors locating and/or opening include files are ignored. +.Pp +Conditional expressions are also preceded by a single dot as the first +character of a line. +The possible conditionals are as follows: +.Bl -tag -width Ds +.It Ic .undef Ar variable +Un-define the specified global variable. +Only global variables may be un-defined. +.It Xo +.Ic \&.if +.Oo \&! Oc Ns Ar expression +.Op Ar operator expression ... +.Xc +Test the value of an expression. +.It Xo +.Ic .ifdef +.Oo \&! Oc Ns Ar variable +.Op Ar operator variable ... +.Xc +Test the value of a variable. +.It Xo +.Ic .ifndef +.Oo \&! Oc Ns Ar variable +.Op Ar operator variable ... +.Xc +Test the value of a variable. +.It Xo +.Ic .ifmake +.Oo \&! Oc Ns Ar target +.Op Ar operator target ... +.Xc +Test the target being built. +.It Xo +.Ic .ifnmake +.Oo \&! Ns Oc Ar target +.Op Ar operator target ... +.Xc +Test the target being built. +.It Ic .else +Reverse the sense of the last conditional. +.It Xo +.Ic .elif +.Oo \&! Ns Oc Ar expression +.Op Ar operator expression ... +.Xc +A combination of +.Ql Ic .else +followed by +.Ql Ic .if . +.It Xo +.Ic .elifdef +.Oo \&! Oc Ns Ar variable +.Op Ar operator variable ... +.Xc +A combination of +.Ql Ic .else +followed by +.Ql Ic .ifdef . +.It Xo +.Ic .elifndef +.Oo \&! Oc Ns Ar variable +.Op Ar operator variable ... +.Xc +A combination of +.Ql Ic .else +followed by +.Ql Ic .ifndef . +.It Xo +.Ic .elifmake +.Oo \&! Oc Ns Ar target +.Op Ar operator target ... +.Xc +A combination of +.Ql Ic .else +followed by +.Ql Ic .ifmake . +.It Xo +.Ic .elifnmake +.Oo \&! Oc Ns Ar target +.Op Ar operator target ... +.Xc +A combination of +.Ql Ic .else +followed by +.Ql Ic .ifnmake . +.It Ic .endif +End the body of the conditional. +.El +.Pp +The +.Ar operator +may be any one of the following: +.Bl -tag -width "Cm XX" +.It Cm \&|\&| +Logical OR. +.It Cm \&\*[Am]\*[Am] +Logical +.Tn AND ; +of higher precedence than +.Dq \&|\&| . +.El +.Pp +As in C, +.Nm +will only evaluate a conditional as far as is necessary to determine +its value. +Parentheses may be used to change the order of evaluation. +The boolean operator +.Ql Ic \&! +may be used to logically negate an entire +conditional. +It is of higher precedence than +.Ql Ic \&\*[Am]\*[Am] . +.Pp +The value of +.Ar expression +may be any of the following: +.Bl -tag -width defined +.It Ic defined +Takes a variable name as an argument and evaluates to true if the variable +has been defined. +.It Ic make +Takes a target name as an argument and evaluates to true if the target +was specified as part of +.Nm Ns 's +command line or was declared the default target (either implicitly or +explicitly, see +.Va .MAIN ) +before the line containing the conditional. +.It Ic empty +Takes a variable, with possible modifiers, and evaluates to true if +the expansion of the variable would result in an empty string. +.It Ic exists +Takes a file name as an argument and evaluates to true if the file exists. +The file is searched for on the system search path (see +.Va .PATH ) . +.It Ic target +Takes a target name as an argument and evaluates to true if the target +has been defined. +.It Ic commands +Takes a target name as an argument and evaluates to true if the target +has been defined and has commands associated with it. +.El +.Pp +.Ar Expression +may also be an arithmetic or string comparison. +Variable expansion is +performed on both sides of the comparison, after which the integral +values are compared. +A value is interpreted as hexadecimal if it is +preceded by 0x, otherwise it is decimal; octal numbers are not supported. +The standard C relational operators are all supported. +If after +variable expansion, either the left or right hand side of a +.Ql Ic == +or +.Ql Ic "!=" +operator is not an integral value, then +string comparison is performed between the expanded +variables. +If no relational operator is given, it is assumed that the expanded +variable is being compared against 0 or an empty string in the case +of a string comparison. +.Pp +When +.Nm +is evaluating one of these conditional expression, and it encounters +a word it doesn't recognize, either the ``make'' or ``defined'' +expression is applied to it, depending on the form of the conditional. +If the form is +.Ql Ic .ifdef +or +.Ql Ic .ifndef , +the ``defined'' expression +is applied. +Similarly, if the form is +.Ql Ic .ifmake +or +.Ql Ic .ifnmake , the ``make'' +expression is applied. +.Pp +If the conditional evaluates to true the parsing of the makefile continues +as before. +If it evaluates to false, the following lines are skipped. +In both cases this continues until a +.Ql Ic .else +or +.Ql Ic .endif +is found. +.Pp +For loops are typically used to apply a set of rules to a list of files. +The syntax of a for loop is: +.Pp +.Bl -tag -compact -width Ds +.It Xo +.Ic \&.for +.Ar variable +.Op Ar variable ... +.Ic in +.Ar expression +.Xc +.It Aq make-rules +.It Ic \&.endfor +.El +.Pp +After the for +.Ic expression +is evaluated, it is split into words. +On each iteration of the loop, one word is taken and assigned to each +.Ic variable , +in order, and these +.Ic variables +are substituted into the +.Ic make-rules +inside the body of the for loop. +The number of words must come out even; that is, if there are three +iteration variables, the number of words provided must be a multiple +of three. +.Sh COMMENTS +Comments begin with a hash +.Pq Ql \&# +character, anywhere but in a shell +command line, and continue to the end of an unescaped new line. +.Sh SPECIAL SOURCES (ATTRIBUTES) +.Bl -tag -width .IGNOREx +.It Ic .EXEC +Target is never out of date, but always execute commands anyway. +.It Ic .IGNORE +Ignore any errors from the commands associated with this target, exactly +as if they all were preceded by a dash +.Pq Ql \- . +.\" .It Ic .INVISIBLE +.\" XXX +.\" .It Ic .JOIN +.\" XXX +.It Ic .MADE +Mark all sources of this target as being up-to-date. +.It Ic .MAKE +Execute the commands associated with this target even if the +.Fl n +or +.Fl t +options were specified. +Normally used to mark recursive +.Nm Ns 's . +.It Ic .NOPATH +Do not search for the target in the directories specified by +.Ic .PATH . +.It Ic .NOTMAIN +Normally +.Nm +selects the first target it encounters as the default target to be built +if no target was specified. +This source prevents this target from being selected. +.It Ic .OPTIONAL +If a target is marked with this attribute and +.Nm +can't figure out how to create it, it will ignore this fact and assume +the file isn't needed or already exists. +.It Ic .PHONY +The target does not +correspond to an actual file; it is always considered to be out of date, +and will not be created with the +.Fl t +option. +.It Ic .PRECIOUS +When +.Nm +is interrupted, it removes any partially made targets. +This source prevents the target from being removed. +.It Ic .RECURSIVE +Synonym for +.Ic .MAKE . +.It Ic .SILENT +Do not echo any of the commands associated with this target, exactly +as if they all were preceded by an at sign +.Pq Ql @ . +.It Ic .USE +Turn the target into +.Nm Ns 's +version of a macro. +When the target is used as a source for another target, the other target +acquires the commands, sources, and attributes (except for +.Ic .USE ) +of the +source. +If the target already has commands, the +.Ic .USE +target's commands are appended +to them. +.It Ic .USEBEFORE +Exactly like +.Ic .USE , +but prepend the +.Ic .USEBEFORE +target commands to the target. +.It Ic .WAIT +If +.Ic .WAIT +appears in a dependency line, the sources that precede it are +made before the sources that succeed it in the line. +Loops are not +detected and targets that form loops will be silently ignored. +.El +.Sh SPECIAL TARGETS +Special targets may not be included with other targets, i.e. they must be +the only target specified. +.Bl -tag -width .BEGINx +.It Ic .BEGIN +Any command lines attached to this target are executed before anything +else is done. +.It Ic .DEFAULT +This is sort of a +.Ic .USE +rule for any target (that was used only as a +source) that +.Nm +can't figure out any other way to create. +Only the shell script is used. +The +.Ic .IMPSRC +variable of a target that inherits +.Ic .DEFAULT Ns 's +commands is set +to the target's own name. +.It Ic .END +Any command lines attached to this target are executed after everything +else is done. +.It Ic .IGNORE +Mark each of the sources with the +.Ic .IGNORE +attribute. +If no sources are specified, this is the equivalent of specifying the +.Fl i +option. +.It Ic .INTERRUPT +If +.Nm +is interrupted, the commands for this target will be executed. +.It Ic .MAIN +If no target is specified when +.Nm +is invoked, this target will be built. +.It Ic .MAKEFLAGS +This target provides a way to specify flags for +.Nm +when the makefile is used. +The flags are as if typed to the shell, though the +.Fl f +option will have +no effect. +.\" XXX: NOT YET!!!! +.\" .It Ic .NOTPARALLEL +.\" The named targets are executed in non parallel mode. +.\" If no targets are +.\" specified, then all targets are executed in non parallel mode. +.It Ic .NOPATH +Apply the +.Ic .NOPATH +attribute to any specified sources. +.It Ic .NOTPARALLEL +Disable parallel mode. +.It Ic .NO_PARALLEL +Synonym for +.Ic .NOTPARALLEL , +for compatibility with other pmake variants. +.It Ic .ORDER +The named targets are made in sequence. +.\" XXX: NOT YET!!!! +.\" .It Ic .PARALLEL +.\" The named targets are executed in parallel mode. +.\" If no targets are +.\" specified, then all targets are executed in parallel mode. +.It Ic .PATH +The sources are directories which are to be searched for files not +found in the current directory. +If no sources are specified, any previously specified directories are +deleted. +If the source is the special +.Ic .DOTLAST +target, then the current working +directory is searched last. +.It Ic .PHONY +Apply the +.Ic .PHONY +attribute to any specified sources. +.It Ic .PRECIOUS +Apply the +.Ic .PRECIOUS +attribute to any specified sources. +If no sources are specified, the +.Ic .PRECIOUS +attribute is applied to every +target in the file. +.It Ic .SHELL +Sets the shell that +.Nm +will use to execute commands. +The sources are a set of +.Ar field=value +pairs. +.Bl -tag -width hasErrCtls +.It Ar name +This is the minimal specification, used to select one of the builtin +shell specs; +.Ar sh , +.Ar ksh , +and +.Ar csh . +.It Ar path +Specifies the path to the shell. +.It Ar hasErrCtl +Indicates whether the shell supports exit on error. +.It Ar check +The command to turn on error checking. +.It Ar ignore +The command to disable error checking. +.It Ar echo +The command to turn on echoing of commands executed. +.It Ar quiet +The command to turn off echoing of commands executed. +.It Ar filter +The output to filter after issuing the +.Ar quiet +command. +It is typically identical to +.Ar quiet . +.It Ar errFlag +The flag to pass the shell to enable error checking. +.It Ar echoFlag +The flag to pass the shell to enable command echoing. +.El +Example: +.Bd -literal +\&.SHELL: name=ksh path=/bin/ksh hasErrCtl=true \\ + check="set -e" ignore="set +e" \\ + echo="set -v" quiet="set +v" filter="set +v" \\ + echoFlag=v errFlag=e +.Ed +.It Ic .SILENT +Apply the +.Ic .SILENT +attribute to any specified sources. +If no sources are specified, the +.Ic .SILENT +attribute is applied to every +command in the file. +.It Ic .SUFFIXES +Each source specifies a suffix to +.Nm . +If no sources are specified, any previously specified suffixes are deleted. +.El +.Sh ENVIRONMENT +.Nm +uses the following environment variables, if they exist: +.Ev MACHINE , +.Ev MACHINE_ARCH , +.Ev MAKE , +.Ev MAKEFLAGS , +.Ev MAKEOBJDIR , +.Ev MAKEOBJDIRPREFIX , +.Ev MAKESYSPATH , +and +.Ev PWD . +.Pp +.Ev MAKEOBJDIRPREFIX +and +.Ev MAKEOBJDIR +may only be set in the environment or on the command line to +.Nm +and not as makefile variables; +see the description of +.Ql Va .OBJDIR +for more details. +.Sh FILES +.Bl -tag -width /usr/share/mk -compact +.It .depend +list of dependencies +.It Makefile +list of dependencies +.It makefile +list of dependencies +.It sys.mk +system makefile +.It /usr/share/mk +system makefile directory +.El +.Sh SEE ALSO +.Xr mkdep 1 +.Sh HISTORY +A +.Nm +command appeared in +.At v7 . diff --git a/devel/bmake/files/make.c b/devel/bmake/files/make.c new file mode 100644 index 0000000000000..1661b7d0de63a --- /dev/null +++ b/devel/bmake/files/make.c @@ -0,0 +1,1234 @@ +/* $NetBSD: make.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: make.c,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $"; +#else +#include +#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 2005/12/02 00:03:00 sjg Exp $"); +#endif +#endif /* not lint */ +#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(ClientData, ClientData); +static int MakeFindChild(ClientData, ClientData); +static int MakeUnmark(ClientData, ClientData); +static int MakeAddAllSrc(ClientData, ClientData); +static int MakeTimeStamp(ClientData, ClientData); +static int MakeHandleUse(ClientData, ClientData); +static Boolean MakeStartJobs(void); +static int MakePrintStatus(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. + * + * Input: + * pgn the current parent + * cgn the child we've just examined + * + * 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(GNode *pgn, GNode *cgn) +{ + if (cgn->mtime > pgn->cmtime) { + pgn->cmtime = cgn->mtime; + } + return (0); +} + +/* + * Input: + * pgn the current parent + * cgn the child we've just examined + * + */ +static int +MakeTimeStamp(ClientData pgn, ClientData cgn) +{ + 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. + * + * Input: + * gn the node to check + * + * 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(GNode *gn) +{ + 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) ? TRUE : FALSE; + } 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_OPTIONAL)) + || 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) and it isn't optional + * 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) ? TRUE : FALSE; + } + + /* + * 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. + * + * Input: + * gnp the node to add + * lp the list to which to add it + * + * Results: + * Always returns 0 + * + * Side Effects: + * The given list is extended + *----------------------------------------------------------------------- + */ +static int +MakeAddChild(ClientData gnp, ClientData lp) +{ + 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. + * + * Input: + * gnp the node to find + * + * Results: + * Always returns 0 + * + * Side Effects: + * The path and mtime of the node and the cmtime of the parent are + * updated; the unmade children count of the parent is decremented. + *----------------------------------------------------------------------- + */ +static int +MakeFindChild(ClientData gnp, ClientData pgnp) +{ + GNode *gn = (GNode *)gnp; + GNode *pgn = (GNode *)pgnp; + + (void)Dir_MTime(gn); + Make_TimeStamp(pgn, gn); + pgn->unmade--; + + return (0); +} + +/*- + *----------------------------------------------------------------------- + * Make_HandleUse -- + * Function called by Make_Run and SuffApplyTransform on the downward + * pass to handle .USE and transformation nodes. It implements the + * .USE and transformation functionality by copying the node's commands, + * type flags and children to the parent node. + * + * 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. + * + * Input: + * cgn The .USE node + * pgn The target of the .USE node + * + * Results: + * none + * + * Side Effects: + * Children and commands may be added to the parent and the parent's + * type may be changed. + * + *----------------------------------------------------------------------- + */ +void +Make_HandleUse(GNode *cgn, GNode *pgn) +{ + LstNode ln; /* An element in the children list */ + +#ifdef DEBUG_SRC + if ((cgn->type & (OP_USE|OP_USEBEFORE|OP_TRANSFORM)) == 0) { + printf("Make_HandleUse: called for plain node %s\n", cgn->name); + return; + } +#endif + + 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) { + 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); +} + +/*- + *----------------------------------------------------------------------- + * MakeHandleUse -- + * Callback function for Lst_ForEach, used by Make_Run on the downward + * pass to handle .USE nodes. Should be called before the children + * are enqueued to be looked at by MakeAddChild. + * This function calls Make_HandleUse to copy the .USE node's commands, + * type flags and children to the parent node. + * + * Input: + * cgnp the child we've just examined + * pgnp the current parent + * + * Results: + * returns 0. + * + * Side Effects: + * After expansion, .USE child nodes are removed from the parent + * + *----------------------------------------------------------------------- + */ +static int +MakeHandleUse(ClientData cgnp, ClientData pgnp) +{ + GNode *cgn = (GNode *)cgnp; + GNode *pgn = (GNode *)pgnp; + LstNode ln; /* An element in the children list */ + int unmarked; + + unmarked = ((cgn->type & OP_MARK) == 0); + cgn->type |= OP_MARK; + + if ((cgn->type & (OP_USE|OP_USEBEFORE)) == 0) + return (0); + + if (unmarked) + Make_HandleUse(cgn, pgn); + + /* + * 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 ((ln = Lst_Member(pgn->children, (ClientData) cgn)) != NILLNODE) { + Lst_Remove(pgn->children, ln); + pgn->unmade--; + } + return (0); +} + + +/*- + *----------------------------------------------------------------------- + * 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(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(" recheck(%s): update time to now: %s\n", + gn->name, Targ_FmtTime(gn->mtime)); + } + gn->mtime = now; + } + else { + if (DEBUG(MAKE)) { + printf(" recheck(%s): current update time: %s\n", + gn->name, 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. + * + * Input: + * cgn the child 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(GNode *cgn) +{ + GNode *pgn; /* the parent node */ + char *cname; /* the child's name */ + LstNode ln; /* Element in parents and iParents lists */ + time_t mtime = -1; + char *p1; + Lst parents; + GNode *centurion; + + 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 this is a `::' node, we must consult its first instance + * which is where all parents are linked. + */ + if ((centurion = cgn->centurion) != NULL) { + if (!Lst_IsEmpty(cgn->parents)) + Punt("%s: cohort has parents", cgn->name); + centurion->unmade_cohorts -= 1; + if (centurion->unmade_cohorts < 0) + Error("Graph cycles through centurion %s", centurion->name); + parents = centurion->parents; + } else { + centurion = cgn; + parents = cgn->parents; + } + if (Lst_Open(parents) == SUCCESS) { + while ((ln = Lst_Next(parents)) != NILLNODE) { + pgn = (GNode *)Lst_Datum(ln); + if (mtime == 0) + pgn->flags |= FORCE; + /* + * If the parent has the .MADE attribute, it has already + * been queued on the `toBeMade' list in Make_ExpandUse() + * and its unmade children counter is zero. + */ + if ((pgn->flags & REMAKE) == 0 || (pgn->type & OP_MADE) != 0) + continue; + + if ( ! (cgn->type & (OP_EXEC|OP_USE|OP_USEBEFORE))) { + if (cgn->made == MADE) + pgn->flags |= CHILDMADE; + (void)Make_TimeStamp(pgn, cgn); + } + + /* + * A parent must wait for the completion of all instances + * of a `::' dependency. + */ + if (centurion->unmade_cohorts != 0 || centurion->made == UNMADE) + continue; + + /* One more child of this parent is now made */ + pgn->unmade -= 1; + 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(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(centurion->successors); ln != NILLNODE; + ln = Lst_Succ(ln)) { + GNode *succ = (GNode *)Lst_Datum(ln); + + if ((succ->flags & REMAKE) != 0 && 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 *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... + * If the child is a .JOIN node, its ALLSRC is propagated to the parent. + * + * 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(ClientData cgnp, ClientData pgnp __unused) +{ + GNode *cgn = (GNode *)cgnp; + + cgn->type &= ~OP_MARK; + return (0); +} + +/* + * Input: + * cgnp The child to add + * pgnp The parent to whose ALLSRC variable it should + * be added + * + */ +static int +MakeAddAllSrc(ClientData cgnp, ClientData pgnp) +{ + 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, *allsrc; + char *p1 = NULL, *p2 = NULL; + + if (cgn->type & OP_ARCHV) + child = Var_Value(MEMBER, cgn, &p1); + else + child = cgn->path ? cgn->path : cgn->name; + if (cgn->type & OP_JOIN) { + allsrc = Var_Value(ALLSRC, cgn, &p2); + } else { + allsrc = child; + } + if (allsrc != NULL) + Var_Append(ALLSRC, allsrc, pgn); + if (p2) + free(p2); + 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(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(void) +{ + 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 || pgn->unmade_cohorts != 0)) { + 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. + * + * Input: + * gnp Node to examine + * cyclep True if gn->unmade being non-zero implies a + * cycle in the graph, not an error in an + * inferior. + * + * Results: + * Always returns 0. + * + * Side Effects: + * A message may be printed. + * + *----------------------------------------------------------------------- + */ +static int +MakePrintStatus(ClientData gnp, ClientData cyclep) +{ + 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 + * + * Input: + * targs the initial list of targets + * + * Results: + * The new list of targets. + * + * Side Effects: + * numNodes is set to the number of elements in the list of targets. + *----------------------------------------------------------------------- + */ +Lst +Make_ExpandUse(Lst targs) +{ + GNode *gn; /* a temporary pointer */ + Lst examine; /* List of targets to examine */ + 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); + + if ((gn->type & OP_MADE) == 0) + Suff_FindDeps(gn); + else { + /* Pretend we made all this node's children */ + Lst_ForEach(gn->children, MakeFindChild, (ClientData)gn); + if (gn->unmade != 0) + printf("Warning: %s still has %d unmade children\n", + gn->name, gn->unmade); + } + + if (gn->unmade != 0) { + Lst_ForEach(gn->children, MakeAddChild, (ClientData)examine); + } else { + (void)Lst_EnQueue(ntargs, (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. + * + * Input: + * targs the initial list of targets + * + * 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(Lst targs) +{ + 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/devel/bmake/files/make.h b/devel/bmake/files/make.h new file mode 100644 index 0000000000000..aeff250e283f8 --- /dev/null +++ b/devel/bmake/files/make.h @@ -0,0 +1,489 @@ +/* $NetBSd: make.h,v 1.53 2005/05/01 01:25:36 christos 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 + */ + +/* + * 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 +#include +#ifdef HAVE_STRING_H +#include +#else +#include +#endif +#include +#include + +#if !defined(__GNUC_PREREQ__) +#if defined(__GNUC__) +#define __GNUC_PREREQ__(x, y) \ + ((__GNUC__ == (x) && __GNUC_MINOR__ >= (y)) || \ + (__GNUC__ > (x))) +#else /* defined(__GNUC__) */ +#define __GNUC_PREREQ__(x, y) 0 +#endif /* defined(__GNUC__) */ +#endif /* !defined(__GNUC_PREREQ__) */ + +#if !defined(__unused) +#if __GNUC_PREREQ__(2, 7) +#define __unused __attribute__((__unused__)) +#else +#define __unused /* delete */ +#endif +#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 */ + int unmade_cohorts;/* # of unmade instances on the + cohorts list */ + struct GNode *centurion; /* Pointer to the first instance of a :: + node; only set when on a cohorts list */ + + 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 recursive 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 children of the node have + * been already made */ +#define OP_SPECIAL 0x00001000 /* Special .BEGIN, .END, .INTERRUPT */ +#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_ADDSLASH 0x02 /* 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 " b) ? a : b) +#endif + +#endif /* _MAKE_H_ */ diff --git a/devel/bmake/files/makefile.boot.in b/devel/bmake/files/makefile.boot.in new file mode 100644 index 0000000000000..2967b8c67b447 --- /dev/null +++ b/devel/bmake/files/makefile.boot.in @@ -0,0 +1,76 @@ +# RCSid: +# $Id: makefile.boot.in,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $ + +# +# modify MACHINE and MACHINE_ARCH as appropriate for your target architecture +# +prefix=@prefix@ +srcdir=@srcdir@ +VPATH=.:$(srcdir) +CC=@CC@ +INSTALL=$(srcdir)/install-sh +MKDEP=$(srcdir)/mkdeps.sh -n -i/usr/include +MKDEP_OPTS=-A +MK=${prefix}/share/mk +MKSRC=@mksrc@ +DEFAULT_MAKESYSPATH=/usr/share/mk:/usr/local/share/mk:/opt/share/mk +CFLAGS=-I. -I$(srcdir) @DEFS@ @CPPFLAGS@ ${XDEFS} -D_PATH_DEFSYSPATH=\"@default_sys_path@\" +MDEFS="-D@force_machine@MACHINE=\"@machine@\"" "-DMACHINE_ARCH=\"@machine_arch@\"" + +OBJ=arch.o buf.o compat.o cond.o dir.o for.o hash.o job.o main.o make.o \ + parse.o str.o suff.o targ.o trace.o var.o util.o getopt.o sigcompat.o @LIBOBJS@ + +BMAKE_ENV= CC="$(CC)" LIBC= MAKEFLAGS= MAKESYSPATH=`pwd`/mk:${MKSRC}:${MK}:${DEFAULT_MAKESYSPATH} + +bootstrap: bmake.boot .mk.done + ${BMAKE_ENV} ./bmake.boot -f Makefile + ${BMAKE_ENV} `pwd`/bmake -f Makefile test + +bmake: bmake.boot + @echo you might want to try: + @echo ${MAKE} -f makefile.boot bootstrap + cp bmake.boot $@ + +bmake.boot: ${OBJ} + (cd lst.lib; $(MAKE) -f makefile.boot CC="$(CC)" CFLAGS="-I.. -I../${srcdir} -I${srcdir} ${CFLAGS}" ) + ${CC} *.o lst.lib/*.o -o $@ @LDFLAGS@ @LIBS@ + rm -f *.[ado] */*.[ado] + + +.mk.done: + @if test -s ${MKSRC}/install-mk; then \ + sh ${MKSRC}/install-mk -v -m 644 `pwd`/mk ${USE_OS}; \ + elif test ! -s /usr/share/mk/sys.mk -a ! -s ${MK}/sys.mk; then \ + echo need to unpack mk.tar.gz under ${srcdir} or set MKSRC or MK; false; \ + fi + @touch $@ + +install: install-bin install-man install-mk + +install-bin: + test -d ${prefix}/bin || ${INSTALL} -m 755 -d ${prefix}/bin + ${INSTALL} -m 755 bmake ${prefix}/bin + +install-man: + test -d ${prefix}/man/cat1 || ${INSTALL} -m 755 -d ${prefix}/man/cat1 + ${INSTALL} -m 444 ${srcdir}/bmake.cat1 ${prefix}/man/cat1/bmake.1 + +install-mk: + @if test -s ${MKSRC}/install-mk; then \ + test -d ${DESTDIR}${MK} || ${INSTALL} -m 775 -d ${DESTDIR}${MK}; \ + sh ${MKSRC}/install-mk -v -m 644 ${DESTDIR}${MK} ${USE_OS}; \ + else \ + echo need to unpack mk.tar.gz under ${srcdir} or set MKSRC; false; \ + fi + +depend: + VPATH=${VPATH} ${MKDEP} $(MKDEP_OPTS) -f makefile.boot ${CFLAGS} ${OBJ:.o=.c} + (cd lst.lib; $(MAKE) -f makefile.boot depend MKDEP="$(MKDEP) $(MKDEP_OPTS)" CC="$(CC)" CFLAGS="-I.. ${CFLAGS}" ) + +main.o: $(srcdir)/main.c + ${CC} ${CFLAGS} ${MDEFS} -o $@ -c $(srcdir)/main.c + +${OBJ}: config.h + +clean: + rm -f bmake *.[ado] */*.[ado] .*.done .depend diff --git a/devel/bmake/files/missing/sys/cdefs.h b/devel/bmake/files/missing/sys/cdefs.h new file mode 100644 index 0000000000000..b9fb16e13c4e8 --- /dev/null +++ b/devel/bmake/files/missing/sys/cdefs.h @@ -0,0 +1,176 @@ +/* $NetBSD: cdefs.h,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $ */ + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Berkeley Software Design, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)cdefs.h 8.7 (Berkeley) 1/21/94 + */ + +#ifndef _SYS_CDEFS_H_ + +#if defined(NEED_HOST_CDEFS_H) +/* + * make sure we don't come past here again. + */ +#undef NEED_HOST_CDEFS_H +/* + * Some systems - notably linux, have sys/cdefs.h + * which is not really compatible with our's. + */ +#ifdef __GNUC__ +# include_next +#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/devel/bmake/files/mkdeps.sh b/devel/bmake/files/mkdeps.sh new file mode 100755 index 0000000000000..59723b99cd3f8 --- /dev/null +++ b/devel/bmake/files/mkdeps.sh @@ -0,0 +1,314 @@ +: +# NAME: +# mkdeps - generate dependencies +# +# SYNOPSIS: +# mkdeps [options] file ... +# +# DESCRIPTION: +# This script updates "makefile" with dependencies for +# "file"(s). It borrows ideas from various makedepend scripts +# and should be compatible with most. +# +# By default we use grep to extract include file names from +# source files. We source an "rc" file '$Mydir/.${Myname}rc' which +# can contain variable assignments such as: +#.nf +# +# cpp_c=/usr/lib/cpp +# cpp_cc=g++ -E +# ... +# +#.fi +# If the variable 'cpp_$suffix' is set, we use it as our cpp in +# place of grep. The program referenced by these variables are +# expected to produce output like: +#.nf +# +# # 10 \"/usr/include/stdio.h\" 1 +# +#.fi +# This allows us to skip most of our processing. For lex,yacc +# and other source files, grep is probably just as quick and +# certainly more portable. +# +# If the "rc" file does not exist, we create it and attempt to +# find cpp or an equivalent cc invocation to assign to 'cpp_c'. +# +# AUTHOR: +# Simon J. Gerraty +# + +# RCSid: +# $Id: mkdeps.sh,v 1.1.1.1 2005/12/02 00:03:00 sjg 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@zen.void.oz.au +# + +Myname=`basename $0 .sh` +Mydir=`dirname $0` + +case `echo -n .` in +-n*) N=; C="\c";; +*) N=-n; C=;; +esac + +cc_include=-I/usr/include + +TF=/tmp/dep.$$ +EF=/tmp/deperr.$$ +> $EF + +case "$*" in +*-n*) # don't use rc file + rc=/dev/null + norc=yes;; +*) + rc=$Mydir/.${Myname}rc + ;; +esac + +update= +Include=include + +if [ x"$norc" = x -a -f $rc ]; then + . $rc +else + # if /usr/lib/cpp or equivalent is available it is better than + # grepping .c files. + # See what (if anything) works on this system... + echo : > $rc + echo "# pre-processor for .c files" >> $rc + # try a couple of sane places first + for d in /usr/libexec /usr/lib /usr/bin /lib /usr/ccs/bin + do + cpp_c=$d/cpp + [ -x $cpp_c ] && break + done + + if [ -x $cpp_c ]; then + echo cpp_c=$cpp_c >> $rc + else + cpp_c= + # rats see if cc can be used + echo "#include " > /tmp/f$$.c + echo "main() { return 0; }" >> /tmp/f$$.c + # try some sensible args to cc + for arg in -E -P -M + do + ok=`${REALCC:-${CC:-cc}} $arg /tmp/f$$.c 2>/dev/null | grep '^#.*stdio.h' | tail -1` + case "$ok" in + "") ;; + *) + cpp_c="${REALCC:-${CC:-cc}} $arg" + echo cpp_c="'$cpp_c'" >> $rc + break;; + esac + done + rm -f /tmp/f$$.c + fi +fi + +clean_up() { + trap "" 2 3 + trap 0 + if [ -s $EF ]; then + egrep -vi "included from|warning" $EF > ${EF}2 + if [ -s ${EF}2 ]; then + cat $EF >&2 + rm -f .depend + ests=1 + fi + fi + rm -f $TF $EF* + exit ${ests:-0} +} + +# this lot does not work on HPsUX - complain to Hp. +trap clean_up 0 +trap exit 2 3 + +get_incs() { + case "$cpp" in + grep) + # set IGNORE="<" to skip system includes + egrep '^#[ ]*include' $* | egrep -v "$IGNORE" | \ + sed -e 's/^.*include[^"<]*["<]//' -e 's/[">].*//g';; + *) + # $cpp (eg. /usr/lib/cpp or cc -E) should produce output like: + # 1 "/usr/include/stdio.h" 2 + # set IGNORE=/usr/include to skip system includes + $cpp $cpp_opts $cc_include $* 2>> $EF | egrep '^#.*\.h"' | sed 's,^#.*"\(.*\)".*,\1,' | + egrep -v "$IGNORE" | sort -u;; + esac +} + +gen_deps() { + llen=$1 + shift + + for ifile in $* + do + case "$cpp" in + grep) + # this lot is not needed if not using grep. + for dir in $srcdir $dirlist /usr/include + do + [ -f "$dir/$ifile" ] && break + done + + if [ ! -f "$dir/$ifile" ]; then + # produce a useful error message (useful to emacs or error) + iline=`grep -n ".*include.*[\"<]$ifile[\">]" $file | cut -d: -f1` + echo "\"$file\", line $iline: cannot find include file \"$ifile\"" >> $EF + # no point adding to dependency list as the resulting makefile + # would not work anyway... + continue + fi + ifile=$dir/$ifile + + # check whether we have done it yet + case `grep "$ifile" $TF` in + "") echo "$ifile" >> $TF;; + *) continue;; # no repeats... + esac + ;; + esac + + len=`expr "$ifile " : '.*'` + if [ "`expr $llen + $len`" -gt ${width:-76} ]; then + echo "\\" >> .depend + echo $N " $C" >> .depend + llen=8 + fi + echo $N "$ifile $C" >> .depend + llen=`expr $llen + $len` + + case "$cpp" in + grep) + # this lot is not needed unless using grep. + ilist=`get_incs $ifile` # recurse needed? + [ "$ilist" ] && llen=`gen_deps $llen $ilist` + ;; + esac + done + echo $llen +} + +for f in makefile Makefile +do + test -s $f && { MAKEFILE=$f; break; } +done + +MAKEFILE=${MAKEFILE:-makefile} +IGNORE=${IGNORE:-"^-"} # won't happen +obj=o +cpp_opts= # incase cpp != grep +vpath= +append= +progDep= + +set -- `getopt "AanNV:s:w:o:I:D:b:f:i:p" "$@"` +for key in "$@" +do + case $key in + --) shift; break;; + -A) Include=;; # cat .depend >> $MAKEFILE + -a) append=yes; shift;; + -n) shift;; # ignore rc + -N) update=no; shift;; # don't update $MAKEFILE + -I) cpp_opts="$cpp_opts$1$2 "; dirlist="$dirlist $2"; shift 2;; + -o) obj=$2; shift 2;; + -s) shift 2;; # can't handle it anyway... + -w) width=$2; shift 2;; + -f) MAKEFILE=$2; shift 2;; + -b) BASEDIR=$2; shift 2;; + -i) IGNORE="$2"; shift 2;; # ignore headers matching this... + -D) cpp_opts="$cpp_opts$1$2 "; shift 2;; + -V) VPATH="$2"; shift 2;; # where to look for files + -p) progDep=yes; shift;; + esac +done + +[ "$VPATH" ] && vpath=`IFS=:; set -- $VPATH; echo $*` + +[ "$append" ] || > .depend + +for file in $* +do + cpp= + suffix=`expr $file : '.*\.\([^.]*\)'` + + eval cpp=\"\${cpp_${suffix}:-grep}\" + + if [ ! -f $file -a "$vpath" ]; then + for d in . $vpath + do + [ -f $d/$file ] && { file=$d/$file; break; } + done + fi + srcdir=`dirname $file` + base=`basename $file .$suffix` + + ilist=`get_incs $file` + + if [ "$ilist" ]; then + > $TF + if [ "$progDep" ]; then + echo "$base: $file \\" >> .depend + else + echo "$base.$obj: $file \\" >> .depend + fi + echo $N " $C" >> .depend + llen=8 + llen=`gen_deps $llen $ilist` + echo >> .depend + echo >> .depend + elif [ "$progDep" ]; then + echo "$base: $file" >> .depend + echo >> .depend + fi +done + +if [ -s .depend ]; then + # ./foo.h looks ugly + mv .depend $TF + { test "$BASEDIR" && sed -e "s;$BASEDIR;\$(BASEDIR);g" $TF || cat $TF; } | + sed 's;\([^.]\)\./;\1;g' > .depend + + # + # Save the manually updated section of the makefile + # + if [ x$update != xno ]; then + trap "" 2 # don't die if we got this far + + # if make doesn't support include, then append our deps... + depended=`grep 'include.*\.depend' $MAKEFILE` + test "$depended" && clean_up + + sed '/^# DO NOT DELETE.*depend.*$/,$d' < $MAKEFILE > $TF + mv $TF $MAKEFILE + cat <> $MAKEFILE +# DO NOT DELETE THIS LINE -- make depend depends on it +# Do not edit anything below, it was added automagically by $Myname. + +! + + case "$Include" in + "") cat .depend >> $MAKEFILE;; + .include) echo '.include ".depend"' >> $MAKEFILE;; + include) echo include .depend >> $MAKEFILE;; + esac + fi +fi +clean_up diff --git a/devel/bmake/files/nonints.h b/devel/bmake/files/nonints.h new file mode 100644 index 0000000000000..812b5015406d3 --- /dev/null +++ b/devel/bmake/files/nonints.h @@ -0,0 +1,192 @@ +/* $NetBSD: nonints.h,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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: @(#)nonints.h 8.3 (Berkeley) 3/19/94 + */ + +/*- + * 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: @(#)nonints.h 8.3 (Berkeley) 3/19/94 + */ + +#ifndef MAKE_NATIVE +#undef __attribute__ +#define __attribute__(x) +#endif + +/* arch.c */ +ReturnStatus Arch_ParseArchive(char **, Lst, GNode *); +void Arch_Touch(GNode *); +void Arch_TouchLib(GNode *); +time_t Arch_MTime(GNode *); +time_t Arch_MemMTime(GNode *); +void Arch_FindLib(GNode *, Lst); +Boolean Arch_LibOODate(GNode *); +void Arch_Init(void); +void Arch_End(void); +int Arch_IsLib(GNode *); + +/* compat.c */ +int CompatRunCommand(ClientData, ClientData); +void Compat_Run(Lst); +int Compat_Make(ClientData, ClientData); + +/* cond.c */ +int Cond_EvalExpression(int, char *, Boolean *, int); +int Cond_Eval(char *); +void Cond_End(void); + +/* for.c */ +int For_Eval(char *); +void For_Run(int); + +/* main.c */ +void Main_ParseArgLine(char *); +int main(int, char **); +char *Cmd_Exec(const char *, const char **); +void Error(const char *, ...) __attribute__((__format__(__printf__, 1, 2))); +void Fatal(const char *, ...) + __attribute__((__format__(__printf__, 1, 2),__noreturn__)); +void Punt(const char *, ...) + __attribute__((__format__(__printf__, 1, 2),__noreturn__)); +void DieHorribly(void) __attribute__((__noreturn__)); +int PrintAddr(ClientData, ClientData); +void Finish(int); +char *estrdup(const char *); +void *emalloc(size_t); +void *erealloc(void *, size_t); +void enomem(void); +int eunlink(const char *); +void execError(const char *, const char *); + +/* parse.c */ +void Parse_Error(int, const char *, ...) + __attribute__((__format__(__printf__, 2, 3))); +Boolean Parse_AnyExport(void); +Boolean Parse_IsVar(char *); +void Parse_DoVar(char *, GNode *); +void Parse_AddIncludeDir(char *); +void Parse_File(const char *, FILE *); +void Parse_Init(void); +void Parse_End(void); +void Parse_FromString(char *, int); +Lst Parse_MainName(void); + +/* str.c */ +char *str_concat(const char *, const char *, int); +char **brk_string(const char *, int *, Boolean, char **); +char *Str_FindSubstring(const char *, const char *); +int Str_Match(const char *, const char *); +char *Str_SYSVMatch(const char *, const char *, int *len); +void Str_SYSVSubst(Buffer, char *, char *, int); + +/* suff.c */ +void Suff_ClearSuffixes(void); +Boolean Suff_IsTransform(char *); +GNode *Suff_AddTransform(char *); +int Suff_EndTransform(ClientData, ClientData); +void Suff_AddSuffix(char *, GNode **); +Lst Suff_GetPath(char *); +void Suff_DoPaths(void); +void Suff_AddInclude(char *); +void Suff_AddLib(char *); +void Suff_FindDeps(GNode *); +Lst Suff_FindPath(GNode *); +void Suff_SetNull(char *); +void Suff_Init(void); +void Suff_End(void); +void Suff_PrintAll(void); + +/* targ.c */ +void Targ_Init(void); +void Targ_End(void); +Lst Targ_List(void); +GNode *Targ_NewGN(const char *); +GNode *Targ_FindNode(const char *, int); +Lst Targ_FindList(Lst, int); +Boolean Targ_Ignore(GNode *); +Boolean Targ_Silent(GNode *); +Boolean Targ_Precious(GNode *); +void Targ_SetMain(GNode *); +int Targ_PrintCmd(ClientData, ClientData); +char *Targ_FmtTime(time_t); +void Targ_PrintType(int); +void Targ_PrintGraph(int); +void Targ_Propagate(void); + +/* var.c */ +void Var_Delete(const char *, GNode *); +void Var_Set(const char *, const char *, GNode *, int); +void Var_Append(const char *, const char *, GNode *); +Boolean Var_Exists(const char *, GNode *); +char *Var_Value(const char *, GNode *, char **); +char *Var_Parse(const char *, GNode *, Boolean, int *, Boolean *); +char *Var_Subst(const char *, const char *, GNode *, Boolean); +char *Var_GetTail(const char *); +char *Var_GetHead(const char *); +void Var_Init(void); +void Var_End(void); +void Var_Dump(GNode *); diff --git a/devel/bmake/files/os.sh b/devel/bmake/files/os.sh new file mode 100644 index 0000000000000..6e9d6c41e8b3b --- /dev/null +++ b/devel/bmake/files/os.sh @@ -0,0 +1,210 @@ +: +# NAME: +# os.sh - operating system specifics +# +# DESCRIPTION: +# This file is included at the start of processing. Its role is +# to set the variables OS, OSREL, OSMAJOR, MACHINE and MACHINE_ARCH to +# reflect the current system. +# +# It also sets variables such as MAILER, LOCAL_FS, PS_AXC to hide +# certain aspects of different UNIX flavours. +# +# SEE ALSO: +# site.sh,funcs.sh +# +# AUTHOR: +# Simon J. Gerraty + +# RCSid: +# $Id: os.sh,v 1.1.1.1 2005/12/02 00:02:59 sjg 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@crufty.net +# + +# this lets us skip sourcing it again +_OS_SH=: + +OS=`uname` +OSREL=`uname -r` +OSMAJOR=`IFS=.; set $OSREL; echo $1` +MACHINE=`uname -m` +MACHINE_ARCH=`uname -p 2>/dev/null || echo $MACHINE` + +# we need this here, and it is not always available... +Which() { + case "$1" in + -*) t=$1; shift;; + *) t=-x;; + esac + case "$1" in + /*) test $t $1 && echo $1;; + *) + # some shells cannot correctly handle `IFS` + # in conjunction with the for loop. + _dirs=`IFS=:; echo ${2:-$PATH}` + for d in $_dirs + do + test $t $d/$1 && { echo $d/$1; break; } + done + ;; + esac +} + +# tr is insanely non-portable wrt char classes, so we need to +# spell out the alphabet. sed y/// would work too. +toUpper() { + ${TR:-tr} abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ +} + +toLower() { + ${TR:-tr} ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz +} + +K= +case $OS in +AIX) # everyone loves to be different... + OSMAJOR=`uname -v` + OSREL="$OSMAJOR.`uname -r`" + LOCAL_FS=jfs + PS_AXC=-e + SHARE_ARCH=$OS/$OSMAJOR.X + ;; +SunOS) + CHOWN=`Which chown /usr/etc:/usr/bin` + export CHOWN + + # Great! Solaris keeps moving arch(1) + # should just bite the bullet and use uname -p + arch=`Which arch /usr/bin:/usr/ucb` + + MAILER=/usr/ucb/Mail + LOCAL_FS=4.2 + + case "$OSREL" in + 4.0*) + # uname -m just says sun which could be anything + # so use arch(1). + MACHINE_ARCH=`arch` + MACHINE=$MACHINE_ARCH + ;; + 4*) + MACHINE_ARCH=`arch` + ;; + 5*) + K=-k + LOCAL_FS=ufs + MAILER=mailx + PS_AXC=-e + # can you believe that ln on Solaris defaults to + # overwriting an existing file!!!!! We want one that works! + test -x /usr/xpg4/bin/ln && LN=${LN:-/usr/xpg4/bin/ln} + # wonderful, 5.8's tr again require's []'s + # but /usr/xpg4/bin/tr causes problems if LC_COLLATE is set! + # use toUpper/toLower instead. + ;; + esac + case "$OS/$MACHINE_ARCH" in + *sun386) SHARE_ARCH=$MACHINE_ARCH;; + esac + ;; +*BSD) + K=-k + MAILER=/usr/bin/Mail + LOCAL_FS=local + case "$-" in + *i*) ;; + *) ENV=;; + esac + # NetBSD at least has good backward compatability + # so NetBSD/i386 is good enough + case $OS in + NetBSD) SHARE_ARCH=$OS/${MACHINE_ARCH:-$MACHINE};; + OpenBSD) + arch=`Which arch /usr/bin:/usr/ucb:$PATH` + MACHINE_ARCH=`$arch -s` + ;; + esac + NAWK=awk + export NAWK + ;; +HP-UX) + TMP_DIRS="/tmp /usr/tmp" + LOCAL_FS=hfs + MAILER=mailx + # don't rely on /bin/sh, its broken + _shell=/bin/ksh; ENV= + # also, no one would be interested in OSMAJOR=A + case "$OSREL" in + ?.09*) OSMAJOR=9; PS_AXC=-e;; + ?.10*) OSMAJOR=10; PS_AXC=-e;; + esac + ;; +IRIX) + LOCAL_FS=efs + ;; +Interix) + MACHINE=i386 + MACHINE_ARCH=i386 + ;; +UnixWare) + OSREL=`uname -v` + OSMAJOR=`IFS=.; set $OSREL; echo $1` + MACHINE_ARCH=`uname -m` + ;; +Linux) + # Not really any such thing as Linux, but + # this covers red-hat and hopefully others. + case $MACHINE in + i?86) MACHINE_ARCH=i386;; # we don't care about i686 vs i586 + esac + LOCAL_FS=ext2 + PS_AXC=axc + [ -x /usr/bin/md5sum ] && { MD5=/usr/bin/md5sum; export MD5; } + ;; +esac + +HOSTNAME=${HOSTNAME:-`( hostname ) 2>/dev/null`} +HOSTNAME=${HOSTNAME:-`( uname -n ) 2>/dev/null`} +case "$HOSTNAME" in +*.*) HOST=`IFS=.; set -- $HOSTNAME; echo $1`;; +*) HOST=$HOSTNAME;; +esac + +TMP_DIRS=${TMP_DIRS:-"/tmp /var/tmp"} +MACHINE_ARCH=${MACHINE_ARCH:-$MACHINE} +# we mount server:/share/arch/$SHARE_ARCH as /usr/local +SHARE_ARCH=${SHARE_ARCH:-$OS/$OSMAJOR.X/$MACHINE_ARCH} +LN=${LN:-ln} +TR=${TR:-tr} + +# Some people like have /share/$HOST_TARGET/bin etc. +HOST_TARGET=`echo ${OS}${OSMAJOR}-${MACHINE_ARCH} | toLower` +export HOST_TARGET + +case `echo -n .` in -n*) N=; C="\c";; *) N=-n; C=;; esac + +export HOSTNAME HOST +export OS MACHINE MACHINE_ARCH OSREL OSMAJOR LOCAL_FS TMP_DIRS MAILER N C K PS_AXC +export LN SHARE_ARCH TR + +case /$0 in +*/os.sh) + for v in $* + do + eval vv=\$$v + echo "$v='$vv'" + done + ;; +esac + diff --git a/devel/bmake/files/parse.c b/devel/bmake/files/parse.c new file mode 100644 index 0000000000000..0b6a0edec7ec0 --- /dev/null +++ b/devel/bmake/files/parse.c @@ -0,0 +1,2993 @@ +/* $NetBSD: parse.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: parse.c,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)parse.c 8.3 (Berkeley) 3/19/94"; +#else +__RCSID("$NetBSD: parse.c,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * parse.c -- + * Functions to parse a makefile. + * + * One function, Parse_Init, must be called before any functions + * in this module are used. After that, the function Parse_File is the + * main entry point and controls most of the other functions in this + * module. + * + * Most important structures are kept in Lsts. Directories for + * the #include "..." function are kept in the 'parseIncPath' Lst, while + * those for the #include <...> are kept in the 'sysIncPath' Lst. The + * targets currently being defined are kept in the 'targets' Lst. + * + * The variables 'fname' and 'lineno' are used to track the name + * of the current file and the line number in that file so that error + * messages can be more meaningful. + * + * Interface: + * Parse_Init Initialization function which must be + * called before anything else in this module + * is used. + * + * Parse_End Cleanup the module + * + * Parse_File Function used to parse a makefile. It must + * be given the name of the file, which should + * already have been opened, and a function + * to call to read a character from the file. + * + * Parse_IsVar Returns TRUE if the given line is a + * variable assignment. Used by MainParseArgs + * to determine if an argument is a target + * or a variable assignment. Used internally + * for pretty much the same thing... + * + * Parse_Error Function called when an error occurs in + * parsing. Used by the variable and + * conditional modules. + * Parse_MainName Returns a Lst of the main target to create. + */ + +#include +#include +#include +#include + +#include "make.h" +#include "hash.h" +#include "dir.h" +#include "job.h" +#include "buf.h" +#include "pathnames.h" + +/* + * These values are returned by ParseEOF to tell Parse_File whether to + * CONTINUE parsing, i.e. it had only reached the end of an include file, + * or if it's DONE. + */ +#define CONTINUE 1 +#define DONE 0 +static Lst targets; /* targets we're working on */ +#ifdef CLEANUP +static Lst targCmds; /* command lines for targets */ +#endif +static Boolean inLine; /* true if currently in a dependency + * line or its commands */ +typedef struct { + char *str; + char *ptr; +} PTR; + +static int fatals = 0; + +static GNode *mainNode; /* The main target to create. This is the + * first target on the first dependency + * line in the first makefile */ +typedef struct IFile { + char *fname; /* name of previous file */ + int lineno; /* saved line number */ + FILE * F; /* the open stream */ + PTR * P; /* the char pointer */ +} IFile; + +static IFile curFile; + + +/* + * Definitions for handling #include specifications + */ + +static Lst includes; /* stack of IFiles generated by + * #includes */ +Lst parseIncPath; /* list of directories for "..." includes */ +Lst sysIncPath; /* list of directories for <...> includes */ +Lst defIncPath; /* default directories for <...> includes */ + +/*- + * specType contains the SPECial TYPE of the current target. It is + * Not if the target is unspecial. If it *is* special, however, the children + * are linked as children of the parent but not vice versa. This variable is + * set in ParseDoDependency + */ +typedef enum { + Begin, /* .BEGIN */ + Default, /* .DEFAULT */ + End, /* .END */ + Ignore, /* .IGNORE */ + Includes, /* .INCLUDES */ + Interrupt, /* .INTERRUPT */ + Libs, /* .LIBS */ + MFlags, /* .MFLAGS or .MAKEFLAGS */ + Main, /* .MAIN and we don't have anything user-specified to + * make */ + NoExport, /* .NOEXPORT */ + NoPath, /* .NOPATH */ + Not, /* Not special */ + NotParallel, /* .NOTPARALLEL */ + Null, /* .NULL */ + ExObjdir, /* .OBJDIR */ + Order, /* .ORDER */ + Parallel, /* .PARALLEL */ + ExPath, /* .PATH */ + Phony, /* .PHONY */ +#ifdef POSIX + Posix, /* .POSIX */ +#endif + Precious, /* .PRECIOUS */ + ExShell, /* .SHELL */ + Silent, /* .SILENT */ + SingleShell, /* .SINGLESHELL */ + Suffixes, /* .SUFFIXES */ + Wait, /* .WAIT */ + Attribute /* Generic attribute */ +} ParseSpecial; + +static ParseSpecial specType; +static int waiting; + +#define LPAREN '(' +#define RPAREN ')' +/* + * Predecessor node for handling .ORDER. Initialized to NILGNODE when .ORDER + * seen, then set to each successive source on the line. + */ +static GNode *predecessor; + +/* + * The parseKeywords table is searched using binary search when deciding + * if a target or source is special. The 'spec' field is the ParseSpecial + * type of the keyword ("Not" if the keyword isn't special as a target) while + * the 'op' field is the operator to apply to the list of targets if the + * keyword is used as a source ("0" if the keyword isn't special as a source) + */ +static struct { + const char *name; /* Name of keyword */ + ParseSpecial spec; /* Type when used as a target */ + int op; /* Operator when used as a source */ +} parseKeywords[] = { +{ ".BEGIN", Begin, 0 }, +{ ".DEFAULT", Default, 0 }, +{ ".END", End, 0 }, +{ ".EXEC", Attribute, OP_EXEC }, +{ ".IGNORE", Ignore, OP_IGNORE }, +{ ".INCLUDES", Includes, 0 }, +{ ".INTERRUPT", Interrupt, 0 }, +{ ".INVISIBLE", Attribute, OP_INVISIBLE }, +{ ".JOIN", Attribute, OP_JOIN }, +{ ".LIBS", Libs, 0 }, +{ ".MADE", Attribute, OP_MADE }, +{ ".MAIN", Main, 0 }, +{ ".MAKE", Attribute, OP_MAKE }, +{ ".MAKEFLAGS", MFlags, 0 }, +{ ".MFLAGS", MFlags, 0 }, +{ ".NOPATH", NoPath, OP_NOPATH }, +{ ".NOTMAIN", Attribute, OP_NOTMAIN }, +{ ".NOTPARALLEL", NotParallel, 0 }, +{ ".NO_PARALLEL", NotParallel, 0 }, +{ ".NULL", Null, 0 }, +{ ".OBJDIR", ExObjdir, 0 }, +{ ".OPTIONAL", Attribute, OP_OPTIONAL }, +{ ".ORDER", Order, 0 }, +{ ".PARALLEL", Parallel, 0 }, +{ ".PATH", ExPath, 0 }, +{ ".PHONY", Phony, OP_PHONY }, +#ifdef POSIX +{ ".POSIX", Posix, 0 }, +#endif +{ ".PRECIOUS", Precious, OP_PRECIOUS }, +{ ".RECURSIVE", Attribute, OP_MAKE }, +{ ".SHELL", ExShell, 0 }, +{ ".SILENT", Silent, OP_SILENT }, +{ ".SINGLESHELL", SingleShell, 0 }, +{ ".SUFFIXES", Suffixes, 0 }, +{ ".USE", Attribute, OP_USE }, +{ ".USEBEFORE", Attribute, OP_USEBEFORE }, +{ ".WAIT", Wait, 0 }, +}; + +/* + * Used by ParseDoSpecialSrc() + */ +typedef struct { + int op; + char *src; + Lst allsrc; +} SpecialSrc; + +static int ParseIsEscaped(const char *, const char *); +static void ParseErrorInternal(char *, size_t, int, const char *, ...) + __attribute__((__format__(__printf__, 4, 5))); +static void ParseVErrorInternal(char *, size_t, int, const char *, va_list) + __attribute__((__format__(__printf__, 4, 0))); +static int ParseFindKeyword(char *); +static int ParseLinkSrc(ClientData, ClientData); +static int ParseDoOp(ClientData, ClientData); +static int ParseAddDep(ClientData, ClientData); +static int ParseDoSpecialSrc(ClientData, ClientData); +static void ParseDoSrc(int, char *, Lst, Boolean); +static int ParseFindMain(ClientData, ClientData); +static int ParseAddDir(ClientData, ClientData); +static int ParseClearPath(ClientData, ClientData); +static void ParseDoDependency(char *); +static int ParseAddCmd(ClientData, ClientData); +static __inline int ParseReadc(void); +static void ParseUnreadc(int); +static void ParseHasCommands(ClientData); +static void ParseDoInclude(char *); +static void ParseSetParseFile(char *); +#ifdef SYSVINCLUDE +static void ParseTraditionalInclude(char *); +#endif +static int ParseEOF(int); +static char *ParseReadLine(void); +static char *ParseSkipLine(int, int); +static void ParseFinishLine(void); +static void ParseMark(GNode *); + +extern int maxJobs; + + +/*- + *---------------------------------------------------------------------- + * ParseIsEscaped -- + * Check if the current character is escaped on the current line + * + * Results: + * 0 if the character is not backslash escaped, 1 otherwise + * + * Side Effects: + * None + *---------------------------------------------------------------------- + */ +static int +ParseIsEscaped(const char *line, const char *c) +{ + int active = 0; + for (;;) { + if (line == c) + return active; + if (*--c != '\\') + return active; + active = !active; + } +} + +/*- + *---------------------------------------------------------------------- + * ParseFindKeyword -- + * Look in the table of keywords for one matching the given string. + * + * Input: + * str String to find + * + * Results: + * The index of the keyword, or -1 if it isn't there. + * + * Side Effects: + * None + *---------------------------------------------------------------------- + */ +static int +ParseFindKeyword(char *str) +{ + int start, end, cur; + int diff; + + start = 0; + end = (sizeof(parseKeywords)/sizeof(parseKeywords[0])) - 1; + + do { + cur = start + ((end - start) / 2); + diff = strcmp(str, parseKeywords[cur].name); + + if (diff == 0) { + return (cur); + } else if (diff < 0) { + end = cur - 1; + } else { + start = cur + 1; + } + } while (start <= end); + return (-1); +} + +/*- + * ParseVErrorInternal -- + * Error message abort function for parsing. Prints out the context + * of the error (line number and file) as well as the message with + * two optional arguments. + * + * Results: + * None + * + * Side Effects: + * "fatals" is incremented if the level is PARSE_FATAL. + */ +/* VARARGS */ +static void +ParseVErrorInternal(char *cfname, size_t clineno, int type, const char *fmt, + va_list ap) +{ + static Boolean fatal_warning_error_printed = FALSE; + + (void)fprintf(stderr, "%s: \"", progname); + + if (*cfname != '/') { + char *cp; + const char *dir; + + /* + * Nothing is more anoying than not knowing which Makefile + * is the culprit. + */ + dir = Var_Value(".PARSEDIR", VAR_GLOBAL, &cp); + if (dir == NULL || *dir == '\0' || + (*dir == '.' && dir[1] == '\0')) + dir = Var_Value(".CURDIR", VAR_GLOBAL, &cp); + if (dir == NULL) + dir = "."; + + (void)fprintf(stderr, "%s/%s", dir, cfname); + } else + (void)fprintf(stderr, "%s", cfname); + + (void)fprintf(stderr, "\" line %d: ", (int)clineno); + if (type == PARSE_WARNING) + (void)fprintf(stderr, "warning: "); + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, "\n"); + (void)fflush(stderr); + if (type == PARSE_FATAL || parseWarnFatal) + fatals += 1; + if (parseWarnFatal && !fatal_warning_error_printed) { + Error("parsing warnings being treated as errors"); + fatal_warning_error_printed = TRUE; + } +} + +/*- + * ParseErrorInternal -- + * Error function + * + * Results: + * None + * + * Side Effects: + * None + */ +/* VARARGS */ +static void +ParseErrorInternal(char *cfname, size_t clineno, int type, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + ParseVErrorInternal(cfname, clineno, type, fmt, ap); + va_end(ap); +} + +/*- + * Parse_Error -- + * External interface to ParseErrorInternal; uses the default filename + * Line number. + * + * Results: + * None + * + * Side Effects: + * None + */ +/* VARARGS */ +void +Parse_Error(int type, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + ParseVErrorInternal(curFile.fname, curFile.lineno, type, fmt, ap); + va_end(ap); +} + +/*- + *--------------------------------------------------------------------- + * ParseLinkSrc -- + * Link the parent node to its new child. Used in a Lst_ForEach by + * ParseDoDependency. If the specType isn't 'Not', the parent + * isn't linked as a parent of the child. + * + * Input: + * pgnp The parent node + * cgpn The child node + * + * Results: + * Always = 0 + * + * Side Effects: + * New elements are added to the parents list of cgn and the + * children list of cgn. the unmade field of pgn is updated + * to reflect the additional child. + *--------------------------------------------------------------------- + */ +static int +ParseLinkSrc(ClientData pgnp, ClientData cgnp) +{ + GNode *pgn = (GNode *)pgnp; + GNode *cgn = (GNode *)cgnp; + + if ((pgn->type & OP_DOUBLEDEP) && !Lst_IsEmpty (pgn->cohorts)) + pgn = (GNode *)Lst_Datum(Lst_Last(pgn->cohorts)); + (void)Lst_AtEnd(pgn->children, (ClientData)cgn); + if (specType == Not) + (void)Lst_AtEnd(cgn->parents, (ClientData)pgn); + pgn->unmade += 1; + return (0); +} + +/*- + *--------------------------------------------------------------------- + * ParseDoOp -- + * Apply the parsed operator to the given target node. Used in a + * Lst_ForEach call by ParseDoDependency once all targets have + * been found and their operator parsed. If the previous and new + * operators are incompatible, a major error is taken. + * + * Input: + * gnp The node to which the operator is to be applied + * opp The operator to apply + * + * Results: + * Always 0 + * + * Side Effects: + * The type field of the node is altered to reflect any new bits in + * the op. + *--------------------------------------------------------------------- + */ +static int +ParseDoOp(ClientData gnp, ClientData opp) +{ + GNode *gn = (GNode *)gnp; + int op = *(int *)opp; + /* + * If the dependency mask of the operator and the node don't match and + * the node has actually had an operator applied to it before, and + * the operator actually has some dependency information in it, complain. + */ + if (((op & OP_OPMASK) != (gn->type & OP_OPMASK)) && + !OP_NOP(gn->type) && !OP_NOP(op)) + { + Parse_Error(PARSE_FATAL, "Inconsistent operator for %s", gn->name); + return (1); + } + + if ((op == OP_DOUBLEDEP) && ((gn->type & OP_OPMASK) == OP_DOUBLEDEP)) { + /* + * If the node was the object of a :: operator, we need to create a + * new instance of it for the children and commands on this dependency + * line. The new instance is placed on the 'cohorts' list of the + * initial one (note the initial one is not on its own cohorts list) + * and the new instance is linked to all parents of the initial + * instance. + */ + GNode *cohort; + + /* + * Propagate copied bits to the initial node. They'll be propagated + * back to the rest of the cohorts later. + */ + gn->type |= op & ~OP_OPMASK; + + cohort = Targ_NewGN(gn->name); + /* + * Make the cohort invisible as well to avoid duplicating it into + * other variables. True, parents of this target won't tend to do + * anything with their local variables, but better safe than + * sorry. (I think this is pointless now, since the relevant list + * traversals will no longer see this node anyway. -mycroft) + */ + cohort->type = op | OP_INVISIBLE; + (void)Lst_AtEnd(gn->cohorts, (ClientData)cohort); + cohort->centurion = gn; + gn->unmade_cohorts += 1; + } else { + /* + * We don't want to nuke any previous flags (whatever they were) so we + * just OR the new operator into the old + */ + gn->type |= op; + } + + return (0); +} + +/*- + *--------------------------------------------------------------------- + * ParseAddDep -- + * Check if the pair of GNodes given needs to be synchronized. + * This has to be when two nodes are on different sides of a + * .WAIT directive. + * + * Results: + * Returns 1 if the two targets need to be ordered, 0 otherwise. + * If it returns 1, the search can stop + * + * Side Effects: + * A dependency can be added between the two nodes. + * + *--------------------------------------------------------------------- + */ +static int +ParseAddDep(ClientData pp, ClientData sp) +{ + GNode *p = (GNode *)pp; + GNode *s = (GNode *)sp; + + if (p->order < s->order) { + /* + * XXX: This can cause loops, and loops can cause unmade targets, + * but checking is tedious, and the debugging output can show the + * problem + */ + (void)Lst_AtEnd(p->successors, (ClientData)s); + (void)Lst_AtEnd(s->preds, (ClientData)p); + return 0; + } + else + return 1; +} + +/* - + *--------------------------------------------------------------------- + * ParseDoSpecialSrc -- + * ParseDoSrc struck an unexpanded variable in a src. + * The most likely reason is a src that refers to .TARGET or + * .PREFIX so we get called to set those for each target + * and then call ParseDoSrc again to do the real work. + * + * Input: + * tp A target GNode * + * sp A SpecialSrc * which contains the args we need + * for ParseDoSrc. + * + * Results: + * Goodness + * + * Side Effects: + * The target GNode will have .TARGET and .PREFIX set, this seems + * harmless. + */ +static int +ParseDoSpecialSrc(ClientData tp, ClientData sp) +{ + GNode *tn = (GNode *)tp; + GNode *gn; + SpecialSrc *ss = (SpecialSrc *)sp; + char *cp; + char *cp2; + char *pref; + + /* + * If the target is a suffix rule, leave it alone. + */ + if (Suff_IsTransform(tn->name)) { + ParseDoSrc(ss->op, ss->src, ss->allsrc, FALSE); /* don't come back */ + return 0; + } + Var_Set(TARGET, tn->name, tn, 0); + if ((pref = strrchr(tn->name, '/'))) + pref++; + else + pref = tn->name; + if ((cp2 = strchr(pref, '.')) > pref) { + cp = estrdup(pref); + cp[cp2 - pref] = '\0'; + Var_Set(PREFIX, cp, tn, 0); + free(cp); + } else + Var_Set(PREFIX, pref, tn, 0); + cp = Var_Subst(NULL, ss->src, tn, FALSE); + if (strchr(cp, '$')) { + Parse_Error(PARSE_WARNING, "Cannot resolve '%s' here", ss->src); + ParseDoSrc(ss->op, ss->src, ss->allsrc, FALSE); /* don't come back */ + return 1; /* stop list traversal */ + } + /* + * We don't want to make every target dependent on sources for + * other targets. This is the bit of ParseDoSrc which is relevant. + * The main difference is we don't link the resolved src to all targets. + */ + gn = Targ_FindNode(cp, TARG_CREATE); + if (ss->op) { + gn->type |= ss->op; + } else { + ParseLinkSrc((ClientData)tn, (ClientData)gn); + } + gn->order = waiting; + (void)Lst_AtEnd(ss->allsrc, (ClientData)gn); + if (waiting) { + Lst_ForEach(ss->allsrc, ParseAddDep, (ClientData)gn); + } + return 0; +} + + +/*- + *--------------------------------------------------------------------- + * ParseDoSrc -- + * Given the name of a source, figure out if it is an attribute + * and apply it to the targets if it is. Else decide if there is + * some attribute which should be applied *to* the source because + * of some special target and apply it if so. Otherwise, make the + * source be a child of the targets in the list 'targets' + * + * Input: + * tOp operator (if any) from special targets + * src name of the source to handle + * allsrc List of all sources to wait for + * resolve boolean - should we try and resolve .TARGET refs. + * + * Results: + * None + * + * Side Effects: + * Operator bits may be added to the list of targets or to the source. + * The targets may have a new source added to their lists of children. + *--------------------------------------------------------------------- + */ +static void +ParseDoSrc(int tOp, char *src, Lst allsrc, Boolean resolve) +{ + GNode *gn = NULL; + + if (*src == '.' && isupper ((unsigned char)src[1])) { + int keywd = ParseFindKeyword(src); + if (keywd != -1) { + int op = parseKeywords[keywd].op; + if (op != 0) { + Lst_ForEach(targets, ParseDoOp, (ClientData)&op); + return; + } + if (parseKeywords[keywd].spec == Wait) { + waiting++; + return; + } + } + } + + switch (specType) { + case Main: + /* + * If we have noted the existence of a .MAIN, it means we need + * to add the sources of said target to the list of things + * to create. The string 'src' is likely to be free, so we + * must make a new copy of it. Note that this will only be + * invoked if the user didn't specify a target on the command + * line. This is to allow #ifmake's to succeed, or something... + */ + (void)Lst_AtEnd(create, (ClientData)estrdup(src)); + /* + * Add the name to the .TARGETS variable as well, so the user cna + * employ that, if desired. + */ + Var_Append(".TARGETS", src, VAR_GLOBAL); + return; + + case Order: + /* + * Create proper predecessor/successor links between the previous + * source and the current one. + */ + gn = Targ_FindNode(src, TARG_CREATE); + if (predecessor != NILGNODE) { + (void)Lst_AtEnd(predecessor->successors, (ClientData)gn); + (void)Lst_AtEnd(gn->preds, (ClientData)predecessor); + } + /* + * The current source now becomes the predecessor for the next one. + */ + predecessor = gn; + break; + + default: + /* + * If the source is not an attribute, we need to find/create + * a node for it. After that we can apply any operator to it + * from a special target or link it to its parents, as + * appropriate. + * + * In the case of a source that was the object of a :: operator, + * the attribute is applied to all of its instances (as kept in + * the 'cohorts' list of the node) or all the cohorts are linked + * to all the targets. + */ + if (resolve && strchr(src, '$')) { + SpecialSrc ss; + + ss.op = tOp; + ss.src = src; + ss.allsrc = allsrc; + + /* + * If src cannot be fully resolved, we'll be called again + * with resolve==FALSE. + */ + Lst_ForEach(targets, ParseDoSpecialSrc, (ClientData)&ss); + return; + } + gn = Targ_FindNode(src, TARG_CREATE); + if (tOp) { + gn->type |= tOp; + } else { + Lst_ForEach(targets, ParseLinkSrc, (ClientData)gn); + } + break; + } + + gn->order = waiting; + (void)Lst_AtEnd(allsrc, (ClientData)gn); + if (waiting) { + Lst_ForEach(allsrc, ParseAddDep, (ClientData)gn); + } +} + +/*- + *----------------------------------------------------------------------- + * ParseFindMain -- + * Find a real target in the list and set it to be the main one. + * Called by ParseDoDependency when a main target hasn't been found + * yet. + * + * Input: + * gnp Node to examine + * + * Results: + * 0 if main not found yet, 1 if it is. + * + * Side Effects: + * mainNode is changed and Targ_SetMain is called. + * + *----------------------------------------------------------------------- + */ +static int +ParseFindMain(ClientData gnp, ClientData dummy) +{ + GNode *gn = (GNode *)gnp; + if ((gn->type & OP_NOTARGET) == 0) { + mainNode = gn; + Targ_SetMain(gn); + return (dummy ? 1 : 1); + } else { + return (dummy ? 0 : 0); + } +} + +/*- + *----------------------------------------------------------------------- + * ParseAddDir -- + * Front-end for Dir_AddDir to make sure Lst_ForEach keeps going + * + * Results: + * === 0 + * + * Side Effects: + * See Dir_AddDir. + * + *----------------------------------------------------------------------- + */ +static int +ParseAddDir(ClientData path, ClientData name) +{ + (void)Dir_AddDir((Lst) path, (char *)name); + return(0); +} + +/*- + *----------------------------------------------------------------------- + * ParseClearPath -- + * Front-end for Dir_ClearPath to make sure Lst_ForEach keeps going + * + * Results: + * === 0 + * + * Side Effects: + * See Dir_ClearPath + * + *----------------------------------------------------------------------- + */ +static int +ParseClearPath(ClientData path, ClientData dummy) +{ + Dir_ClearPath((Lst) path); + return(dummy ? 0 : 0); +} + +/*- + *--------------------------------------------------------------------- + * ParseDoDependency -- + * Parse the dependency line in line. + * + * Input: + * line the line to parse + * + * Results: + * None + * + * Side Effects: + * The nodes of the sources are linked as children to the nodes of the + * targets. Some nodes may be created. + * + * We parse a dependency line by first extracting words from the line and + * finding nodes in the list of all targets with that name. This is done + * until a character is encountered which is an operator character. Currently + * these are only ! and :. At this point the operator is parsed and the + * pointer into the line advanced until the first source is encountered. + * The parsed operator is applied to each node in the 'targets' list, + * which is where the nodes found for the targets are kept, by means of + * the ParseDoOp function. + * The sources are read in much the same way as the targets were except + * that now they are expanded using the wildcarding scheme of the C-Shell + * and all instances of the resulting words in the list of all targets + * are found. Each of the resulting nodes is then linked to each of the + * targets as one of its children. + * Certain targets are handled specially. These are the ones detailed + * by the specType variable. + * The storing of transformation rules is also taken care of here. + * A target is recognized as a transformation rule by calling + * Suff_IsTransform. If it is a transformation rule, its node is gotten + * from the suffix module via Suff_AddTransform rather than the standard + * Targ_FindNode in the target module. + *--------------------------------------------------------------------- + */ +static void +ParseDoDependency(char *line) +{ + char *cp; /* our current position */ + GNode *gn = NULL; /* a general purpose temporary node */ + int op; /* the operator on the line */ + char savec; /* a place to save a character */ + Lst paths; /* List of search paths to alter when parsing + * a list of .PATH targets */ + int tOp; /* operator from special target */ + Lst sources; /* list of archive source names after + * expansion */ + Lst curTargs; /* list of target names to be found and added + * to the targets list */ + Lst curSrcs; /* list of sources in order */ + char *lstart = line; + Boolean hasWait; /* is .WAIT present in srcs */ + + tOp = 0; + + specType = Not; + waiting = 0; + paths = (Lst)NULL; + + curTargs = Lst_Init(FALSE); + curSrcs = Lst_Init(FALSE); + + do { + for (cp = line; + *cp && (ParseIsEscaped(lstart, cp) || + (!isspace ((unsigned char)*cp) && + (*cp != '!') && (*cp != ':') && (*cp != LPAREN))); + cp++) + { + if (*cp == '$') { + /* + * Must be a dynamic source (would have been expanded + * otherwise), so call the Var module to parse the puppy + * so we can safely advance beyond it...There should be + * no errors in this, as they would have been discovered + * in the initial Var_Subst and we wouldn't be here. + */ + int length; + Boolean freeIt; + char *result; + + result=Var_Parse(cp, VAR_CMD, TRUE, &length, &freeIt); + + if (freeIt) { + free(result); + } + cp += length-1; + } + continue; + } + if (!ParseIsEscaped(lstart, cp) && *cp == LPAREN) { + /* + * Archives must be handled specially to make sure the OP_ARCHV + * flag is set in their 'type' field, for one thing, and because + * things like "archive(file1.o file2.o file3.o)" are permissible. + * Arch_ParseArchive will set 'line' to be the first non-blank + * after the archive-spec. It creates/finds nodes for the members + * and places them on the given list, returning SUCCESS if all + * went well and FAILURE if there was an error in the + * specification. On error, line should remain untouched. + */ + if (Arch_ParseArchive(&line, targets, VAR_CMD) != SUCCESS) { + Parse_Error(PARSE_FATAL, + "Error in archive specification: \"%s\"", line); + return; + } else { + continue; + } + } + savec = *cp; + + if (!*cp) { + /* + * Ending a dependency line without an operator is a Bozo + * no-no. As a heuristic, this is also often triggered by + * undetected conflicts from cvs/rcs merges. + */ + if ((strncmp(line, "<<<<<<", 6) == 0) || + (strncmp(line, "======", 6) == 0) || + (strncmp(line, ">>>>>>", 6) == 0)) + Parse_Error(PARSE_FATAL, + "Makefile appears to contain unresolved cvs/rcs/??? merge conflicts"); + else + Parse_Error(PARSE_FATAL, "Need an operator"); + return; + } + *cp = '\0'; + /* + * Have a word in line. See if it's a special target and set + * specType to match it. + */ + if (*line == '.' && isupper ((unsigned char)line[1])) { + /* + * See if the target is a special target that must have it + * or its sources handled specially. + */ + int keywd = ParseFindKeyword(line); + if (keywd != -1) { + if (specType == ExPath && parseKeywords[keywd].spec != ExPath) { + Parse_Error(PARSE_FATAL, "Mismatched special targets"); + return; + } + + specType = parseKeywords[keywd].spec; + tOp = parseKeywords[keywd].op; + + /* + * Certain special targets have special semantics: + * .PATH Have to set the dirSearchPath + * variable too + * .MAIN Its sources are only used if + * nothing has been specified to + * create. + * .DEFAULT Need to create a node to hang + * commands on, but we don't want + * it in the graph, nor do we want + * it to be the Main Target, so we + * create it, set OP_NOTMAIN and + * add it to the list, setting + * DEFAULT to the new node for + * later use. We claim the node is + * A transformation rule to make + * life easier later, when we'll + * use Make_HandleUse to actually + * apply the .DEFAULT commands. + * .PHONY The list of targets + * .NOPATH Don't search for file in the path + * .BEGIN + * .END + * .INTERRUPT Are not to be considered the + * main target. + * .NOTPARALLEL Make only one target at a time. + * .SINGLESHELL Create a shell for each command. + * .ORDER Must set initial predecessor to NIL + */ + switch (specType) { + case ExPath: + if (paths == NULL) { + paths = Lst_Init(FALSE); + } + (void)Lst_AtEnd(paths, (ClientData)dirSearchPath); + break; + case Main: + if (!Lst_IsEmpty(create)) { + specType = Not; + } + break; + case Begin: + case End: + case Interrupt: + gn = Targ_FindNode(line, TARG_CREATE); + gn->type |= OP_NOTMAIN|OP_SPECIAL; + (void)Lst_AtEnd(targets, (ClientData)gn); + break; + case Default: + gn = Targ_NewGN(".DEFAULT"); + gn->type |= (OP_NOTMAIN|OP_TRANSFORM); + (void)Lst_AtEnd(targets, (ClientData)gn); + DEFAULT = gn; + break; + case NotParallel: + { + maxJobs = 1; + break; + } + case SingleShell: + compatMake = TRUE; + break; + case Order: + predecessor = NILGNODE; + break; + default: + break; + } + } else if (strncmp(line, ".PATH", 5) == 0) { + /* + * .PATH has to be handled specially. + * Call on the suffix module to give us a path to + * modify. + */ + Lst path; + + specType = ExPath; + path = Suff_GetPath(&line[5]); + if (path == NILLST) { + Parse_Error(PARSE_FATAL, + "Suffix '%s' not defined (yet)", + &line[5]); + return; + } else { + if (paths == (Lst)NULL) { + paths = Lst_Init(FALSE); + } + (void)Lst_AtEnd(paths, (ClientData)path); + } + } + } + + /* + * Have word in line. Get or create its node and stick it at + * the end of the targets list + */ + if ((specType == Not) && (*line != '\0')) { + if (Dir_HasWildcards(line)) { + /* + * Targets are to be sought only in the current directory, + * so create an empty path for the thing. Note we need to + * use Dir_Destroy in the destruction of the path as the + * Dir module could have added a directory to the path... + */ + Lst emptyPath = Lst_Init(FALSE); + + Dir_Expand(line, emptyPath, curTargs); + + Lst_Destroy(emptyPath, Dir_Destroy); + } else { + /* + * No wildcards, but we want to avoid code duplication, + * so create a list with the word on it. + */ + (void)Lst_AtEnd(curTargs, (ClientData)line); + } + + while(!Lst_IsEmpty(curTargs)) { + char *targName = (char *)Lst_DeQueue(curTargs); + + if (!Suff_IsTransform (targName)) { + gn = Targ_FindNode(targName, TARG_CREATE); + } else { + gn = Suff_AddTransform(targName); + } + + (void)Lst_AtEnd(targets, (ClientData)gn); + } + } else if (specType == ExPath && *line != '.' && *line != '\0') { + Parse_Error(PARSE_WARNING, "Extra target (%s) ignored", line); + } + + *cp = savec; + /* + * If it is a special type and not .PATH, it's the only target we + * allow on this line... + */ + if (specType != Not && specType != ExPath) { + Boolean warn = FALSE; + + while (*cp && (ParseIsEscaped(lstart, cp) || + ((*cp != '!') && (*cp != ':')))) { + if (ParseIsEscaped(lstart, cp) || + (*cp != ' ' && *cp != '\t')) { + warn = TRUE; + } + cp++; + } + if (warn) { + Parse_Error(PARSE_WARNING, "Extra target ignored"); + } + } else { + while (*cp && isspace ((unsigned char)*cp)) { + cp++; + } + } + line = cp; + } while (*line && (ParseIsEscaped(lstart, line) || + ((*line != '!') && (*line != ':')))); + + /* + * Don't need the list of target names anymore... + */ + Lst_Destroy(curTargs, NOFREE); + + if (!Lst_IsEmpty(targets)) { + switch(specType) { + default: + Parse_Error(PARSE_WARNING, "Special and mundane targets don't mix. Mundane ones ignored"); + break; + case Default: + case Begin: + case End: + case Interrupt: + /* + * These four create nodes on which to hang commands, so + * targets shouldn't be empty... + */ + case Not: + /* + * Nothing special here -- targets can be empty if it wants. + */ + break; + } + } + + /* + * Have now parsed all the target names. Must parse the operator next. The + * result is left in op . + */ + if (*cp == '!') { + op = OP_FORCE; + } else if (*cp == ':') { + if (cp[1] == ':') { + op = OP_DOUBLEDEP; + cp++; + } else { + op = OP_DEPENDS; + } + } else { + Parse_Error(PARSE_FATAL, "Missing dependency operator"); + return; + } + + cp++; /* Advance beyond operator */ + + Lst_ForEach(targets, ParseDoOp, (ClientData)&op); + + /* + * Get to the first source + */ + while (*cp && isspace ((unsigned char)*cp)) { + cp++; + } + line = cp; + + /* + * Several special targets take different actions if present with no + * sources: + * a .SUFFIXES line with no sources clears out all old suffixes + * a .PRECIOUS line makes all targets precious + * a .IGNORE line ignores errors for all targets + * a .SILENT line creates silence when making all targets + * a .PATH removes all directories from the search path(s). + */ + if (!*line) { + switch (specType) { + case Suffixes: + Suff_ClearSuffixes(); + break; + case Precious: + allPrecious = TRUE; + break; + case Ignore: + ignoreErrors = TRUE; + break; + case Silent: + beSilent = TRUE; + break; + case ExPath: + Lst_ForEach(paths, ParseClearPath, (ClientData)NULL); + Dir_SetPATH(); + break; +#ifdef POSIX + case Posix: + Var_Set("%POSIX", "1003.2", VAR_GLOBAL, 0); + break; +#endif + default: + break; + } + } else if (specType == MFlags) { + /* + * Call on functions in main.c to deal with these arguments and + * set the initial character to a null-character so the loop to + * get sources won't get anything + */ + Main_ParseArgLine(line); + *line = '\0'; + } else if (specType == ExShell) { + if (Job_ParseShell(line) != SUCCESS) { + Parse_Error(PARSE_FATAL, "improper shell specification"); + return; + } + *line = '\0'; + } else if ((specType == NotParallel) || (specType == SingleShell)) { + *line = '\0'; + } + + /* + * NOW GO FOR THE SOURCES + */ + if ((specType == Suffixes) || (specType == ExPath) || + (specType == Includes) || (specType == Libs) || + (specType == Null) || (specType == ExObjdir)) + { + while (*line) { + /* + * If the target was one that doesn't take files as its sources + * but takes something like suffixes, we take each + * space-separated word on the line as a something and deal + * with it accordingly. + * + * If the target was .SUFFIXES, we take each source as a + * suffix and add it to the list of suffixes maintained by the + * Suff module. + * + * If the target was a .PATH, we add the source as a directory + * to search on the search path. + * + * If it was .INCLUDES, the source is taken to be the suffix of + * files which will be #included and whose search path should + * be present in the .INCLUDES variable. + * + * If it was .LIBS, the source is taken to be the suffix of + * files which are considered libraries and whose search path + * should be present in the .LIBS variable. + * + * If it was .NULL, the source is the suffix to use when a file + * has no valid suffix. + * + * If it was .OBJDIR, the source is a new definition for .OBJDIR, + * and will cause make to do a new chdir to that path. + */ + while (*cp && !isspace ((unsigned char)*cp)) { + cp++; + } + savec = *cp; + *cp = '\0'; + switch (specType) { + case Suffixes: + Suff_AddSuffix(line, &mainNode); + break; + case ExPath: + Lst_ForEach(paths, ParseAddDir, (ClientData)line); + break; + case Includes: + Suff_AddInclude(line); + break; + case Libs: + Suff_AddLib(line); + break; + case Null: + Suff_SetNull(line); + break; + case ExObjdir: + Main_SetObjdir(line); + break; + default: + break; + } + *cp = savec; + if (savec != '\0') { + cp++; + } + while (*cp && isspace ((unsigned char)*cp)) { + cp++; + } + line = cp; + } + if (paths) { + Lst_Destroy(paths, NOFREE); + } + if (specType == ExPath) + Dir_SetPATH(); + } else { + /* + * We don't need ParseDoSpecialSrc unless .WAIT is present. + */ + hasWait = (strstr(line, ".WAIT") != NULL); + + while (*line) { + /* + * The targets take real sources, so we must beware of archive + * specifications (i.e. things with left parentheses in them) + * and handle them accordingly. + */ + while (*cp && !isspace ((unsigned char)*cp)) { + if ((*cp == LPAREN) && (cp > line) && (cp[-1] != '$')) { + /* + * Only stop for a left parenthesis if it isn't at the + * start of a word (that'll be for variable changes + * later) and isn't preceded by a dollar sign (a dynamic + * source). + */ + break; + } else { + cp++; + } + } + + if (*cp == LPAREN) { + sources = Lst_Init(FALSE); + if (Arch_ParseArchive(&line, sources, VAR_CMD) != SUCCESS) { + Parse_Error(PARSE_FATAL, + "Error in source archive spec \"%s\"", line); + return; + } + + while (!Lst_IsEmpty (sources)) { + gn = (GNode *)Lst_DeQueue(sources); + ParseDoSrc(tOp, gn->name, curSrcs, hasWait); + } + Lst_Destroy(sources, NOFREE); + cp = line; + } else { + if (*cp) { + *cp = '\0'; + cp += 1; + } + + ParseDoSrc(tOp, line, curSrcs, hasWait); + } + while (*cp && isspace ((unsigned char)*cp)) { + cp++; + } + line = cp; + } + } + + if (mainNode == NILGNODE) { + /* + * If we have yet to decide on a main target to make, in the + * absence of any user input, we want the first target on + * the first dependency line that is actually a real target + * (i.e. isn't a .USE or .EXEC rule) to be made. + */ + Lst_ForEach(targets, ParseFindMain, (ClientData)0); + } + + /* + * Finally, destroy the list of sources + */ + Lst_Destroy(curSrcs, NOFREE); +} + +/*- + *--------------------------------------------------------------------- + * Parse_IsVar -- + * Return TRUE if the passed line is a variable assignment. A variable + * assignment consists of a single word followed by optional whitespace + * followed by either a += or an = operator. + * This function is used both by the Parse_File function and main when + * parsing the command-line arguments. + * + * Input: + * line the line to check + * + * Results: + * TRUE if it is. FALSE if it ain't + * + * Side Effects: + * none + *--------------------------------------------------------------------- + */ +Boolean +Parse_IsVar(char *line) +{ + Boolean wasSpace = FALSE; /* set TRUE if found a space */ + Boolean haveName = FALSE; /* Set TRUE if have a variable name */ + int level = 0; +#define ISEQOPERATOR(c) \ + (((c) == '+') || ((c) == ':') || ((c) == '?') || ((c) == '!')) + + /* + * Skip to variable name + */ + for (;(*line == ' ') || (*line == '\t'); line++) + continue; + + for (; *line != '=' || level != 0; line++) + switch (*line) { + case '\0': + /* + * end-of-line -- can't be a variable assignment. + */ + return FALSE; + + case ' ': + case '\t': + /* + * there can be as much white space as desired so long as there is + * only one word before the operator + */ + wasSpace = TRUE; + break; + + case LPAREN: + case '{': + level++; + break; + + case '}': + case RPAREN: + level--; + break; + + default: + if (wasSpace && haveName) { + if (ISEQOPERATOR(*line)) { + /* + * We must have a finished word + */ + if (level != 0) + return FALSE; + + /* + * When an = operator [+?!:] is found, the next + * character must be an = or it ain't a valid + * assignment. + */ + if (line[1] == '=') + return haveName; +#ifdef SUNSHCMD + /* + * This is a shell command + */ + if (strncmp(line, ":sh", 3) == 0) + return haveName; +#endif + } + /* + * This is the start of another word, so not assignment. + */ + return FALSE; + } + else { + haveName = TRUE; + wasSpace = FALSE; + } + break; + } + + return haveName; +} + +/*- + *--------------------------------------------------------------------- + * Parse_DoVar -- + * Take the variable assignment in the passed line and do it in the + * global context. + * + * Note: There is a lexical ambiguity with assignment modifier characters + * in variable names. This routine interprets the character before the = + * as a modifier. Therefore, an assignment like + * C++=/usr/bin/CC + * is interpreted as "C+ +=" instead of "C++ =". + * + * Input: + * line a line guaranteed to be a variable assignment. + * This reduces error checks + * ctxt Context in which to do the assignment + * + * Results: + * none + * + * Side Effects: + * the variable structure of the given variable name is altered in the + * global context. + *--------------------------------------------------------------------- + */ +void +Parse_DoVar(char *line, GNode *ctxt) +{ + char *cp; /* pointer into line */ + enum { + VAR_SUBST, VAR_APPEND, VAR_SHELL, VAR_NORMAL + } type; /* Type of assignment */ + char *opc; /* ptr to operator character to + * null-terminate the variable name */ + Boolean freeCp = FALSE; /* TRUE if cp needs to be freed, + * i.e. if any variable expansion was + * performed */ + /* + * Avoid clobbered variable warnings by forcing the compiler + * to ``unregister'' variables + */ +#if __GNUC__ + (void) &cp; + (void) &line; +#endif + + /* + * Skip to variable name + */ + while ((*line == ' ') || (*line == '\t')) { + line++; + } + + /* + * Skip to operator character, nulling out whitespace as we go + */ + for (cp = line + 1; *cp != '='; cp++) { + if (isspace ((unsigned char)*cp)) { + *cp = '\0'; + } + } + opc = cp-1; /* operator is the previous character */ + *cp++ = '\0'; /* nuke the = */ + + /* + * Check operator type + */ + switch (*opc) { + case '+': + type = VAR_APPEND; + *opc = '\0'; + break; + + case '?': + /* + * If the variable already has a value, we don't do anything. + */ + *opc = '\0'; + if (Var_Exists(line, ctxt)) { + return; + } else { + type = VAR_NORMAL; + } + break; + + case ':': + type = VAR_SUBST; + *opc = '\0'; + break; + + case '!': + type = VAR_SHELL; + *opc = '\0'; + break; + + default: +#ifdef SUNSHCMD + while (opc > line && *opc != ':') + opc--; + + if (strncmp(opc, ":sh", 3) == 0) { + type = VAR_SHELL; + *opc = '\0'; + break; + } +#endif + type = VAR_NORMAL; + break; + } + + while (isspace ((unsigned char)*cp)) { + cp++; + } + + if (type == VAR_APPEND) { + Var_Append(line, cp, ctxt); + } else if (type == VAR_SUBST) { + /* + * Allow variables in the old value to be undefined, but leave their + * invocation alone -- this is done by forcing oldVars to be false. + * XXX: This can cause recursive variables, but that's not hard to do, + * and this allows someone to do something like + * + * CFLAGS = $(.INCLUDES) + * CFLAGS := -I.. $(CFLAGS) + * + * And not get an error. + */ + Boolean oldOldVars = oldVars; + + oldVars = FALSE; + + /* + * make sure that we set the variable the first time to nothing + * so that it gets substituted! + */ + if (!Var_Exists(line, ctxt)) + Var_Set(line, "", ctxt, 0); + + cp = Var_Subst(NULL, cp, ctxt, FALSE); + oldVars = oldOldVars; + freeCp = TRUE; + + Var_Set(line, cp, ctxt, 0); + } else if (type == VAR_SHELL) { + char *res; + const char *err; + + if (strchr(cp, '$') != NULL) { + /* + * There's a dollar sign in the command, so perform variable + * expansion on the whole thing. The resulting string will need + * freeing when we're done, so set freeCmd to TRUE. + */ + cp = Var_Subst(NULL, cp, VAR_CMD, TRUE); + freeCp = TRUE; + } + + res = Cmd_Exec(cp, &err); + Var_Set(line, res, ctxt, 0); + free(res); + + if (err) + Parse_Error(PARSE_WARNING, err, cp); + } else { + /* + * Normal assignment -- just do it. + */ + Var_Set(line, cp, ctxt, 0); + } + if (strcmp(line, MAKEOVERRIDES) == 0) + Main_ExportMAKEFLAGS(FALSE); /* re-export MAKEFLAGS */ + else if (strcmp(line, ".CURDIR") == 0) { + /* + * Somone is being (too?) clever... + * Let's pretend they know what they are doing and + * re-initialize the 'cur' Path. + */ + Dir_InitCur(cp); + Dir_SetPATH(); + } + if (freeCp) + free(cp); +} + + +/*- + * ParseAddCmd -- + * Lst_ForEach function to add a command line to all targets + * + * Input: + * gnp the node to which the command is to be added + * cmd the command to add + * + * Results: + * Always 0 + * + * Side Effects: + * A new element is added to the commands list of the node. + */ +static int +ParseAddCmd(ClientData gnp, ClientData cmd) +{ + GNode *gn = (GNode *)gnp; + /* if target already supplied, ignore commands */ + if ((gn->type & OP_DOUBLEDEP) && !Lst_IsEmpty (gn->cohorts)) + gn = (GNode *)Lst_Datum(Lst_Last(gn->cohorts)); + if (!(gn->type & OP_HAS_COMMANDS)) { + (void)Lst_AtEnd(gn->commands, cmd); + ParseMark(gn); + } else { +#ifdef notyet + /* XXX: We cannot do this until we fix the tree */ + (void)Lst_AtEnd(gn->commands, cmd); + Parse_Error(PARSE_WARNING, + "overriding commands for target \"%s\"; " + "previous commands defined at %s: %d ignored", + gn->name, gn->fname, gn->lineno); +#else + Parse_Error(PARSE_WARNING, + "duplicate script for target \"%s\" ignored", + gn->name); + ParseErrorInternal(gn->fname, gn->lineno, PARSE_WARNING, + "using previous script for \"%s\" defined here", + gn->name); +#endif + } + return(0); +} + +/*- + *----------------------------------------------------------------------- + * ParseHasCommands -- + * Callback procedure for Parse_File when destroying the list of + * targets on the last dependency line. Marks a target as already + * having commands if it does, to keep from having shell commands + * on multiple dependency lines. + * + * Input: + * gnp Node to examine + * + * Results: + * None + * + * Side Effects: + * OP_HAS_COMMANDS may be set for the target. + * + *----------------------------------------------------------------------- + */ +static void +ParseHasCommands(ClientData gnp) +{ + GNode *gn = (GNode *)gnp; + if (!Lst_IsEmpty(gn->commands)) { + gn->type |= OP_HAS_COMMANDS; + } +} + +/*- + *----------------------------------------------------------------------- + * Parse_AddIncludeDir -- + * Add a directory to the path searched for included makefiles + * bracketed by double-quotes. Used by functions in main.c + * + * Input: + * dir The name of the directory to add + * + * Results: + * None. + * + * Side Effects: + * The directory is appended to the list. + * + *----------------------------------------------------------------------- + */ +void +Parse_AddIncludeDir(char *dir) +{ + (void)Dir_AddDir(parseIncPath, dir); +} + +/*- + *--------------------------------------------------------------------- + * ParseDoInclude -- + * Push to another file. + * + * The input is the line minus the `.'. A file spec is a string + * enclosed in <> or "". The former is looked for only in sysIncPath. + * The latter in . and the directories specified by -I command line + * options + * + * Results: + * None + * + * Side Effects: + * A structure is added to the includes Lst and readProc, lineno, + * fname and curFILE are altered for the new file + *--------------------------------------------------------------------- + */ +static void +ParseDoInclude(char *line) +{ + char *fullname; /* full pathname of file */ + IFile *oldFile; /* state associated with current file */ + char endc; /* the character which ends the file spec */ + char *cp; /* current position in file spec */ + Boolean isSystem; /* TRUE if makefile is a system makefile */ + int silent = (*line != 'i') ? 1 : 0; + char *file = &line[7 + silent]; + + /* + * Skip to delimiter character so we know where to look + */ + while ((*file == ' ') || (*file == '\t')) { + file++; + } + + if ((*file != '"') && (*file != '<')) { + Parse_Error(PARSE_FATAL, + ".include filename must be delimited by '\"' or '<'"); + return; + } + + /* + * Set the search path on which to find the include file based on the + * characters which bracket its name. Angle-brackets imply it's + * a system Makefile while double-quotes imply it's a user makefile + */ + if (*file == '<') { + isSystem = TRUE; + endc = '>'; + } else { + isSystem = FALSE; + endc = '"'; + } + + /* + * Skip to matching delimiter + */ + for (cp = ++file; *cp && *cp != endc; cp++) { + continue; + } + + if (*cp != endc) { + Parse_Error(PARSE_FATAL, + "Unclosed %cinclude filename. '%c' expected", + '.', endc); + return; + } + *cp = '\0'; + + /* + * Substitute for any variables in the file name before trying to + * find the thing. + */ + file = Var_Subst(NULL, file, VAR_CMD, FALSE); + + /* + * Now we know the file's name and its search path, we attempt to + * find the durn thing. A return of NULL indicates the file don't + * exist. + */ + fullname = NULL; + + if (!isSystem) { + /* + * Include files contained in double-quotes are first searched for + * relative to the including file's location. We don't want to + * cd there, of course, so we just tack on the old file's + * leading path components and call Dir_FindFile to see if + * we can locate the beast. + */ + char *prefEnd, *Fname; + + /* Make a temporary copy of this, to be safe. */ + Fname = estrdup(curFile.fname); + + prefEnd = strrchr(Fname, '/'); + if (prefEnd != NULL) { + char *newName; + + *prefEnd = '\0'; + if (file[0] == '/') + newName = estrdup(file); + else + newName = str_concat(Fname, file, STR_ADDSLASH); + fullname = Dir_FindFile(newName, parseIncPath); + if (fullname == NULL) { + fullname = Dir_FindFile(newName, dirSearchPath); + } + free(newName); + *prefEnd = '/'; + } else { + fullname = NULL; + } + free(Fname); + if (fullname == NULL) { + /* + * Makefile wasn't found in same directory as included makefile. + * Search for it first on the -I search path, + * then on the .PATH search path, if not found in a -I directory. + * XXX: Suffix specific? + */ + fullname = Dir_FindFile(file, parseIncPath); + if (fullname == NULL) { + fullname = Dir_FindFile(file, dirSearchPath); + } + } + } + + /* Looking for a system file or file still not found */ + if (fullname == NULL) { + /* + * Look for it on the system path + */ + fullname = Dir_FindFile(file, Lst_IsEmpty(sysIncPath) ? defIncPath : sysIncPath); + } + + if (fullname == NULL) { + *cp = endc; + if (!silent) + Parse_Error(PARSE_FATAL, "Could not find %s", file); + return; + } + + free(file); + + /* + * Once we find the absolute path to the file, we get to save all the + * state from the current file before we can start reading this + * include file. The state is stored in an IFile structure which + * is placed on a list with other IFile structures. The list makes + * a very nice stack to track how we got here... + */ + oldFile = emalloc(sizeof(IFile)); + + memcpy(oldFile, &curFile, sizeof(IFile)); + + (void)Lst_AtFront(includes, (ClientData)oldFile); + + /* + * Once the previous state has been saved, we can get down to reading + * the new file. We set up the name of the file to be the absolute + * name of the include file so error messages refer to the right + * place. Naturally enough, we start reading at line number 0. + */ + curFile.fname = fullname; + curFile.lineno = 0; + + ParseSetParseFile(curFile.fname); + + curFile.F = fopen(fullname, "r"); + curFile.P = NULL; + + if (curFile.F == (FILE * ) NULL) { + if (!silent) + Parse_Error(PARSE_FATAL, "Cannot open %s", fullname); + /* + * Pop to previous file + */ + (void)ParseEOF(0); + } +} + + +/*- + *--------------------------------------------------------------------- + * ParseSetParseFile -- + * Set the .PARSEDIR and .PARSEFILE variables to the dirname and + * basename of the given filename + * + * Results: + * None + * + * Side Effects: + * The .PARSEDIR and .PARSEFILE variables are overwritten by the + * dirname and basename of the given filename. + *--------------------------------------------------------------------- + */ +static void +ParseSetParseFile(char *filename) +{ + char *slash; + + slash = strrchr(filename, '/'); + if (slash == 0) { + Var_Set(".PARSEDIR", ".", VAR_GLOBAL, 0); + Var_Set(".PARSEFILE", filename, VAR_GLOBAL, 0); + } else { + *slash = '\0'; + Var_Set(".PARSEDIR", filename, VAR_GLOBAL, 0); + Var_Set(".PARSEFILE", slash+1, VAR_GLOBAL, 0); + *slash = '/'; + } +} + + +/*- + *--------------------------------------------------------------------- + * Parse_FromString -- + * Start Parsing from the given string + * + * Results: + * None + * + * Side Effects: + * A structure is added to the includes Lst and readProc, lineno, + * fname and curFILE are altered for the new file + *--------------------------------------------------------------------- + */ +void +Parse_FromString(char *str, int lineno) +{ + IFile *oldFile; /* state associated with this file */ + + if (DEBUG(FOR)) + (void)fprintf(stderr, "%s\n---- at line %d\n", str, lineno); + + oldFile = emalloc(sizeof(IFile)); + memcpy(oldFile, &curFile, sizeof(IFile)); + + (void)Lst_AtFront(includes, (ClientData)oldFile); + + curFile.F = NULL; + curFile.P = emalloc(sizeof(PTR)); + curFile.P->str = curFile.P->ptr = str; + curFile.lineno = lineno; + curFile.fname = estrdup(curFile.fname); +} + + +#ifdef SYSVINCLUDE +/*- + *--------------------------------------------------------------------- + * ParseTraditionalInclude -- + * Push to another file. + * + * The input is the current line. The file name(s) are + * following the "include". + * + * Results: + * None + * + * Side Effects: + * A structure is added to the includes Lst and readProc, lineno, + * fname and curFILE are altered for the new file + *--------------------------------------------------------------------- + */ +static void +ParseTraditionalInclude(char *line) +{ + char *fullname; /* full pathname of file */ + IFile *oldFile; /* state associated with current file */ + char *cp; /* current position in file spec */ + char *prefEnd; + int done = 0; + int silent = (line[0] != 'i') ? 1 : 0; + char *file = &line[silent + 7]; + char *cfname; + size_t clineno; + + cfname = curFile.fname; + clineno = curFile.lineno; + + /* + * Skip over whitespace + */ + while (isspace((unsigned char)*file)) + file++; + + if (*file == '\0') { + Parse_Error(PARSE_FATAL, + "Filename missing from \"include\""); + return; + } + + for (; !done; file = cp + 1) { + /* + * Skip to end of line or next whitespace + */ + for (cp = file; *cp && !isspace((unsigned char) *cp); cp++) + continue; + + if (*cp) + *cp = '\0'; + else + done = 1; + + /* + * Substitute for any variables in the file name before trying to + * find the thing. + */ + file = Var_Subst(NULL, file, VAR_CMD, FALSE); + + /* + * Now we know the file's name, we attempt to find the durn thing. + * A return of NULL indicates the file don't exist. + * + * Include files are first searched for relative to the including + * file's location. We don't want to cd there, of course, so we + * just tack on the old file's leading path components and call + * Dir_FindFile to see if we can locate the beast. + * XXX - this *does* search in the current directory, right? + */ + + prefEnd = strrchr(cfname, '/'); + if (prefEnd != NULL) { + char *newName; + + *prefEnd = '\0'; + newName = str_concat(cfname, file, STR_ADDSLASH); + fullname = Dir_FindFile(newName, parseIncPath); + if (fullname == NULL) { + fullname = Dir_FindFile(newName, dirSearchPath); + } + free(newName); + *prefEnd = '/'; + } else { + fullname = NULL; + } + + if (fullname == NULL) { + /* + * System makefile or makefile wasn't found in same directory as + * included makefile. Search for it first on the -I search path, + * then on the .PATH search path, if not found in a + * -I directory. XXX: Suffix specific? + */ + fullname = Dir_FindFile(file, parseIncPath); + if (fullname == NULL) { + fullname = Dir_FindFile(file, dirSearchPath); + } + } + + if (fullname == NULL) { + /* + * Still haven't found the makefile. Look for it on the system + * path as a last resort. + */ + fullname = Dir_FindFile(file, + Lst_IsEmpty(sysIncPath) ? defIncPath : sysIncPath); + } + + if (fullname == NULL) { + if (!silent) + ParseErrorInternal(cfname, clineno, PARSE_FATAL, + "Could not find %s", file); + free(file); + continue; + } + + free(file); + + /* + * Once we find the absolute path to the file, we get to save all + * the state from the current file before we can start reading this + * include file. The state is stored in an IFile structure which + * is placed on a list with other IFile structures. The list makes + * a very nice stack to track how we got here... + */ + oldFile = emalloc(sizeof(IFile)); + memcpy(oldFile, &curFile, sizeof(IFile)); + + (void)Lst_AtFront(includes, (ClientData)oldFile); + + /* + * Once the previous state has been saved, we can get down to + * reading the new file. We set up the name of the file to be the + * absolute name of the include file so error messages refer to the + * right place. Naturally enough, we start reading at line number 0. + */ + curFile.fname = fullname; + curFile.lineno = 0; + + curFile.F = fopen(fullname, "r"); + curFile.P = NULL; + + if (curFile.F == NULL) { + if (!silent) + ParseErrorInternal(cfname, clineno, PARSE_FATAL, + "Cannot open %s", fullname); + /* + * Pop to previous file + */ + (void)ParseEOF(1); + } + } +} +#endif + +/*- + *--------------------------------------------------------------------- + * ParseEOF -- + * Called when EOF is reached in the current file. If we were reading + * an include file, the includes stack is popped and things set up + * to go back to reading the previous file at the previous location. + * + * Results: + * CONTINUE if there's more to do. DONE if not. + * + * Side Effects: + * The old curFILE, is closed. The includes list is shortened. + * lineno, curFILE, and fname are changed if CONTINUE is returned. + *--------------------------------------------------------------------- + */ +static int +ParseEOF(int opened) +{ + IFile *ifile; /* the state on the top of the includes stack */ + + if (Lst_IsEmpty(includes)) { + Var_Delete(".PARSEDIR", VAR_GLOBAL); + Var_Delete(".PARSEFILE", VAR_GLOBAL); + return (DONE); + } + + ifile = (IFile *)Lst_DeQueue(includes); + + /* XXX dispose of curFile info */ + free( curFile.fname); + if (opened && curFile.F) + (void)fclose(curFile.F); + if (curFile.P) { + free(curFile.P->str); + free(curFile.P); + } + + memcpy(&curFile, ifile, sizeof(IFile)); + + free(ifile); + + /* pop the PARSEDIR/PARSEFILE variables */ + ParseSetParseFile(curFile.fname); + return (CONTINUE); +} + +/*- + *--------------------------------------------------------------------- + * ParseReadc -- + * Read a character from the current file + * + * Results: + * The character that was read + * + * Side Effects: + *--------------------------------------------------------------------- + */ +static __inline int +ParseReadc(void) +{ + if (curFile.F) + return fgetc(curFile.F); + + if (curFile.P && *curFile.P->ptr) + return *curFile.P->ptr++; + return EOF; +} + + +/*- + *--------------------------------------------------------------------- + * ParseUnreadc -- + * Put back a character to the current file + * + * Results: + * None. + * + * Side Effects: + *--------------------------------------------------------------------- + */ +static void +ParseUnreadc(int c) +{ + if (curFile.F) { + ungetc(c, curFile.F); + return; + } + if (curFile.P) { + *--(curFile.P->ptr) = c; + return; + } +} + + +/* ParseSkipLine(): + * Grab the next line + * + * Input: + * skip Skip lines that don't start with . + * keep_newline Keep newline character as is. + * + */ +static char * +ParseSkipLine(int skip, int keep_newline) +{ + char *line; + int c, lastc, lineLength = 0; + Buffer buf; + + buf = Buf_Init(MAKE_BSIZE); + + do { + Buf_Discard(buf, lineLength); + lastc = '\0'; + + while (((c = ParseReadc()) != '\n' || lastc == '\\') + && c != EOF) { + if (c == '\n') { + if (keep_newline) + Buf_AddByte(buf, (Byte)c); + else + Buf_ReplaceLastByte(buf, (Byte)' '); + curFile.lineno++; + + while ((c = ParseReadc()) == ' ' || c == '\t'); + + if (c == EOF) + break; + } + + Buf_AddByte(buf, (Byte)c); + lastc = c; + } + + if (c == EOF) { + Parse_Error(PARSE_FATAL, "Unclosed conditional/for loop"); + Buf_Destroy(buf, TRUE); + return(NULL); + } + + curFile.lineno++; + Buf_AddByte(buf, (Byte)'\0'); + line = (char *)Buf_GetAll(buf, &lineLength); + } while (skip == 1 && line[0] != '.'); + + Buf_Destroy(buf, FALSE); + return line; +} + + +/*- + *--------------------------------------------------------------------- + * ParseReadLine -- + * Read an entire line from the input file. Called only by Parse_File. + * To facilitate escaped newlines and what have you, a character is + * buffered in 'lastc', which is '\0' when no characters have been + * read. When we break out of the loop, c holds the terminating + * character and lastc holds a character that should be added to + * the line (unless we don't read anything but a terminator). + * + * Results: + * A line w/o its newline + * + * Side Effects: + * Only those associated with reading a character + *--------------------------------------------------------------------- + */ +static char * +ParseReadLine(void) +{ + Buffer buf; /* Buffer for current line */ + int c; /* the current character */ + int lastc; /* The most-recent character */ + Boolean semiNL; /* treat semi-colons as newlines */ + Boolean ignDepOp; /* TRUE if should ignore dependency operators + * for the purposes of setting semiNL */ + Boolean ignComment; /* TRUE if should ignore comments (in a + * shell command */ + char *line; /* Result */ + char *ep; /* to strip trailing blanks */ + int lineLength; /* Length of result */ + int lineno; /* Saved line # */ + + semiNL = FALSE; + ignDepOp = FALSE; + ignComment = FALSE; + + /* + * Handle special-characters at the beginning of the line. Either a + * leading tab (shell command) or pound-sign (possible conditional) + * forces us to ignore comments and dependency operators and treat + * semi-colons as semi-colons (by leaving semiNL FALSE). This also + * discards completely blank lines. + */ + for (;;) { + c = ParseReadc(); + + if (c == '\t') { + ignComment = ignDepOp = TRUE; + break; + } else if (c == '\n') { + curFile.lineno++; + } else if (c == '#') { + ParseUnreadc(c); + break; + } else { + /* + * Anything else breaks out without doing anything + */ + break; + } + } + + if (c != EOF) { + lastc = c; + buf = Buf_Init(MAKE_BSIZE); + + while (((c = ParseReadc()) != '\n' || (lastc == '\\')) && + (c != EOF)) + { +test_char: + switch(c) { + case '\n': + /* + * Escaped newline: read characters until a non-space or an + * unescaped newline and replace them all by a single space. + * This is done by storing the space over the backslash and + * dropping through with the next nonspace. If it is a + * semi-colon and semiNL is TRUE, it will be recognized as a + * newline in the code below this... + */ + curFile.lineno++; + lastc = ' '; + while ((c = ParseReadc()) == ' ' || c == '\t') { + continue; + } + if (c == EOF || c == '\n') { + goto line_read; + } else { + /* + * Check for comments, semiNL's, etc. -- easier than + * ParseUnreadc(c); continue; + */ + goto test_char; + } + /*NOTREACHED*/ + break; + + case ';': + /* + * Semi-colon: Need to see if it should be interpreted as a + * newline + */ + if (semiNL) { + /* + * To make sure the command that may be following this + * semi-colon begins with a tab, we push one back into the + * input stream. This will overwrite the semi-colon in the + * buffer. If there is no command following, this does no + * harm, since the newline remains in the buffer and the + * whole line is ignored. + */ + ParseUnreadc('\t'); + goto line_read; + } + break; + case '=': + if (!semiNL) { + /* + * Haven't seen a dependency operator before this, so this + * must be a variable assignment -- don't pay attention to + * dependency operators after this. + */ + ignDepOp = TRUE; + } else if (lastc == ':' || lastc == '!') { + /* + * Well, we've seen a dependency operator already, but it + * was the previous character, so this is really just an + * expanded variable assignment. Revert semi-colons to + * being just semi-colons again and ignore any more + * dependency operators. + * + * XXX: Note that a line like "foo : a:=b" will blow up, + * but who'd write a line like that anyway? + */ + ignDepOp = TRUE; semiNL = FALSE; + } + break; + case '#': + if (!ignComment) { + if ( +#if 0 + compatMake && +#endif + (lastc != '\\')) { + /* + * If the character is a hash mark and it isn't escaped + * (or we're being compatible), the thing is a comment. + * Skip to the end of the line. + */ + do { + c = ParseReadc(); + /* + * If we found a backslash not escaped + * itself it means that the comment is + * going to continue in the next line. + */ + if (c == '\\') + ParseReadc(); + } while ((c != '\n') && (c != EOF)); + goto line_read; + } else { + /* + * Don't add the backslash. Just let the # get copied + * over. + */ + lastc = c; + continue; + } + } + break; + case ':': + case '!': + if (!ignDepOp && (c == ':' || c == '!')) { + /* + * A semi-colon is recognized as a newline only on + * dependency lines. Dependency lines are lines with a + * colon or an exclamation point. Ergo... + */ + semiNL = TRUE; + } + break; + } + /* + * Copy in the previous character and save this one in lastc. + */ + Buf_AddByte(buf, (Byte)lastc); + lastc = c; + + } + line_read: + curFile.lineno++; + + if (lastc != '\0') { + Buf_AddByte(buf, (Byte)lastc); + } + Buf_AddByte(buf, (Byte)'\0'); + line = (char *)Buf_GetAll(buf, &lineLength); + Buf_Destroy(buf, FALSE); + + /* + * Strip trailing blanks and tabs from the line. + * Do not strip a blank or tab that is preceded by + * a '\' + */ + ep = line; + while (*ep) + ++ep; + while (ep > line + 1 && (ep[-1] == ' ' || ep[-1] == '\t')) { + if (ep > line + 1 && ep[-2] == '\\') + break; + --ep; + } + *ep = 0; + + if (line[0] == '.') { + /* + * The line might be a conditional. Ask the conditional module + * about it and act accordingly + */ + switch (Cond_Eval(line)) { + case COND_SKIP: + /* + * Skip to next conditional that evaluates to COND_PARSE. + */ + do { + free(line); + line = ParseSkipLine(1, 0); + } while (line && Cond_Eval(line) != COND_PARSE); + if (line == NULL) + break; + /*FALLTHRU*/ + case COND_PARSE: + free(line); + line = ParseReadLine(); + break; + case COND_INVALID: + lineno = curFile.lineno; + if (For_Eval(line)) { + int ok; + free(line); + do { + /* + * Skip after the matching end + */ + line = ParseSkipLine(0, 1); + if (line == NULL) { + Parse_Error(PARSE_FATAL, + "Unexpected end of file in for loop.\n"); + break; + } + ok = For_Eval(line); + free(line); + } + while (ok); + if (line != NULL) + For_Run(lineno); + line = ParseReadLine(); + } + break; + } + } + return (line); + + } else { + /* + * Hit end-of-file, so return a NULL line to indicate this. + */ + return(NULL); + } +} + +/*- + *----------------------------------------------------------------------- + * ParseFinishLine -- + * Handle the end of a dependency group. + * + * Results: + * Nothing. + * + * Side Effects: + * inLine set FALSE. 'targets' list destroyed. + * + *----------------------------------------------------------------------- + */ +static void +ParseFinishLine(void) +{ + if (inLine) { + Lst_ForEach(targets, Suff_EndTransform, (ClientData)NULL); + Lst_Destroy(targets, ParseHasCommands); + targets = NULL; + inLine = FALSE; + } +} + + +/*- + *--------------------------------------------------------------------- + * Parse_File -- + * Parse a file into its component parts, incorporating it into the + * current dependency graph. This is the main function and controls + * almost every other function in this module + * + * Input: + * name the name of the file being read + * stream Stream open to makefile to parse + * + * Results: + * None + * + * Side Effects: + * Loads. Nodes are added to the list of all targets, nodes and links + * are added to the dependency graph. etc. etc. etc. + *--------------------------------------------------------------------- + */ +void +Parse_File(const char *name, FILE *stream) +{ + char *cp, /* pointer into the line */ + *line; /* the line we're working on */ + + inLine = FALSE; + fatals = 0; + + curFile.fname = UNCONST(name); + curFile.F = stream; + curFile.lineno = 0; + + ParseSetParseFile(curFile.fname); + + do { + while ((line = ParseReadLine()) != NULL) { + if (*line == '.') { + /* + * Lines that begin with the special character are either + * include or undef directives. + */ + for (cp = line + 1; isspace ((unsigned char)*cp); cp++) { + continue; + } + if (strncmp(cp, "include", 7) == 0 || + ((cp[0] == 's' || cp[0] == '-') && + strncmp(&cp[1], "include", 7) == 0)) { + ParseDoInclude(cp); + goto nextLine; + } else if (strncmp(cp, "undef", 5) == 0) { + char *cp2; + for (cp += 5; isspace((unsigned char) *cp); cp++) { + continue; + } + + for (cp2 = cp; !isspace((unsigned char) *cp2) && + (*cp2 != '\0'); cp2++) { + continue; + } + + *cp2 = '\0'; + + Var_Delete(cp, VAR_GLOBAL); + goto nextLine; + } + } + if (*line == '#') { + /* If we're this far, the line must be a comment. */ + goto nextLine; + } + + if (*line == '\t') { + /* + * If a line starts with a tab, it can only hope to be + * a creation command. + */ +#ifndef POSIX + shellCommand: +#endif + for (cp = line + 1; isspace ((unsigned char)*cp); cp++) { + continue; + } + if (*cp) { + if (inLine) { + /* + * So long as it's not a blank line and we're actually + * in a dependency spec, add the command to the list of + * commands of all targets in the dependency spec + */ + Lst_ForEach(targets, ParseAddCmd, cp); +#ifdef CLEANUP + Lst_AtEnd(targCmds, (ClientData) line); +#endif + continue; + } else { + Parse_Error(PARSE_FATAL, + "Unassociated shell command \"%s\"", + cp); + } + } +#ifdef SYSVINCLUDE + } else if (((strncmp(line, "include", 7) == 0 && + isspace((unsigned char) line[7])) || + ((line[0] == 's' || line[0] == '-') && + strncmp(&line[1], "include", 7) == 0 && + isspace((unsigned char) line[8]))) && + strchr(line, ':') == NULL) { + /* + * It's an S3/S5-style "include". + */ + ParseTraditionalInclude(line); + goto nextLine; +#endif + } else if (Parse_IsVar(line)) { + ParseFinishLine(); + Parse_DoVar(line, VAR_GLOBAL); + } else { + /* + * We now know it's a dependency line so it needs to have all + * variables expanded before being parsed. Tell the variable + * module to complain if some variable is undefined... + * To make life easier on novices, if the line is indented we + * first make sure the line has a dependency operator in it. + * If it doesn't have an operator and we're in a dependency + * line's script, we assume it's actually a shell command + * and add it to the current list of targets. + */ +#ifndef POSIX + Boolean nonSpace = FALSE; +#endif + + cp = line; + if (isspace((unsigned char) line[0])) { + while ((*cp != '\0') && isspace((unsigned char) *cp)) { + cp++; + } + if (*cp == '\0') { + goto nextLine; + } +#ifndef POSIX + while (*cp && (ParseIsEscaped(line, cp) || + (*cp != ':') && (*cp != '!'))) { + nonSpace = TRUE; + cp++; + } +#endif + } + +#ifndef POSIX + if (*cp == '\0') { + if (inLine) { + Parse_Error(PARSE_WARNING, + "Shell command needs a leading tab"); + goto shellCommand; + } else if (nonSpace) { + Parse_Error(PARSE_FATAL, "Missing operator"); + } + } else { +#endif + ParseFinishLine(); + + cp = Var_Subst(NULL, line, VAR_CMD, TRUE); + free(line); + line = cp; + + /* + * Need a non-circular list for the target nodes + */ + if (targets) + Lst_Destroy(targets, NOFREE); + + targets = Lst_Init(FALSE); + inLine = TRUE; + + ParseDoDependency(line); +#ifndef POSIX + } +#endif + } + + nextLine: + + free(line); + } + /* + * Reached EOF, but it may be just EOF of an include file... + */ + } while (ParseEOF(1) == CONTINUE); + + /* + * Make sure conditionals are clean + */ + Cond_End(); + + if (fatals) { + (void)fprintf(stderr, + "%s: Fatal errors encountered -- cannot continue\n", + progname); + PrintOnError(NULL); + exit(1); + } +} + +/*- + *--------------------------------------------------------------------- + * Parse_Init -- + * initialize the parsing module + * + * Results: + * none + * + * Side Effects: + * the parseIncPath list is initialized... + *--------------------------------------------------------------------- + */ +void +Parse_Init(void) +{ + mainNode = NILGNODE; + parseIncPath = Lst_Init(FALSE); + sysIncPath = Lst_Init(FALSE); + defIncPath = Lst_Init(FALSE); + includes = Lst_Init(FALSE); +#ifdef CLEANUP + targCmds = Lst_Init(FALSE); +#endif +} + +void +Parse_End(void) +{ +#ifdef CLEANUP + Lst_Destroy(targCmds, (FreeProc *)free); + if (targets) + Lst_Destroy(targets, NOFREE); + Lst_Destroy(defIncPath, Dir_Destroy); + Lst_Destroy(sysIncPath, Dir_Destroy); + Lst_Destroy(parseIncPath, Dir_Destroy); + Lst_Destroy(includes, NOFREE); /* Should be empty now */ +#endif +} + + +/*- + *----------------------------------------------------------------------- + * Parse_MainName -- + * Return a Lst of the main target to create for main()'s sake. If + * no such target exists, we Punt with an obnoxious error message. + * + * Results: + * A Lst of the single node to create. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +Lst +Parse_MainName(void) +{ + Lst mainList; /* result list */ + + mainList = Lst_Init(FALSE); + + if (mainNode == NILGNODE) { + Punt("no target to make."); + /*NOTREACHED*/ + } else if (mainNode->type & OP_DOUBLEDEP) { + (void)Lst_AtEnd(mainList, (ClientData)mainNode); + Lst_Concat(mainList, mainNode->cohorts, LST_CONCNEW); + } + else + (void)Lst_AtEnd(mainList, (ClientData)mainNode); + Var_Append(".TARGETS", mainNode->name, VAR_GLOBAL); + return (mainList); +} + +/*- + *----------------------------------------------------------------------- + * ParseMark -- + * Add the filename and lineno to the GNode so that we remember + * where it was first defined. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static void +ParseMark(GNode *gn) +{ + gn->fname = strdup(curFile.fname); + gn->lineno = curFile.lineno; +} diff --git a/devel/bmake/files/pathnames.h b/devel/bmake/files/pathnames.h new file mode 100644 index 0000000000000..185d9681161c3 --- /dev/null +++ b/devel/bmake/files/pathnames.h @@ -0,0 +1,59 @@ +/* $NetBSD: pathnames.h,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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: @(#)pathnames.h 5.2 (Berkeley) 6/1/90 + * $Id: pathnames.h,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif +#ifndef MAKE_NATIVE +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif +#endif +#ifdef HAVE_PATHS_H +#include +#endif +#define _PATH_OBJDIR "obj" +#define _PATH_OBJDIRPREFIX "/usr/obj" +#ifndef _PATH_DEFSHELLDIR +#define _PATH_DEFSHELLDIR "/bin" +#endif +#define _PATH_DEFSYSMK "sys.mk" +#define _path_defsyspath "/usr/share/mk:/usr/local/share/mk:/opt/share/mk" +#ifndef _PATH_DEFSYSPATH +# ifdef _PATH_PREFIX_SYSPATH +# define _PATH_DEFSYSPATH _PATH_PREFIX_SYSPATH ":" _path_defsyspath +# else +# define _PATH_DEFSYSPATH _path_defsyspath +# endif +#endif diff --git a/devel/bmake/files/ranlib.h b/devel/bmake/files/ranlib.h new file mode 100644 index 0000000000000..715987a22fca9 --- /dev/null +++ b/devel/bmake/files/ranlib.h @@ -0,0 +1,32 @@ +/* @(#)ranlib.h 1.6 88/08/19 SMI; from UCB 4.1 83/05/03 */ +/* $Id: ranlib.h,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $ */ + +/* + * Structure of the __.SYMDEF table of contents for an archive. + * __.SYMDEF begins with a word giving the number of ranlib structures + * which immediately follow, and then continues with a string + * table consisting of a word giving the number of bytes of strings + * which follow and then the strings themselves. + * The ran_strx fields index the string table whose first byte is numbered 0. + */ + +#if !defined(IRIX) && !defined(__digital__) && !defined(__osf__) +#ifndef _ranlib_h +#define _ranlib_h + +#if 0 +#define RANLIBMAG "!\n__.SYMDEF" /* archive file name */ +#endif +#define RANLIBMAG "__.SYMDEF" /* archive file name */ +#define RANLIBSKEW 3 /* creation time offset */ + +struct ranlib { + union { + off_t ran_strx; /* string table index of */ + char *ran_name; /* symbol defined by */ + } ran_un; + off_t ran_off; /* library member at this offset */ +}; + +#endif /*!_ranlib_h*/ +#endif diff --git a/devel/bmake/files/setenv.c b/devel/bmake/files/setenv.c new file mode 100644 index 0000000000000..a0d19527fb5a9 --- /dev/null +++ b/devel/bmake/files/setenv.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 1987 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifndef HAVE_SETENV + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)setenv.c 5.6 (Berkeley) 6/4/91";*/ +static char *rcsid = "$Id: setenv.c,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include + +/* + * __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. + */ +static 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); +} + +/* + * setenv -- + * Set the value of the environmental variable "name" to be + * "value". If rewrite is set, replace any current value. + */ +setenv(name, value, rewrite) + register const char *name; + register const char *value; + int rewrite; +{ + extern char **environ; + static int alloced; /* if allocated space before */ + register char *C; + int l_value, offset; + char *__findenv(); + + if (*value == '=') /* no `=' in value */ + ++value; + l_value = strlen(value); + if ((C = __findenv(name, &offset))) { /* find if already exists */ + if (!rewrite) + return (0); + if (strlen(C) >= l_value) { /* old larger; copy over */ + while (*C++ = *value++); + return (0); + } + } else { /* create new slot */ + register int cnt; + register char **P; + + for (P = environ, cnt = 0; *P; ++P, ++cnt); + if (alloced) { /* just increase size */ + environ = (char **)realloc((char *)environ, + (size_t)(sizeof(char *) * (cnt + 2))); + if (!environ) + return (-1); + } + else { /* get new space */ + alloced = 1; /* copy old entries into it */ + P = (char **)malloc((size_t)(sizeof(char *) * + (cnt + 2))); + if (!P) + return (-1); + bcopy(environ, P, cnt * sizeof(char *)); + environ = P; + } + environ[cnt + 1] = NULL; + offset = cnt; + } + for (C = (char *)name; *C && *C != '='; ++C); /* no `=' in name */ + if (!(environ[offset] = /* name + `=' + value */ + malloc((size_t)((int)(C - name) + l_value + 2)))) + return (-1); + for (C = environ[offset]; (*C = *name++) && *C != '='; ++C) + ; + for (*C++ = '='; *C++ = *value++; ) + ; + return (0); +} + +/* + * unsetenv(name) -- + * Delete environmental variable "name". + */ +void +unsetenv(name) + const char *name; +{ + extern char **environ; + register char **P; + int offset; + char *__findenv(); + + while (__findenv(name, &offset)) /* if set multiple times */ + for (P = &environ[offset];; ++P) + if (!(*P = *(P + 1))) + break; +} +#endif diff --git a/devel/bmake/files/sigcompat.c b/devel/bmake/files/sigcompat.c new file mode 100644 index 0000000000000..b196ac995b657 --- /dev/null +++ b/devel/bmake/files/sigcompat.c @@ -0,0 +1,333 @@ +/* + * NAME: + * sigcompat - BSD compat signals via POSIX + * + * SYNOPSIS: + * void (*signal(int "sig", void (*"handler")(int)))(int); + * int sigsetmask(int "mask"); + * int sigblock(int "mask"); + * int sigpause(int "mask"); + * int sigvec(int "signo", struct sigvec *"sv", struct sigvec *"osv"); + * + * DESCRIPTION: + * These implement the old BSD routines via the POSIX equivalents. + * This module can be used to provide the missing routines, or if + * 'FORCE_POSIX_SIGNALS' is defined, force use of these. + * + * Note that signal() is identical to my Signal() routine except + * for checking for recursion. Within libsig, signal() just + * calls Signal(). + * + * BUGS: + * This package assumes POSIX signal handling is available and + * NOT implemeneted using these routines. To be safe, we check + * for recursion and abort(3) if detected. + * + * Sadly, on some systems, sigset_t is an array, and we cannot + * test for this via #if sizeof(sigset_t) ..., so unless + * 'SIGSET_T_INT' is defined, we have to assume the worst and use + * memcpy(3) to handle args and return values. + * + * HISTORY: + * These routines originate from BSD, and are derrived from the + * NetBSD 1.1 implementation. They have been seriously hacked to + * make them portable to other systems. + * + * AUTHOR: + * Simon J. Gerraty + */ +/* + * @(#)Copyright (c) 1994, Simon J. Gerraty. + * + * This is free software. It comes with NO WARRANTY. + * Permission to use, modify and distribute this source code + * is granted subject to the following conditions. + * 1/ that the above copyright notice and this notice + * are preserved in all copies and that due credit be given + * to the author. + * 2/ that any changes to this code are clearly commented + * as such so that the author does not get blamed for bugs + * other than his own. + * + * Please send copies of changes and bug-fixes to: + * sjg@crufty.net + */ + +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include + +#if defined(sun) && !(defined(__svr4__) || defined(__SVR4)) +# define NO_SIGCOMPAT +#endif + +#if !defined(NO_SIGCOMPAT) && (defined(HAVE_SIGACTION) || defined(SA_NOCLDSTOP)) + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)sigcompat.c 5.3 (Berkeley) 2/24/91";*/ +static char *rcsid = "$Id: sigcompat.c,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#undef signal +#include +#include +#include +#include +#include "assert.h" + +#ifndef ASSERT +# define ASSERT assert +#endif + +#ifdef NDEBUG +# define _DBUG(x) +#else +# define _DBUG(x) x +#endif + +#ifndef SA_RESTART +# define SA_RESTART 2 +#endif +#ifndef SV_INTERRUPT +# define SV_INTERRUPT SA_RESTART +#endif + +#ifndef MASK_T +# ifdef __hpux__ +# define MASK_T long +# else +# define MASK_T int +# endif +#endif +/* I just hate HPsUX */ +#if defined(__HPUX_VERSION) && __HPUX_VERSION > 9 +# define PAUSE_MASK_T int +#else +# define PAUSE_MASK_T MASK_T +#endif + +#ifndef SIG_HDLR +# define SIG_HDLR void +#endif + +#ifdef FORCE_POSIX_SIGNALS +#if !(defined(libsig) || defined(libsjg)) +/* + * This little block is almost identical to Signal(), + * and make this module standalone. + * We don't use it in libsig by default, as some apps might use both + * and expect _SignalFlags to be used by both. + */ + +#ifndef SIGNAL_FLAGS +# define SIGNAL_FLAGS 0 /* no auto-restart */ +#endif +int _signalFlags = SIGNAL_FLAGS; + +SIG_HDLR(*signal(sig, handler)) __P((int)) + int sig; + SIG_HDLR(*handler) __P((int)); +{ + _DBUG(static int depth_signal = 0); + struct sigaction act, oact; + int n; + + _DBUG(++depth_signal); + ASSERT(depth_signal < 2); + act.sa_handler = handler; + sigemptyset(&act.sa_mask); + act.sa_flags = _signalFlags; + n = sigaction(sig, &act, &oact); + _DBUG(--depth_signal); + if (n < 0) + return (SIG_ERR); + return (oact.sa_handler); +} +#else +SIG_HDLR(*signal(sig, handler)) __P((int)) + int sig; + SIG_HDLR(*handler) __P((int)); +{ + extern SIG_HDLR(*Signal __P((int, void (*) __P((int))))) __P((int)); + _DBUG(static int depth_signal = 0); + SIG_HDLR(*old) __P((int)); + + _DBUG(++depth_signal); + ASSERT(depth_signal < 2); + old = Signal(sig, handler); + _DBUG(--depth_signal); + return old; +} +#endif +#endif + +/* + * on some systems, sigset_t is an array... + * it would be nicer if we could do + * #if sizeof(sigset_t) > sizeof(MASK_T) + */ +#ifdef SIGSET_T_INT +# define ss2m(ss) (MASK_T) *(ss) +# define m2ss(ss, m) *ss = (sigset_t) *(m) +#else +static MASK_T +ss2m(ss) + sigset_t *ss; +{ + MASK_T ma[(sizeof(sigset_t) / sizeof(MASK_T)) + 1]; + + memcpy((char *) ma, (char *) ss, sizeof(sigset_t)); + return ma[0]; +} + +static void +m2ss(ss, m) + sigset_t *ss; + MASK_T *m; +{ + if (sizeof(sigset_t) > sizeof(MASK_T)) + memset((char *) ss, 0, sizeof(sigset_t)); + + memcpy((char *) ss, (char *) m, sizeof(MASK_T)); +} +#endif + +#if !defined(HAVE_SIGSETMASK) || defined(FORCE_POSIX_SIGNALS) +MASK_T +sigsetmask(mask) + MASK_T mask; +{ + _DBUG(static int depth_sigsetmask = 0); + sigset_t m, omask; + int n; + + _DBUG(++depth_sigsetmask); + ASSERT(depth_sigsetmask < 2); + m2ss(&m, &mask); + n = sigprocmask(SIG_SETMASK, (sigset_t *) & m, (sigset_t *) & omask); + _DBUG(--depth_sigsetmask); + if (n) + return (n); + + return ss2m(&omask); +} + + +MASK_T +sigblock(mask) + MASK_T mask; +{ + _DBUG(static int depth_sigblock = 0); + sigset_t m, omask; + int n; + + _DBUG(++depth_sigblock); + ASSERT(depth_sigblock < 2); + if (mask) + m2ss(&m, &mask); + n = sigprocmask(SIG_BLOCK, (sigset_t *) ((mask) ? &m : 0), (sigset_t *) & omask); + _DBUG(--depth_sigblock); + if (n) + return (n); + return ss2m(&omask); +} + +#undef sigpause /* Linux at least */ + +PAUSE_MASK_T +sigpause(mask) + PAUSE_MASK_T mask; +{ + _DBUG(static int depth_sigpause = 0); + sigset_t m; + PAUSE_MASK_T n; + + _DBUG(++depth_sigpause); + ASSERT(depth_sigpause < 2); + m2ss(&m, &mask); + n = sigsuspend(&m); + _DBUG(--depth_sigpause); + return n; +} +#endif + +#if defined(HAVE_SIGVEC) && defined(FORCE_POSIX_SIGNALS) +int +sigvec(signo, sv, osv) + int signo; + struct sigvec *sv, *osv; +{ + _DBUG(static int depth_sigvec = 0); + int ret; + struct sigvec nsv; + + _DBUG(++depth_sigvec); + ASSERT(depth_sigvec < 2); + if (sv) { + nsv = *sv; + nsv.sv_flags ^= SV_INTERRUPT; /* !SA_INTERRUPT */ + } + ret = sigaction(signo, sv ? (struct sigaction *) & nsv : NULL, + (struct sigaction *) osv); + _DBUG(--depth_sigvec); + if (ret == 0 && osv) + osv->sv_flags ^= SV_INTERRUPT; /* !SA_INTERRUPT */ + return (ret); +} +#endif + +#ifdef MAIN +# ifndef sigmask +# define sigmask(n) ((unsigned int)1 << (((n) - 1) & (32 - 1))) +# endif + +int +main(argc, argv) + int argc; + char **argv; +{ + MASK_T old = 0; + + printf("expect: old=0,old=2\n"); + fflush(stdout); + signal(SIGQUIT, SIG_IGN); + old = sigblock(sigmask(SIGINT)); + printf("old=%d,", old); + old = sigsetmask(sigmask(SIGALRM)); + printf("old=%d\n", old); +} +#endif +#endif diff --git a/devel/bmake/files/sprite.h b/devel/bmake/files/sprite.h new file mode 100644 index 0000000000000..7f998061ad7f5 --- /dev/null +++ b/devel/bmake/files/sprite.h @@ -0,0 +1,137 @@ +/* $NetBSD: sprite.h,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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: @(#)sprite.h 8.1 (Berkeley) 6/6/93 + */ + +/* + * 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: @(#)sprite.h 8.1 (Berkeley) 6/6/93 + */ + +/* + * sprite.h -- + * + * Common constants and type declarations for Sprite. + */ + +#ifndef _SPRITE +#define _SPRITE + + +/* + * A boolean type is defined as an integer, not an enum. This allows a + * boolean argument to be an expression that isn't strictly 0 or 1 valued. + */ + +typedef int Boolean; +#ifndef TRUE +#define TRUE 1 +#endif /* TRUE */ +#ifndef FALSE +#define FALSE 0 +#endif /* FALSE */ + +/* + * Functions that must return a status can return a ReturnStatus to + * indicate success or type of failure. + */ + +typedef int ReturnStatus; + +/* + * The following statuses overlap with the first 2 generic statuses + * defined in status.h: + * + * SUCCESS There was no error. + * FAILURE There was a general error. + */ + +#define SUCCESS 0x00000000 +#define FAILURE 0x00000001 + + +/* + * A nil pointer must be something that will cause an exception if + * referenced. There are two nils: the kernels nil and the nil used + * by user processes. + */ + +#define NIL ~0 +#ifndef NULL +#define NULL 0 +#endif /* NULL */ + +/* + * ClientData is an uninterpreted word. It is defined as an int so that + * kdbx will not interpret client data as a string. The + * client data will generally not be used in arithmetic. + * But we don't have kdbx anymore so we define it as void (christos) + */ + +typedef void *ClientData; + +#endif /* _SPRITE */ diff --git a/devel/bmake/files/str.c b/devel/bmake/files/str.c new file mode 100644 index 0000000000000..217f5efb48fd4 --- /dev/null +++ b/devel/bmake/files/str.c @@ -0,0 +1,485 @@ +/* $NetBSD: str.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/*- + * Copyright (c) 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: str.c,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)str.c 5.8 (Berkeley) 6/1/90"; +#else +__RCSID("$NetBSD: str.c,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $"); +#endif +#endif /* not lint */ +#endif + +#include "make.h" + +/*- + * str_concat -- + * concatenate the two strings, inserting a space or slash between them, + * freeing them if requested. + * + * returns -- + * the resulting string in allocated space. + */ +char * +str_concat(const char *s1, const char *s2, int flags) +{ + int len1, len2; + char *result; + + /* get the length of both strings */ + len1 = strlen(s1); + len2 = strlen(s2); + + /* allocate length plus separator plus EOS */ + result = emalloc((u_int)(len1 + len2 + 2)); + + /* copy first string into place */ + memcpy(result, s1, len1); + + /* add separator character */ + if (flags & STR_ADDSPACE) { + result[len1] = ' '; + ++len1; + } else if (flags & STR_ADDSLASH) { + result[len1] = '/'; + ++len1; + } + + /* copy second string plus EOS into place */ + memcpy(result + len1, s2, len2 + 1); + + return(result); +} + +/*- + * brk_string -- + * Fracture a string into an array of words (as delineated by tabs or + * spaces) taking quotation marks into account. Leading tabs/spaces + * are ignored. + * + * returns -- + * Pointer to the array of pointers to the words. + */ +char ** +brk_string(const char *str, int *store_argc, Boolean expand, char **buffer) +{ + int argc, ch; + char inquote, *start, *t; + const char *p; + int len; + int argmax = 50, curlen = 0; + char **argv = emalloc((argmax + 1) * sizeof(char *)); + + /* skip leading space chars. */ + for (; *str == ' ' || *str == '\t'; ++str) + continue; + + /* allocate room for a copy of the string */ + if ((len = strlen(str) + 1) > curlen) + *buffer = emalloc(curlen = len); + + /* + * copy the string; at the same time, parse backslashes, + * quotes and build the argument list. + */ + argc = 0; + inquote = '\0'; + for (p = str, start = t = *buffer;; ++p) { + switch(ch = *p) { + case '"': + case '\'': + if (inquote) { + if (inquote == ch) + inquote = '\0'; + else + break; + } + else { + inquote = (char) ch; + /* Don't miss "" or '' */ + if (start == NULL && p[1] == inquote) { + start = t + 1; + break; + } + } + if (!expand) { + if (!start) + start = t; + *t++ = ch; + } + continue; + case ' ': + case '\t': + case '\n': + if (inquote) + break; + if (!start) + continue; + /* FALLTHROUGH */ + case '\0': + /* + * end of a token -- make sure there's enough argv + * space and save off a pointer. + */ + if (!start) + goto done; + + *t++ = '\0'; + if (argc == argmax) { + argmax *= 2; /* ramp up fast */ + argv = (char **)erealloc(argv, + (argmax + 1) * sizeof(char *)); + } + argv[argc++] = start; + start = NULL; + if (ch == '\n' || ch == '\0') + goto done; + continue; + case '\\': + if (!expand) { + if (!start) + start = t; + *t++ = '\\'; + ch = *++p; + break; + } + + switch (ch = *++p) { + case '\0': + case '\n': + /* hmmm; fix it up as best we can */ + ch = '\\'; + --p; + break; + case 'b': + ch = '\b'; + break; + case 'f': + ch = '\f'; + break; + case 'n': + ch = '\n'; + break; + case 'r': + ch = '\r'; + break; + case 't': + ch = '\t'; + break; + } + break; + } + if (!start) + start = t; + *t++ = (char) ch; + } +done: argv[argc] = NULL; + *store_argc = argc; + return(argv); +} + +/* + * Str_FindSubstring -- See if a string contains a particular substring. + * + * Input: + * string String to search. + * substring Substring to find in string. + * + * Results: If string contains substring, the return value is the location of + * the first matching instance of substring in string. If string doesn't + * contain substring, the return value is NULL. Matching is done on an exact + * character-for-character basis with no wildcards or special characters. + * + * Side effects: None. + */ +char * +Str_FindSubstring(const char *string, const char *substring) +{ + const char *a, *b; + + /* + * First scan quickly through the two strings looking for a single- + * character match. When it's found, then compare the rest of the + * substring. + */ + + for (b = substring; *string != 0; string += 1) { + if (*string != *b) + continue; + a = string; + for (;;) { + if (*b == 0) + return UNCONST(string); + if (*a++ != *b++) + break; + } + b = substring; + } + return NULL; +} + +/* + * Str_Match -- + * + * See if a particular string matches a particular pattern. + * + * Results: Non-zero is returned if string matches pattern, 0 otherwise. The + * matching operation permits the following special characters in the + * pattern: *?\[] (see the man page for details on what these mean). + * + * Side effects: None. + */ +int +Str_Match(const char *string, const char *pattern) +{ + char c2; + + for (;;) { + /* + * See if we're at the end of both the pattern and the + * string. If, we succeeded. If we're at the end of the + * pattern but not at the end of the string, we failed. + */ + if (*pattern == 0) + return(!*string); + if (*string == 0 && *pattern != '*') + return(0); + /* + * Check for a "*" as the next pattern character. It matches + * any substring. We handle this by calling ourselves + * recursively for each postfix of string, until either we + * match or we reach the end of the string. + */ + if (*pattern == '*') { + pattern += 1; + if (*pattern == 0) + return(1); + while (*string != 0) { + if (Str_Match(string, pattern)) + return(1); + ++string; + } + return(0); + } + /* + * Check for a "?" as the next pattern character. It matches + * any single character. + */ + if (*pattern == '?') + goto thisCharOK; + /* + * Check for a "[" as the next pattern character. It is + * followed by a list of characters that are acceptable, or + * by a range (two characters separated by "-"). + */ + if (*pattern == '[') { + ++pattern; + for (;;) { + if ((*pattern == ']') || (*pattern == 0)) + return(0); + if (*pattern == *string) + break; + if (pattern[1] == '-') { + c2 = pattern[2]; + if (c2 == 0) + return(0); + if ((*pattern <= *string) && + (c2 >= *string)) + break; + if ((*pattern >= *string) && + (c2 <= *string)) + break; + pattern += 2; + } + ++pattern; + } + while ((*pattern != ']') && (*pattern != 0)) + ++pattern; + goto thisCharOK; + } + /* + * If the next pattern character is '/', just strip off the + * '/' so we do exact matching on the character that follows. + */ + if (*pattern == '\\') { + ++pattern; + if (*pattern == 0) + return(0); + } + /* + * There's no special character. Just make sure that the + * next characters of each string match. + */ + if (*pattern != *string) + return(0); +thisCharOK: ++pattern; + ++string; + } +} + + +/*- + *----------------------------------------------------------------------- + * Str_SYSVMatch -- + * Check word against pattern for a match (% is wild), + * + * Input: + * word Word to examine + * pattern Pattern to examine against + * len Number of characters to substitute + * + * Results: + * Returns the beginning position of a match or null. The number + * of characters matched is returned in len. + * + * Side Effects: + * None + * + *----------------------------------------------------------------------- + */ +char * +Str_SYSVMatch(const char *word, const char *pattern, int *len) +{ + const char *p = pattern; + const char *w = word; + const char *m; + + if (*p == '\0') { + /* Null pattern is the whole string */ + *len = strlen(w); + return UNCONST(w); + } + + if ((m = strchr(p, '%')) != NULL) { + /* check that the prefix matches */ + for (; p != m && *w && *w == *p; w++, p++) + continue; + + if (p != m) + return NULL; /* No match */ + + if (*++p == '\0') { + /* No more pattern, return the rest of the string */ + *len = strlen(w); + return UNCONST(w); + } + } + + m = w; + + /* Find a matching tail */ + do + if (strcmp(p, w) == 0) { + *len = w - m; + return UNCONST(m); + } + while (*w++ != '\0'); + + return NULL; +} + + +/*- + *----------------------------------------------------------------------- + * Str_SYSVSubst -- + * Substitute '%' on the pattern with len characters from src. + * If the pattern does not contain a '%' prepend len characters + * from src. + * + * Results: + * None + * + * Side Effects: + * Places result on buf + * + *----------------------------------------------------------------------- + */ +void +Str_SYSVSubst(Buffer buf, char *pat, char *src, int len) +{ + char *m; + + if ((m = strchr(pat, '%')) != NULL) { + /* Copy the prefix */ + Buf_AddBytes(buf, m - pat, (Byte *)pat); + /* skip the % */ + pat = m + 1; + } + + /* Copy the pattern */ + Buf_AddBytes(buf, len, (Byte *)src); + + /* append the rest */ + Buf_AddBytes(buf, strlen(pat), (Byte *)pat); +} diff --git a/devel/bmake/files/suff.c b/devel/bmake/files/suff.c new file mode 100644 index 0000000000000..79dd0b005e204 --- /dev/null +++ b/devel/bmake/files/suff.c @@ -0,0 +1,2626 @@ +/* $NetBSD: suff.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: suff.c,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)suff.c 8.4 (Berkeley) 3/21/94"; +#else +__RCSID("$NetBSD: suff.c,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * suff.c -- + * Functions to maintain suffix lists and find implicit dependents + * using suffix transformation rules + * + * Interface: + * Suff_Init Initialize all things to do with suffixes. + * + * Suff_End Cleanup the module + * + * Suff_DoPaths This function is used to make life easier + * when searching for a file according to its + * suffix. It takes the global search path, + * as defined using the .PATH: target, and appends + * its directories to the path of each of the + * defined suffixes, as specified using + * .PATH: targets. In addition, all + * directories given for suffixes labeled as + * include files or libraries, using the .INCLUDES + * or .LIBS targets, are played with using + * Dir_MakeFlags to create the .INCLUDES and + * .LIBS global variables. + * + * Suff_ClearSuffixes Clear out all the suffixes and defined + * transformations. + * + * Suff_IsTransform Return TRUE if the passed string is the lhs + * of a transformation rule. + * + * Suff_AddSuffix Add the passed string as another known suffix. + * + * Suff_GetPath Return the search path for the given suffix. + * + * Suff_AddInclude Mark the given suffix as denoting an include + * file. + * + * Suff_AddLib Mark the given suffix as denoting a library. + * + * Suff_AddTransform Add another transformation to the suffix + * graph. Returns GNode suitable for framing, I + * mean, tacking commands, attributes, etc. on. + * + * Suff_SetNull Define the suffix to consider the suffix of + * any file that doesn't have a known one. + * + * Suff_FindDeps Find implicit sources for and the location of + * a target based on its suffix. Returns the + * bottom-most node added to the graph or NILGNODE + * if the target had no implicit sources. + * + * Suff_FindPath Return the appropriate path to search in + * order to find the node. + */ + +#include +#include "make.h" +#include "hash.h" +#include "dir.h" + +static Lst sufflist; /* Lst of suffixes */ +#ifdef CLEANUP +static Lst suffClean; /* Lst of suffixes to be cleaned */ +#endif +static Lst srclist; /* Lst of sources */ +static Lst transforms; /* Lst of transformation rules */ + +static int sNum = 0; /* Counter for assigning suffix numbers */ + +/* + * Structure describing an individual suffix. + */ +typedef struct _Suff { + char *name; /* The suffix itself */ + int nameLen; /* Length of the suffix */ + short flags; /* Type of suffix */ +#define SUFF_INCLUDE 0x01 /* One which is #include'd */ +#define SUFF_LIBRARY 0x02 /* One which contains a library */ +#define SUFF_NULL 0x04 /* The empty suffix */ + Lst searchPath; /* The path along which files of this suffix + * may be found */ + int sNum; /* The suffix number */ + int refCount; /* Reference count of list membership */ + Lst parents; /* Suffixes we have a transformation to */ + Lst children; /* Suffixes we have a transformation from */ + Lst ref; /* List of lists this suffix is referenced */ +} Suff; + +/* + * for SuffSuffIsSuffix + */ +typedef struct { + char *ename; /* The end of the name */ + int len; /* Length of the name */ +} SuffixCmpData; + +/* + * Structure used in the search for implied sources. + */ +typedef struct _Src { + char *file; /* The file to look for */ + char *pref; /* Prefix from which file was formed */ + Suff *suff; /* The suffix on the file */ + struct _Src *parent; /* The Src for which this is a source */ + GNode *node; /* The node describing the file */ + int children; /* Count of existing children (so we don't free + * this thing too early or never nuke it) */ +#ifdef DEBUG_SRC + Lst cp; /* Debug; children list */ +#endif +} Src; + +/* + * A structure for passing more than one argument to the Lst-library-invoked + * function... + */ +typedef struct { + Lst l; + Src *s; +} LstSrc; + +typedef struct { + GNode **gn; + Suff *s; + Boolean r; +} GNodeSuff; + +static Suff *suffNull; /* The NULL suffix for this run */ +static Suff *emptySuff; /* The empty suffix required for POSIX + * single-suffix transformation rules */ + + +static char *SuffStrIsPrefix(char *, char *); +static char *SuffSuffIsSuffix(Suff *, SuffixCmpData *); +static int SuffSuffIsSuffixP(ClientData, ClientData); +static int SuffSuffHasNameP(ClientData, ClientData); +static int SuffSuffIsPrefix(ClientData, ClientData); +static int SuffGNHasNameP(ClientData, ClientData); +static void SuffUnRef(ClientData, ClientData); +static void SuffFree(ClientData); +static void SuffInsert(Lst, Suff *); +static void SuffRemove(Lst, Suff *); +static Boolean SuffParseTransform(char *, Suff **, Suff **); +static int SuffRebuildGraph(ClientData, ClientData); +static int SuffScanTargets(ClientData, ClientData); +static int SuffAddSrc(ClientData, ClientData); +static int SuffRemoveSrc(Lst); +static void SuffAddLevel(Lst, Src *); +static Src *SuffFindThem(Lst, Lst); +static Src *SuffFindCmds(Src *, Lst); +static int SuffExpandChildren(LstNode, GNode *); +static Boolean SuffApplyTransform(GNode *, GNode *, Suff *, Suff *); +static void SuffFindDeps(GNode *, Lst); +static void SuffFindArchiveDeps(GNode *, Lst); +static void SuffFindNormalDeps(GNode *, Lst); +static int SuffPrintName(ClientData, ClientData); +static int SuffPrintSuff(ClientData, ClientData); +static int SuffPrintTrans(ClientData, ClientData); + + /*************** Lst Predicates ****************/ +/*- + *----------------------------------------------------------------------- + * SuffStrIsPrefix -- + * See if pref is a prefix of str. + * + * Input: + * pref possible prefix + * str string to check + * + * Results: + * NULL if it ain't, pointer to character in str after prefix if so + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +static char * +SuffStrIsPrefix(char *pref, char *str) +{ + while (*str && *pref == *str) { + pref++; + str++; + } + + return (*pref ? NULL : str); +} + +/*- + *----------------------------------------------------------------------- + * SuffSuffIsSuffix -- + * See if suff is a suffix of str. sd->ename should point to THE END + * of the string to check. (THE END == the null byte) + * + * Input: + * s possible suffix + * sd string to examine + * + * Results: + * NULL if it ain't, pointer to character in str before suffix if + * it is. + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +static char * +SuffSuffIsSuffix(Suff *s, SuffixCmpData *sd) +{ + char *p1; /* Pointer into suffix name */ + char *p2; /* Pointer into string being examined */ + + if (sd->len < s->nameLen) + return NULL; /* this string is shorter than the suffix */ + + p1 = s->name + s->nameLen; + p2 = sd->ename; + + while (p1 >= s->name && *p1 == *p2) { + p1--; + p2--; + } + + return (p1 == s->name - 1 ? p2 : NULL); +} + +/*- + *----------------------------------------------------------------------- + * SuffSuffIsSuffixP -- + * Predicate form of SuffSuffIsSuffix. Passed as the callback function + * to Lst_Find. + * + * Results: + * 0 if the suffix is the one desired, non-zero if not. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static int +SuffSuffIsSuffixP(ClientData s, ClientData sd) +{ + return(!SuffSuffIsSuffix((Suff *)s, (SuffixCmpData *)sd)); +} + +/*- + *----------------------------------------------------------------------- + * SuffSuffHasNameP -- + * Callback procedure for finding a suffix based on its name. Used by + * Suff_GetPath. + * + * Input: + * s Suffix to check + * sd Desired name + * + * Results: + * 0 if the suffix is of the given name. non-zero otherwise. + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +static int +SuffSuffHasNameP(ClientData s, ClientData sname) +{ + return (strcmp((char *)sname, ((Suff *)s)->name)); +} + +/*- + *----------------------------------------------------------------------- + * SuffSuffIsPrefix -- + * See if the suffix described by s is a prefix of the string. Care + * must be taken when using this to search for transformations and + * what-not, since there could well be two suffixes, one of which + * is a prefix of the other... + * + * Input: + * s suffix to compare + * str string to examine + * + * Results: + * 0 if s is a prefix of str. non-zero otherwise + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +static int +SuffSuffIsPrefix(ClientData s, ClientData str) +{ + return (SuffStrIsPrefix(((Suff *)s)->name, (char *)str) == NULL ? 1 : 0); +} + +/*- + *----------------------------------------------------------------------- + * SuffGNHasNameP -- + * See if the graph node has the desired name + * + * Input: + * gn current node we're looking at + * name name we're looking for + * + * Results: + * 0 if it does. non-zero if it doesn't + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +static int +SuffGNHasNameP(ClientData gn, ClientData name) +{ + return (strcmp((char *)name, ((GNode *)gn)->name)); +} + + /*********** Maintenance Functions ************/ + +static void +SuffUnRef(ClientData lp, ClientData sp) +{ + Lst l = (Lst) lp; + + LstNode ln = Lst_Member(l, sp); + if (ln != NILLNODE) { + Lst_Remove(l, ln); + ((Suff *)sp)->refCount--; + } +} + +/*- + *----------------------------------------------------------------------- + * SuffFree -- + * Free up all memory associated with the given suffix structure. + * + * Results: + * none + * + * Side Effects: + * the suffix entry is detroyed + *----------------------------------------------------------------------- + */ +static void +SuffFree(ClientData sp) +{ + Suff *s = (Suff *)sp; + + if (s == suffNull) + suffNull = NULL; + + if (s == emptySuff) + emptySuff = NULL; + +#ifdef notdef + /* We don't delete suffixes in order, so we cannot use this */ + if (s->refCount) + Punt("Internal error deleting suffix `%s' with refcount = %d", s->name, + s->refCount); +#endif + + Lst_Destroy(s->ref, NOFREE); + Lst_Destroy(s->children, NOFREE); + Lst_Destroy(s->parents, NOFREE); + Lst_Destroy(s->searchPath, Dir_Destroy); + + free(s->name); + free(s); +} + +/*- + *----------------------------------------------------------------------- + * SuffRemove -- + * Remove the suffix into the list + * + * Results: + * None + * + * Side Effects: + * The reference count for the suffix is decremented and the + * suffix is possibly freed + *----------------------------------------------------------------------- + */ +static void +SuffRemove(Lst l, Suff *s) +{ + SuffUnRef((ClientData) l, (ClientData) s); + if (s->refCount == 0) { + SuffUnRef((ClientData) sufflist, (ClientData) s); + SuffFree((ClientData) s); + } +} + +/*- + *----------------------------------------------------------------------- + * SuffInsert -- + * Insert the suffix into the list keeping the list ordered by suffix + * numbers. + * + * Input: + * l the list where in s should be inserted + * s the suffix to insert + * + * Results: + * None + * + * Side Effects: + * The reference count of the suffix is incremented + *----------------------------------------------------------------------- + */ +static void +SuffInsert(Lst l, Suff *s) +{ + LstNode ln; /* current element in l we're examining */ + Suff *s2 = NULL; /* the suffix descriptor in this element */ + + if (Lst_Open(l) == FAILURE) { + return; + } + while ((ln = Lst_Next(l)) != NILLNODE) { + s2 = (Suff *)Lst_Datum(ln); + if (s2->sNum >= s->sNum) { + break; + } + } + + Lst_Close(l); + if (DEBUG(SUFF)) { + printf("inserting %s(%d)...", s->name, s->sNum); + } + if (ln == NILLNODE) { + if (DEBUG(SUFF)) { + printf("at end of list\n"); + } + (void)Lst_AtEnd(l, (ClientData)s); + s->refCount++; + (void)Lst_AtEnd(s->ref, (ClientData) l); + } else if (s2->sNum != s->sNum) { + if (DEBUG(SUFF)) { + printf("before %s(%d)\n", s2->name, s2->sNum); + } + (void)Lst_Insert(l, ln, (ClientData)s); + s->refCount++; + (void)Lst_AtEnd(s->ref, (ClientData) l); + } else if (DEBUG(SUFF)) { + printf("already there\n"); + } +} + +/*- + *----------------------------------------------------------------------- + * Suff_ClearSuffixes -- + * This is gross. Nuke the list of suffixes but keep all transformation + * rules around. The transformation graph is destroyed in this process, + * but we leave the list of rules so when a new graph is formed the rules + * will remain. + * This function is called from the parse module when a + * .SUFFIXES:\n line is encountered. + * + * Results: + * none + * + * Side Effects: + * the sufflist and its graph nodes are destroyed + *----------------------------------------------------------------------- + */ +void +Suff_ClearSuffixes(void) +{ +#ifdef CLEANUP + Lst_Concat(suffClean, sufflist, LST_CONCLINK); +#endif + sufflist = Lst_Init(FALSE); + sNum = 0; + suffNull = emptySuff; +} + +/*- + *----------------------------------------------------------------------- + * SuffParseTransform -- + * Parse a transformation string to find its two component suffixes. + * + * Input: + * str String being parsed + * srcPtr Place to store source of trans. + * targPtr Place to store target of trans. + * + * Results: + * TRUE if the string is a valid transformation and FALSE otherwise. + * + * Side Effects: + * The passed pointers are overwritten. + * + *----------------------------------------------------------------------- + */ +static Boolean +SuffParseTransform(char *str, Suff **srcPtr, Suff **targPtr) +{ + LstNode srcLn; /* element in suffix list of trans source*/ + Suff *src; /* Source of transformation */ + LstNode targLn; /* element in suffix list of trans target*/ + char *str2; /* Extra pointer (maybe target suffix) */ + LstNode singleLn; /* element in suffix list of any suffix + * that exactly matches str */ + Suff *single = NULL;/* Source of possible transformation to + * null suffix */ + + srcLn = NILLNODE; + singleLn = NILLNODE; + + /* + * Loop looking first for a suffix that matches the start of the + * string and then for one that exactly matches the rest of it. If + * we can find two that meet these criteria, we've successfully + * parsed the string. + */ + for (;;) { + if (srcLn == NILLNODE) { + srcLn = Lst_Find(sufflist, (ClientData)str, SuffSuffIsPrefix); + } else { + srcLn = Lst_FindFrom(sufflist, Lst_Succ(srcLn), (ClientData)str, + SuffSuffIsPrefix); + } + if (srcLn == NILLNODE) { + /* + * Ran out of source suffixes -- no such rule + */ + if (singleLn != NILLNODE) { + /* + * Not so fast Mr. Smith! There was a suffix that encompassed + * the entire string, so we assume it was a transformation + * to the null suffix (thank you POSIX). We still prefer to + * find a double rule over a singleton, hence we leave this + * check until the end. + * + * XXX: Use emptySuff over suffNull? + */ + *srcPtr = single; + *targPtr = suffNull; + return(TRUE); + } + return (FALSE); + } + src = (Suff *)Lst_Datum(srcLn); + str2 = str + src->nameLen; + if (*str2 == '\0') { + single = src; + singleLn = srcLn; + } else { + targLn = Lst_Find(sufflist, (ClientData)str2, SuffSuffHasNameP); + if (targLn != NILLNODE) { + *srcPtr = src; + *targPtr = (Suff *)Lst_Datum(targLn); + return (TRUE); + } + } + } +} + +/*- + *----------------------------------------------------------------------- + * Suff_IsTransform -- + * Return TRUE if the given string is a transformation rule + * + * + * Input: + * str string to check + * + * Results: + * TRUE if the string is a concatenation of two known suffixes. + * FALSE otherwise + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +Boolean +Suff_IsTransform(char *str) +{ + Suff *src, *targ; + + return (SuffParseTransform(str, &src, &targ)); +} + +/*- + *----------------------------------------------------------------------- + * Suff_AddTransform -- + * Add the transformation rule described by the line to the + * list of rules and place the transformation itself in the graph + * + * Input: + * line name of transformation to add + * + * Results: + * The node created for the transformation in the transforms list + * + * Side Effects: + * The node is placed on the end of the transforms Lst and links are + * made between the two suffixes mentioned in the target name + *----------------------------------------------------------------------- + */ +GNode * +Suff_AddTransform(char *line) +{ + GNode *gn; /* GNode of transformation rule */ + Suff *s, /* source suffix */ + *t; /* target suffix */ + LstNode ln; /* Node for existing transformation */ + + ln = Lst_Find(transforms, (ClientData)line, SuffGNHasNameP); + if (ln == NILLNODE) { + /* + * Make a new graph node for the transformation. It will be filled in + * by the Parse module. + */ + gn = Targ_NewGN(line); + (void)Lst_AtEnd(transforms, (ClientData)gn); + } else { + /* + * New specification for transformation rule. Just nuke the old list + * of commands so they can be filled in again... We don't actually + * free the commands themselves, because a given command can be + * attached to several different transformations. + */ + gn = (GNode *)Lst_Datum(ln); + Lst_Destroy(gn->commands, NOFREE); + Lst_Destroy(gn->children, NOFREE); + gn->commands = Lst_Init(FALSE); + gn->children = Lst_Init(FALSE); + } + + gn->type = OP_TRANSFORM; + + (void)SuffParseTransform(line, &s, &t); + + /* + * link the two together in the proper relationship and order + */ + if (DEBUG(SUFF)) { + printf("defining transformation from `%s' to `%s'\n", + s->name, t->name); + } + SuffInsert(t->children, s); + SuffInsert(s->parents, t); + + return (gn); +} + +/*- + *----------------------------------------------------------------------- + * Suff_EndTransform -- + * Handle the finish of a transformation definition, removing the + * transformation from the graph if it has neither commands nor + * sources. This is a callback procedure for the Parse module via + * Lst_ForEach + * + * Input: + * gnp Node for transformation + * dummy Node for transformation + * + * Results: + * === 0 + * + * Side Effects: + * If the node has no commands or children, the children and parents + * lists of the affected suffixes are altered. + * + *----------------------------------------------------------------------- + */ +int +Suff_EndTransform(ClientData gnp, ClientData dummy) +{ + GNode *gn = (GNode *)gnp; + + if ((gn->type & OP_DOUBLEDEP) && !Lst_IsEmpty (gn->cohorts)) + gn = (GNode *)Lst_Datum(Lst_Last(gn->cohorts)); + if ((gn->type & OP_TRANSFORM) && Lst_IsEmpty(gn->commands) && + Lst_IsEmpty(gn->children)) + { + Suff *s, *t; + + /* + * SuffParseTransform() may fail for special rules which are not + * actual transformation rules. (e.g. .DEFAULT) + */ + if (SuffParseTransform(gn->name, &s, &t)) { + Lst p; + + if (DEBUG(SUFF)) { + printf("deleting transformation from `%s' to `%s'\n", + s->name, t->name); + } + + /* + * Store s->parents because s could be deleted in SuffRemove + */ + p = s->parents; + + /* + * Remove the source from the target's children list. We check for a + * nil return to handle a beanhead saying something like + * .c.o .c.o: + * + * We'll be called twice when the next target is seen, but .c and .o + * are only linked once... + */ + SuffRemove(t->children, s); + + /* + * Remove the target from the source's parents list + */ + SuffRemove(p, t); + } + } else if ((gn->type & OP_TRANSFORM) && DEBUG(SUFF)) { + printf("transformation %s complete\n", gn->name); + } + + return(dummy ? 0 : 0); +} + +/*- + *----------------------------------------------------------------------- + * SuffRebuildGraph -- + * Called from Suff_AddSuffix via Lst_ForEach to search through the + * list of existing transformation rules and rebuild the transformation + * graph when it has been destroyed by Suff_ClearSuffixes. If the + * given rule is a transformation involving this suffix and another, + * existing suffix, the proper relationship is established between + * the two. + * + * Input: + * transformp Transformation to test + * sp Suffix to rebuild + * + * Results: + * Always 0. + * + * Side Effects: + * The appropriate links will be made between this suffix and + * others if transformation rules exist for it. + * + *----------------------------------------------------------------------- + */ +static int +SuffRebuildGraph(ClientData transformp, ClientData sp) +{ + GNode *transform = (GNode *)transformp; + Suff *s = (Suff *)sp; + char *cp; + LstNode ln; + Suff *s2; + SuffixCmpData sd; + + /* + * First see if it is a transformation from this suffix. + */ + cp = SuffStrIsPrefix(s->name, transform->name); + if (cp != NULL) { + ln = Lst_Find(sufflist, (ClientData)cp, SuffSuffHasNameP); + if (ln != NILLNODE) { + /* + * Found target. Link in and return, since it can't be anything + * else. + */ + s2 = (Suff *)Lst_Datum(ln); + SuffInsert(s2->children, s); + SuffInsert(s->parents, s2); + return(0); + } + } + + /* + * Not from, maybe to? + */ + sd.len = strlen(transform->name); + sd.ename = transform->name + sd.len; + cp = SuffSuffIsSuffix(s, &sd); + if (cp != NULL) { + /* + * Null-terminate the source suffix in order to find it. + */ + cp[1] = '\0'; + ln = Lst_Find(sufflist, (ClientData)transform->name, SuffSuffHasNameP); + /* + * Replace the start of the target suffix + */ + cp[1] = s->name[0]; + if (ln != NILLNODE) { + /* + * Found it -- establish the proper relationship + */ + s2 = (Suff *)Lst_Datum(ln); + SuffInsert(s->children, s2); + SuffInsert(s2->parents, s); + } + } + return(0); +} + +/*- + *----------------------------------------------------------------------- + * SuffScanTargets -- + * Called from Suff_AddSuffix via Lst_ForEach to search through the + * list of existing targets and find if any of the existing targets + * can be turned into a transformation rule. + * + * Results: + * 1 if a new main target has been selected, 0 otherwise. + * + * Side Effects: + * If such a target is found and the target is the current main + * target, the main target is set to NULL and the next target + * examined (if that exists) becomes the main target. + * + *----------------------------------------------------------------------- + */ +static int +SuffScanTargets(ClientData targetp, ClientData gsp) +{ + GNode *target = (GNode *)targetp; + GNodeSuff *gs = (GNodeSuff *)gsp; + Suff *s, *t; + char *ptr; + + if (*gs->gn == NILGNODE && gs->r && (target->type & OP_NOTARGET) == 0) { + *gs->gn = target; + Targ_SetMain(target); + return 1; + } + + if (target->type == OP_TRANSFORM) + return 0; + + if ((ptr = strstr(target->name, gs->s->name)) == NULL || + ptr == target->name) + return 0; + + if (SuffParseTransform(target->name, &s, &t)) { + if (*gs->gn == target) { + gs->r = TRUE; + *gs->gn = NILGNODE; + Targ_SetMain(NILGNODE); + } + Lst_Destroy(target->children, NOFREE); + target->children = Lst_Init(FALSE); + target->type = OP_TRANSFORM; + /* + * link the two together in the proper relationship and order + */ + if (DEBUG(SUFF)) { + printf("defining transformation from `%s' to `%s'\n", + s->name, t->name); + } + SuffInsert(t->children, s); + SuffInsert(s->parents, t); + } + return 0; +} + +/*- + *----------------------------------------------------------------------- + * Suff_AddSuffix -- + * Add the suffix in string to the end of the list of known suffixes. + * Should we restructure the suffix graph? Make doesn't... + * + * Input: + * str the name of the suffix to add + * + * Results: + * None + * + * Side Effects: + * A GNode is created for the suffix and a Suff structure is created and + * added to the suffixes list unless the suffix was already known. + * The mainNode passed can be modified if a target mutated into a + * transform and that target happened to be the main target. + *----------------------------------------------------------------------- + */ +void +Suff_AddSuffix(char *str, GNode **gn) +{ + Suff *s; /* new suffix descriptor */ + LstNode ln; + GNodeSuff gs; + + ln = Lst_Find(sufflist, (ClientData)str, SuffSuffHasNameP); + if (ln == NILLNODE) { + s = emalloc(sizeof(Suff)); + + s->name = estrdup(str); + s->nameLen = strlen(s->name); + s->searchPath = Lst_Init(FALSE); + s->children = Lst_Init(FALSE); + s->parents = Lst_Init(FALSE); + s->ref = Lst_Init(FALSE); + s->sNum = sNum++; + s->flags = 0; + s->refCount = 1; + + (void)Lst_AtEnd(sufflist, (ClientData)s); + /* + * We also look at our existing targets list to see if adding + * this suffix will make one of our current targets mutate into + * a suffix rule. This is ugly, but other makes treat all targets + * that start with a . as suffix rules. + */ + gs.gn = gn; + gs.s = s; + gs.r = FALSE; + Lst_ForEach(Targ_List(), SuffScanTargets, (ClientData) &gs); + /* + * Look for any existing transformations from or to this suffix. + * XXX: Only do this after a Suff_ClearSuffixes? + */ + Lst_ForEach(transforms, SuffRebuildGraph, (ClientData) s); + } +} + +/*- + *----------------------------------------------------------------------- + * Suff_GetPath -- + * Return the search path for the given suffix, if it's defined. + * + * Results: + * The searchPath for the desired suffix or NILLST if the suffix isn't + * defined. + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +Lst +Suff_GetPath(char *sname) +{ + LstNode ln; + Suff *s; + + ln = Lst_Find(sufflist, (ClientData)sname, SuffSuffHasNameP); + if (ln == NILLNODE) { + return (NILLST); + } else { + s = (Suff *)Lst_Datum(ln); + return (s->searchPath); + } +} + +/*- + *----------------------------------------------------------------------- + * Suff_DoPaths -- + * Extend the search paths for all suffixes to include the default + * search path. + * + * Results: + * None. + * + * Side Effects: + * The searchPath field of all the suffixes is extended by the + * directories in dirSearchPath. If paths were specified for the + * ".h" suffix, the directories are stuffed into a global variable + * called ".INCLUDES" with each directory preceded by a -I. The same + * is done for the ".a" suffix, except the variable is called + * ".LIBS" and the flag is -L. + *----------------------------------------------------------------------- + */ +void +Suff_DoPaths(void) +{ + Suff *s; + LstNode ln; + char *ptr; + Lst inIncludes; /* Cumulative .INCLUDES path */ + Lst inLibs; /* Cumulative .LIBS path */ + + if (Lst_Open(sufflist) == FAILURE) { + return; + } + + inIncludes = Lst_Init(FALSE); + inLibs = Lst_Init(FALSE); + + while ((ln = Lst_Next(sufflist)) != NILLNODE) { + s = (Suff *)Lst_Datum(ln); + if (!Lst_IsEmpty (s->searchPath)) { +#ifdef INCLUDES + if (s->flags & SUFF_INCLUDE) { + Dir_Concat(inIncludes, s->searchPath); + } +#endif /* INCLUDES */ +#ifdef LIBRARIES + if (s->flags & SUFF_LIBRARY) { + Dir_Concat(inLibs, s->searchPath); + } +#endif /* LIBRARIES */ + Dir_Concat(s->searchPath, dirSearchPath); + } else { + Lst_Destroy(s->searchPath, Dir_Destroy); + s->searchPath = Lst_Duplicate(dirSearchPath, Dir_CopyDir); + } + } + + Var_Set(".INCLUDES", ptr = Dir_MakeFlags("-I", inIncludes), VAR_GLOBAL, 0); + free(ptr); + Var_Set(".LIBS", ptr = Dir_MakeFlags("-L", inLibs), VAR_GLOBAL, 0); + free(ptr); + + Lst_Destroy(inIncludes, Dir_Destroy); + Lst_Destroy(inLibs, Dir_Destroy); + + Lst_Close(sufflist); +} + +/*- + *----------------------------------------------------------------------- + * Suff_AddInclude -- + * Add the given suffix as a type of file which gets included. + * Called from the parse module when a .INCLUDES line is parsed. + * The suffix must have already been defined. + * + * Input: + * sname Name of the suffix to mark + * + * Results: + * None. + * + * Side Effects: + * The SUFF_INCLUDE bit is set in the suffix's flags field + * + *----------------------------------------------------------------------- + */ +void +Suff_AddInclude(char *sname) +{ + LstNode ln; + Suff *s; + + ln = Lst_Find(sufflist, (ClientData)sname, SuffSuffHasNameP); + if (ln != NILLNODE) { + s = (Suff *)Lst_Datum(ln); + s->flags |= SUFF_INCLUDE; + } +} + +/*- + *----------------------------------------------------------------------- + * Suff_AddLib -- + * Add the given suffix as a type of file which is a library. + * Called from the parse module when parsing a .LIBS line. The + * suffix must have been defined via .SUFFIXES before this is + * called. + * + * Input: + * sname Name of the suffix to mark + * + * Results: + * None. + * + * Side Effects: + * The SUFF_LIBRARY bit is set in the suffix's flags field + * + *----------------------------------------------------------------------- + */ +void +Suff_AddLib(char *sname) +{ + LstNode ln; + Suff *s; + + ln = Lst_Find(sufflist, (ClientData)sname, SuffSuffHasNameP); + if (ln != NILLNODE) { + s = (Suff *)Lst_Datum(ln); + s->flags |= SUFF_LIBRARY; + } +} + + /********** Implicit Source Search Functions *********/ + +/*- + *----------------------------------------------------------------------- + * SuffAddSrc -- + * Add a suffix as a Src structure to the given list with its parent + * being the given Src structure. If the suffix is the null suffix, + * the prefix is used unaltered as the file name in the Src structure. + * + * Input: + * sp suffix for which to create a Src structure + * lsp list and parent for the new Src + * + * Results: + * always returns 0 + * + * Side Effects: + * A Src structure is created and tacked onto the end of the list + *----------------------------------------------------------------------- + */ +static int +SuffAddSrc(ClientData sp, ClientData lsp) +{ + Suff *s = (Suff *)sp; + LstSrc *ls = (LstSrc *)lsp; + Src *s2; /* new Src structure */ + Src *targ; /* Target structure */ + + targ = ls->s; + + if ((s->flags & SUFF_NULL) && (*s->name != '\0')) { + /* + * If the suffix has been marked as the NULL suffix, also create a Src + * structure for a file with no suffix attached. Two birds, and all + * that... + */ + s2 = emalloc(sizeof(Src)); + s2->file = estrdup(targ->pref); + s2->pref = targ->pref; + s2->parent = targ; + s2->node = NILGNODE; + s2->suff = s; + s->refCount++; + s2->children = 0; + targ->children += 1; + (void)Lst_AtEnd(ls->l, (ClientData)s2); +#ifdef DEBUG_SRC + s2->cp = Lst_Init(FALSE); + Lst_AtEnd(targ->cp, (ClientData) s2); + printf("1 add %x %x to %x:", targ, s2, ls->l); + Lst_ForEach(ls->l, PrintAddr, (ClientData) 0); + printf("\n"); +#endif + } + s2 = emalloc(sizeof(Src)); + s2->file = str_concat(targ->pref, s->name, 0); + s2->pref = targ->pref; + s2->parent = targ; + s2->node = NILGNODE; + s2->suff = s; + s->refCount++; + s2->children = 0; + targ->children += 1; + (void)Lst_AtEnd(ls->l, (ClientData)s2); +#ifdef DEBUG_SRC + s2->cp = Lst_Init(FALSE); + Lst_AtEnd(targ->cp, (ClientData) s2); + printf("2 add %x %x to %x:", targ, s2, ls->l); + Lst_ForEach(ls->l, PrintAddr, (ClientData) 0); + printf("\n"); +#endif + + return(0); +} + +/*- + *----------------------------------------------------------------------- + * SuffAddLevel -- + * Add all the children of targ as Src structures to the given list + * + * Input: + * l list to which to add the new level + * targ Src structure to use as the parent + * + * Results: + * None + * + * Side Effects: + * Lots of structures are created and added to the list + *----------------------------------------------------------------------- + */ +static void +SuffAddLevel(Lst l, Src *targ) +{ + LstSrc ls; + + ls.s = targ; + ls.l = l; + + Lst_ForEach(targ->suff->children, SuffAddSrc, (ClientData)&ls); +} + +/*- + *---------------------------------------------------------------------- + * SuffRemoveSrc -- + * Free all src structures in list that don't have a reference count + * + * Results: + * Ture if an src was removed + * + * Side Effects: + * The memory is free'd. + *---------------------------------------------------------------------- + */ +static int +SuffRemoveSrc(Lst l) +{ + LstNode ln; + Src *s; + int t = 0; + + if (Lst_Open(l) == FAILURE) { + return 0; + } +#ifdef DEBUG_SRC + printf("cleaning %lx: ", (unsigned long) l); + Lst_ForEach(l, PrintAddr, (ClientData) 0); + printf("\n"); +#endif + + + while ((ln = Lst_Next(l)) != NILLNODE) { + s = (Src *)Lst_Datum(ln); + if (s->children == 0) { + free(s->file); + if (!s->parent) + free(s->pref); + else { +#ifdef DEBUG_SRC + LstNode ln = Lst_Member(s->parent->cp, (ClientData)s); + if (ln != NILLNODE) + Lst_Remove(s->parent->cp, ln); +#endif + --s->parent->children; + } +#ifdef DEBUG_SRC + printf("free: [l=%x] p=%x %d\n", l, s, s->children); + Lst_Destroy(s->cp, NOFREE); +#endif + Lst_Remove(l, ln); + free(s); + t |= 1; + Lst_Close(l); + return TRUE; + } +#ifdef DEBUG_SRC + else { + printf("keep: [l=%x] p=%x %d: ", l, s, s->children); + Lst_ForEach(s->cp, PrintAddr, (ClientData) 0); + printf("\n"); + } +#endif + } + + Lst_Close(l); + + return t; +} + +/*- + *----------------------------------------------------------------------- + * SuffFindThem -- + * Find the first existing file/target in the list srcs + * + * Input: + * srcs list of Src structures to search through + * + * Results: + * The lowest structure in the chain of transformations + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +static Src * +SuffFindThem(Lst srcs, Lst slst) +{ + Src *s; /* current Src */ + Src *rs; /* returned Src */ + char *ptr; + + rs = NULL; + + while (!Lst_IsEmpty (srcs)) { + s = (Src *)Lst_DeQueue(srcs); + + if (DEBUG(SUFF)) { + printf("\ttrying %s...", s->file); + } + + /* + * A file is considered to exist if either a node exists in the + * graph for it or the file actually exists. + */ + if (Targ_FindNode(s->file, TARG_NOCREATE) != NILGNODE) { +#ifdef DEBUG_SRC + printf("remove %x from %x\n", s, srcs); +#endif + rs = s; + break; + } + + if ((ptr = Dir_FindFile(s->file, s->suff->searchPath)) != NULL) { + rs = s; +#ifdef DEBUG_SRC + printf("remove %x from %x\n", s, srcs); +#endif + free(ptr); + break; + } + + if (DEBUG(SUFF)) { + printf("not there\n"); + } + + SuffAddLevel(srcs, s); + Lst_AtEnd(slst, (ClientData) s); + } + + if (DEBUG(SUFF) && rs) { + printf("got it\n"); + } + return (rs); +} + +/*- + *----------------------------------------------------------------------- + * SuffFindCmds -- + * See if any of the children of the target in the Src structure is + * one from which the target can be transformed. If there is one, + * a Src structure is put together for it and returned. + * + * Input: + * targ Src structure to play with + * + * Results: + * The Src structure of the "winning" child, or NIL if no such beast. + * + * Side Effects: + * A Src structure may be allocated. + * + *----------------------------------------------------------------------- + */ +static Src * +SuffFindCmds(Src *targ, Lst slst) +{ + LstNode ln; /* General-purpose list node */ + GNode *t, /* Target GNode */ + *s; /* Source GNode */ + int prefLen;/* The length of the defined prefix */ + Suff *suff; /* Suffix on matching beastie */ + Src *ret; /* Return value */ + char *cp; + + t = targ->node; + (void)Lst_Open(t->children); + prefLen = strlen(targ->pref); + + for (;;) { + ln = Lst_Next(t->children); + if (ln == NILLNODE) { + Lst_Close(t->children); + return NULL; + } + s = (GNode *)Lst_Datum(ln); + + cp = strrchr(s->name, '/'); + if (cp == NULL) { + cp = s->name; + } else { + cp++; + } + if (strncmp(cp, targ->pref, prefLen) != 0) + continue; + /* + * The node matches the prefix ok, see if it has a known + * suffix. + */ + ln = Lst_Find(sufflist, (ClientData)&cp[prefLen], + SuffSuffHasNameP); + if (ln == NILLNODE) + continue; + /* + * It even has a known suffix, see if there's a transformation + * defined between the node's suffix and the target's suffix. + * + * XXX: Handle multi-stage transformations here, too. + */ + suff = (Suff *)Lst_Datum(ln); + + if (Lst_Member(suff->parents, (ClientData)targ->suff) != NILLNODE) + break; + } + + /* + * Hot Damn! Create a new Src structure to describe + * this transformation (making sure to duplicate the + * source node's name so Suff_FindDeps can free it + * again (ick)), and return the new structure. + */ + ret = emalloc(sizeof(Src)); + ret->file = estrdup(s->name); + ret->pref = targ->pref; + ret->suff = suff; + suff->refCount++; + ret->parent = targ; + ret->node = s; + ret->children = 0; + targ->children += 1; +#ifdef DEBUG_SRC + ret->cp = Lst_Init(FALSE); + printf("3 add %x %x\n", targ, ret); + Lst_AtEnd(targ->cp, (ClientData) ret); +#endif + Lst_AtEnd(slst, (ClientData) ret); + if (DEBUG(SUFF)) { + printf("\tusing existing source %s\n", s->name); + } + return (ret); +} + +/*- + *----------------------------------------------------------------------- + * SuffExpandChildren -- + * Expand the names of any children of a given node that contain + * variable invocations or file wildcards into actual targets. + * + * Input: + * prevLN Child to examine + * pgn Parent node being processed + * + * Results: + * === 0 (continue) + * + * Side Effects: + * The expanded node is removed from the parent's list of children, + * and the parent's unmade counter is decremented, but other nodes + * may be added. + * + *----------------------------------------------------------------------- + */ +static int +SuffExpandChildren(LstNode prevLN, GNode *pgn) +{ + GNode *cgn = (GNode *)Lst_Datum(prevLN); + GNode *gn; /* New source 8) */ + char *cp; /* Expanded value */ + + /* + * First do variable expansion -- this takes precedence over + * wildcard expansion. If the result contains wildcards, they'll be gotten + * to later since the resulting words are tacked on to the end of + * the children list. + */ + if (strchr(cgn->name, '$') != NULL) { + if (DEBUG(SUFF)) { + printf("Expanding \"%s\"...", cgn->name); + } + cp = Var_Subst(NULL, cgn->name, pgn, TRUE); + + if (cp != NULL) { + Lst members = Lst_Init(FALSE); + + if (cgn->type & OP_ARCHV) { + /* + * Node was an archive(member) target, so we want to call + * on the Arch module to find the nodes for us, expanding + * variables in the parent's context. + */ + char *sacrifice = cp; + + (void)Arch_ParseArchive(&sacrifice, members, pgn); + } else { + /* + * Break the result into a vector of strings whose nodes + * we can find, then add those nodes to the members list. + * Unfortunately, we can't use brk_string b/c it + * doesn't understand about variable specifications with + * spaces in them... + */ + char *start; + char *initcp = cp; /* For freeing... */ + + for (start = cp; *start == ' ' || *start == '\t'; start++) + continue; + for (cp = start; *cp != '\0'; cp++) { + if (*cp == ' ' || *cp == '\t') { + /* + * White-space -- terminate element, find the node, + * add it, skip any further spaces. + */ + *cp++ = '\0'; + gn = Targ_FindNode(start, TARG_CREATE); + (void)Lst_AtEnd(members, (ClientData)gn); + while (*cp == ' ' || *cp == '\t') { + cp++; + } + /* + * Adjust cp for increment at start of loop, but + * set start to first non-space. + */ + start = cp--; + } else if (*cp == '$') { + /* + * Start of a variable spec -- contact variable module + * to find the end so we can skip over it. + */ + char *junk; + int len; + Boolean doFree; + + junk = Var_Parse(cp, pgn, TRUE, &len, &doFree); + if (junk != var_Error) { + cp += len - 1; + } + + if (doFree) { + free(junk); + } + } else if (*cp == '\\' && *cp != '\0') { + /* + * Escaped something -- skip over it + */ + cp++; + } + } + + if (cp != start) { + /* + * Stuff left over -- add it to the list too + */ + gn = Targ_FindNode(start, TARG_CREATE); + (void)Lst_AtEnd(members, (ClientData)gn); + } + /* + * Point cp back at the beginning again so the variable value + * can be freed. + */ + cp = initcp; + } + /* + * Add all elements of the members list to the parent node. + */ + while(!Lst_IsEmpty(members)) { + gn = (GNode *)Lst_DeQueue(members); + + if (DEBUG(SUFF)) { + printf("%s...", gn->name); + } + if (Lst_Member(pgn->children, (ClientData)gn) == NILLNODE) { + (void)Lst_Append(pgn->children, prevLN, (ClientData)gn); + prevLN = Lst_Succ(prevLN); + (void)Lst_AtEnd(gn->parents, (ClientData)pgn); + pgn->unmade++; + } + } + Lst_Destroy(members, NOFREE); + /* + * Free the result + */ + free(cp); + } + /* + * Now the source is expanded, remove it from the list of children to + * keep it from being processed. + */ + if (DEBUG(SUFF)) { + printf("\n"); + } + return(1); + } else if (Dir_HasWildcards(cgn->name)) { + Lst explist; /* List of expansions */ + + /* + * Expand the word along the chosen path + */ + explist = Lst_Init(FALSE); + Dir_Expand(cgn->name, Suff_FindPath(cgn), explist); + + while (!Lst_IsEmpty(explist)) { + /* + * Fetch next expansion off the list and find its GNode + */ + cp = (char *)Lst_DeQueue(explist); + + if (DEBUG(SUFF)) { + printf("%s...", cp); + } + gn = Targ_FindNode(cp, TARG_CREATE); + + /* + * If gn isn't already a child of the parent, make it so and + * up the parent's count of unmade children. + */ + if (Lst_Member(pgn->children, (ClientData)gn) == NILLNODE) { + (void)Lst_Append(pgn->children, prevLN, (ClientData)gn); + prevLN = Lst_Succ(prevLN); + (void)Lst_AtEnd(gn->parents, (ClientData)pgn); + pgn->unmade++; + } + } + + /* + * Nuke what's left of the list + */ + Lst_Destroy(explist, NOFREE); + + /* + * Now the source is expanded, remove it from the list of children to + * keep it from being processed. + */ + if (DEBUG(SUFF)) { + printf("\n"); + } + return(1); + } + + return(0); +} + +/*- + *----------------------------------------------------------------------- + * Suff_FindPath -- + * Find a path along which to expand the node. + * + * If the word has a known suffix, use that path. + * If it has no known suffix, use the default system search path. + * + * Input: + * gn Node being examined + * + * Results: + * The appropriate path to search for the GNode. + * + * Side Effects: + * XXX: We could set the suffix here so that we don't have to scan + * again. + * + *----------------------------------------------------------------------- + */ +Lst +Suff_FindPath(GNode* gn) +{ + Suff *suff = gn->suffix; + + if (suff == NULL) { + SuffixCmpData sd; /* Search string data */ + LstNode ln; + sd.len = strlen(gn->name); + sd.ename = gn->name + sd.len; + ln = Lst_Find(sufflist, (ClientData)&sd, SuffSuffIsSuffixP); + + if (DEBUG(SUFF)) { + printf("Wildcard expanding \"%s\"...", gn->name); + } + if (ln != NILLNODE) + suff = (Suff *)Lst_Datum(ln); + /* XXX: Here we can save the suffix so we don't have to do this again */ + } + + if (suff != NULL) { + if (DEBUG(SUFF)) { + printf("suffix is \"%s\"...", suff->name); + } + return suff->searchPath; + } else { + /* + * Use default search path + */ + return dirSearchPath; + } +} + +/*- + *----------------------------------------------------------------------- + * SuffApplyTransform -- + * Apply a transformation rule, given the source and target nodes + * and suffixes. + * + * Input: + * tGn Target node + * sGn Source node + * t Target suffix + * s Source suffix + * + * Results: + * TRUE if successful, FALSE if not. + * + * Side Effects: + * The source and target are linked and the commands from the + * transformation are added to the target node's commands list. + * All attributes but OP_DEPMASK and OP_TRANSFORM are applied + * to the target. The target also inherits all the sources for + * the transformation rule. + * + *----------------------------------------------------------------------- + */ +static Boolean +SuffApplyTransform(GNode *tGn, GNode *sGn, Suff *t, Suff *s) +{ + LstNode ln, nln; /* General node */ + char *tname; /* Name of transformation rule */ + GNode *gn; /* Node for same */ + + /* + * Form the proper links between the target and source. + */ + (void)Lst_AtEnd(tGn->children, (ClientData)sGn); + (void)Lst_AtEnd(sGn->parents, (ClientData)tGn); + tGn->unmade += 1; + + /* + * Locate the transformation rule itself + */ + tname = str_concat(s->name, t->name, 0); + ln = Lst_Find(transforms, (ClientData)tname, SuffGNHasNameP); + free(tname); + + if (ln == NILLNODE) { + /* + * Not really such a transformation rule (can happen when we're + * called to link an OP_MEMBER and OP_ARCHV node), so return + * FALSE. + */ + return(FALSE); + } + + gn = (GNode *)Lst_Datum(ln); + + if (DEBUG(SUFF)) { + printf("\tapplying %s -> %s to \"%s\"\n", s->name, t->name, tGn->name); + } + + /* + * Record last child for expansion purposes + */ + ln = Lst_Last(tGn->children); + + /* + * Pass the buck to Make_HandleUse to apply the rule + */ + (void)Make_HandleUse(gn, tGn); + + /* + * Deal with wildcards and variables in any acquired sources + */ + ln = Lst_Succ(ln); + while (ln != NILLNODE) { + if (SuffExpandChildren(ln, tGn)) { + nln = Lst_Succ(ln); + tGn->unmade--; + Lst_Remove(tGn->children, ln); + ln = nln; + } else + ln = Lst_Succ(ln); + } + + /* + * Keep track of another parent to which this beast is transformed so + * the .IMPSRC variable can be set correctly for the parent. + */ + (void)Lst_AtEnd(sGn->iParents, (ClientData)tGn); + + return(TRUE); +} + + +/*- + *----------------------------------------------------------------------- + * SuffFindArchiveDeps -- + * Locate dependencies for an OP_ARCHV node. + * + * Input: + * gn Node for which to locate dependencies + * + * Results: + * None + * + * Side Effects: + * Same as Suff_FindDeps + * + *----------------------------------------------------------------------- + */ +static void +SuffFindArchiveDeps(GNode *gn, Lst slst) +{ + char *eoarch; /* End of archive portion */ + char *eoname; /* End of member portion */ + GNode *mem; /* Node for member */ + static const char *copy[] = { + /* Variables to be copied from the member node */ + TARGET, /* Must be first */ + PREFIX, /* Must be second */ + }; + int i; /* Index into copy and vals */ + Suff *ms; /* Suffix descriptor for member */ + char *name; /* Start of member's name */ + + /* + * The node is an archive(member) pair. so we must find a + * suffix for both of them. + */ + eoarch = strchr(gn->name, '('); + eoname = strchr(eoarch, ')'); + + *eoname = '\0'; /* Nuke parentheses during suffix search */ + *eoarch = '\0'; /* So a suffix can be found */ + + name = eoarch + 1; + + /* + * To simplify things, call Suff_FindDeps recursively on the member now, + * so we can simply compare the member's .PREFIX and .TARGET variables + * to locate its suffix. This allows us to figure out the suffix to + * use for the archive without having to do a quadratic search over the + * suffix list, backtracking for each one... + */ + mem = Targ_FindNode(name, TARG_CREATE); + SuffFindDeps(mem, slst); + + /* + * Create the link between the two nodes right off + */ + (void)Lst_AtEnd(gn->children, (ClientData)mem); + (void)Lst_AtEnd(mem->parents, (ClientData)gn); + gn->unmade += 1; + + /* + * Copy in the variables from the member node to this one. + */ + for (i = (sizeof(copy)/sizeof(copy[0]))-1; i >= 0; i--) { + char *p1; + Var_Set(copy[i], Var_Value(copy[i], mem, &p1), gn, 0); + if (p1) + free(p1); + + } + + ms = mem->suffix; + if (ms == NULL) { + /* + * Didn't know what it was -- use .NULL suffix if not in make mode + */ + if (DEBUG(SUFF)) { + printf("using null suffix\n"); + } + ms = suffNull; + } + + + /* + * Set the other two local variables required for this target. + */ + Var_Set(MEMBER, name, gn, 0); + Var_Set(ARCHIVE, gn->name, gn, 0); + + if (ms != NULL) { + /* + * Member has a known suffix, so look for a transformation rule from + * it to a possible suffix of the archive. Rather than searching + * through the entire list, we just look at suffixes to which the + * member's suffix may be transformed... + */ + LstNode ln; + SuffixCmpData sd; /* Search string data */ + + /* + * Use first matching suffix... + */ + sd.len = eoarch - gn->name; + sd.ename = eoarch; + ln = Lst_Find(ms->parents, &sd, SuffSuffIsSuffixP); + + if (ln != NILLNODE) { + /* + * Got one -- apply it + */ + if (!SuffApplyTransform(gn, mem, (Suff *)Lst_Datum(ln), ms) && + DEBUG(SUFF)) + { + printf("\tNo transformation from %s -> %s\n", + ms->name, ((Suff *)Lst_Datum(ln))->name); + } + } + } + + /* + * Replace the opening and closing parens now we've no need of the separate + * pieces. + */ + *eoarch = '('; *eoname = ')'; + + /* + * Pretend gn appeared to the left of a dependency operator so + * the user needn't provide a transformation from the member to the + * archive. + */ + if (OP_NOP(gn->type)) { + gn->type |= OP_DEPENDS; + } + + /* + * Flag the member as such so we remember to look in the archive for + * its modification time. + */ + mem->type |= OP_MEMBER; +} + +/*- + *----------------------------------------------------------------------- + * SuffFindNormalDeps -- + * Locate implicit dependencies for regular targets. + * + * Input: + * gn Node for which to find sources + * + * Results: + * None. + * + * Side Effects: + * Same as Suff_FindDeps... + * + *----------------------------------------------------------------------- + */ +static void +SuffFindNormalDeps(GNode *gn, Lst slst) +{ + char *eoname; /* End of name */ + char *sopref; /* Start of prefix */ + LstNode ln, nln; /* Next suffix node to check */ + Lst srcs; /* List of sources at which to look */ + Lst targs; /* List of targets to which things can be + * transformed. They all have the same file, + * but different suff and pref fields */ + Src *bottom; /* Start of found transformation path */ + Src *src; /* General Src pointer */ + char *pref; /* Prefix to use */ + Src *targ; /* General Src target pointer */ + SuffixCmpData sd; /* Search string data */ + + + sd.len = strlen(gn->name); + sd.ename = eoname = gn->name + sd.len; + + sopref = gn->name; + + /* + * Begin at the beginning... + */ + ln = Lst_First(sufflist); + srcs = Lst_Init(FALSE); + targs = Lst_Init(FALSE); + + /* + * We're caught in a catch-22 here. On the one hand, we want to use any + * transformation implied by the target's sources, but we can't examine + * the sources until we've expanded any variables/wildcards they may hold, + * and we can't do that until we've set up the target's local variables + * and we can't do that until we know what the proper suffix for the + * target is (in case there are two suffixes one of which is a suffix of + * the other) and we can't know that until we've found its implied + * source, which we may not want to use if there's an existing source + * that implies a different transformation. + * + * In an attempt to get around this, which may not work all the time, + * but should work most of the time, we look for implied sources first, + * checking transformations to all possible suffixes of the target, + * use what we find to set the target's local variables, expand the + * children, then look for any overriding transformations they imply. + * Should we find one, we discard the one we found before. + */ + + while (ln != NILLNODE) { + /* + * Look for next possible suffix... + */ + ln = Lst_FindFrom(sufflist, ln, &sd, SuffSuffIsSuffixP); + + if (ln != NILLNODE) { + int prefLen; /* Length of the prefix */ + + /* + * Allocate a Src structure to which things can be transformed + */ + targ = emalloc(sizeof(Src)); + targ->file = estrdup(gn->name); + targ->suff = (Suff *)Lst_Datum(ln); + targ->suff->refCount++; + targ->node = gn; + targ->parent = NULL; + targ->children = 0; +#ifdef DEBUG_SRC + targ->cp = Lst_Init(FALSE); +#endif + + /* + * Allocate room for the prefix, whose end is found by subtracting + * the length of the suffix from the end of the name. + */ + prefLen = (eoname - targ->suff->nameLen) - sopref; + targ->pref = emalloc(prefLen + 1); + memcpy(targ->pref, sopref, prefLen); + targ->pref[prefLen] = '\0'; + + /* + * Add nodes from which the target can be made + */ + SuffAddLevel(srcs, targ); + + /* + * Record the target so we can nuke it + */ + (void)Lst_AtEnd(targs, (ClientData)targ); + + /* + * Search from this suffix's successor... + */ + ln = Lst_Succ(ln); + } + } + + /* + * Handle target of unknown suffix... + */ + if (Lst_IsEmpty(targs) && suffNull != NULL) { + if (DEBUG(SUFF)) { + printf("\tNo known suffix on %s. Using .NULL suffix\n", gn->name); + } + + targ = emalloc(sizeof(Src)); + targ->file = estrdup(gn->name); + targ->suff = suffNull; + targ->suff->refCount++; + targ->node = gn; + targ->parent = NULL; + targ->children = 0; + targ->pref = estrdup(sopref); +#ifdef DEBUG_SRC + targ->cp = Lst_Init(FALSE); +#endif + + /* + * Only use the default suffix rules if we don't have commands + * defined for this gnode; traditional make programs used to + * not define suffix rules if the gnode had children but we + * don't do this anymore. + */ + if (Lst_IsEmpty(gn->commands)) + SuffAddLevel(srcs, targ); + else { + if (DEBUG(SUFF)) + printf("not "); + } + + if (DEBUG(SUFF)) + printf("adding suffix rules\n"); + + (void)Lst_AtEnd(targs, (ClientData)targ); + } + + /* + * Using the list of possible sources built up from the target suffix(es), + * try and find an existing file/target that matches. + */ + bottom = SuffFindThem(srcs, slst); + + if (bottom == NULL) { + /* + * No known transformations -- use the first suffix found for setting + * the local variables. + */ + if (!Lst_IsEmpty(targs)) { + targ = (Src *)Lst_Datum(Lst_First(targs)); + } else { + targ = NULL; + } + } else { + /* + * Work up the transformation path to find the suffix of the + * target to which the transformation was made. + */ + for (targ = bottom; targ->parent != NULL; targ = targ->parent) + continue; + } + + Var_Set(TARGET, gn->path ? gn->path : gn->name, gn, 0); + + pref = (targ != NULL) ? targ->pref : gn->name; + Var_Set(PREFIX, pref, gn, 0); + + /* + * Now we've got the important local variables set, expand any sources + * that still contain variables or wildcards in their names. + */ + ln = Lst_First(gn->children); + while (ln != NILLNODE) { + if (SuffExpandChildren(ln, gn)) { + nln = Lst_Succ(ln); + gn->unmade--; + Lst_Remove(gn->children, ln); + ln = nln; + } else + ln = Lst_Succ(ln); + } + + if (targ == NULL) { + if (DEBUG(SUFF)) { + printf("\tNo valid suffix on %s\n", gn->name); + } + +sfnd_abort: + /* + * Deal with finding the thing on the default search path. We + * always do that, not only if the node is only a source (not + * on the lhs of a dependency operator or [XXX] it has neither + * children or commands) as the old pmake did. + */ + if ((gn->type & (OP_PHONY|OP_NOPATH)) == 0) { + free(gn->path); + gn->path = Dir_FindFile(gn->name, + (targ == NULL ? dirSearchPath : + targ->suff->searchPath)); + if (gn->path != NULL) { + char *ptr; + Var_Set(TARGET, gn->path, gn, 0); + + if (targ != NULL) { + /* + * Suffix known for the thing -- trim the suffix off + * the path to form the proper .PREFIX variable. + */ + int savep = strlen(gn->path) - targ->suff->nameLen; + char savec; + + if (gn->suffix) + gn->suffix->refCount--; + gn->suffix = targ->suff; + gn->suffix->refCount++; + + savec = gn->path[savep]; + gn->path[savep] = '\0'; + + if ((ptr = strrchr(gn->path, '/')) != NULL) + ptr++; + else + ptr = gn->path; + + Var_Set(PREFIX, ptr, gn, 0); + + gn->path[savep] = savec; + } else { + /* + * The .PREFIX gets the full path if the target has + * no known suffix. + */ + if (gn->suffix) + gn->suffix->refCount--; + gn->suffix = NULL; + + if ((ptr = strrchr(gn->path, '/')) != NULL) + ptr++; + else + ptr = gn->path; + + Var_Set(PREFIX, ptr, gn, 0); + } + } + } + + goto sfnd_return; + } + + /* + * If the suffix indicates that the target is a library, mark that in + * the node's type field. + */ + if (targ->suff->flags & SUFF_LIBRARY) { + gn->type |= OP_LIB; + } + + /* + * Check for overriding transformation rule implied by sources + */ + if (!Lst_IsEmpty(gn->children)) { + src = SuffFindCmds(targ, slst); + + if (src != NULL) { + /* + * Free up all the Src structures in the transformation path + * up to, but not including, the parent node. + */ + while (bottom && bottom->parent != NULL) { + if (Lst_Member(slst, (ClientData) bottom) == NILLNODE) { + Lst_AtEnd(slst, (ClientData) bottom); + } + bottom = bottom->parent; + } + bottom = src; + } + } + + if (bottom == NULL) { + /* + * No idea from where it can come -- return now. + */ + goto sfnd_abort; + } + + /* + * We now have a list of Src structures headed by 'bottom' and linked via + * their 'parent' pointers. What we do next is create links between + * source and target nodes (which may or may not have been created) + * and set the necessary local variables in each target. The + * commands for each target are set from the commands of the + * transformation rule used to get from the src suffix to the targ + * suffix. Note that this causes the commands list of the original + * node, gn, to be replaced by the commands of the final + * transformation rule. Also, the unmade field of gn is incremented. + * Etc. + */ + if (bottom->node == NILGNODE) { + bottom->node = Targ_FindNode(bottom->file, TARG_CREATE); + } + + for (src = bottom; src->parent != NULL; src = src->parent) { + targ = src->parent; + + if (src->node->suffix) + src->node->suffix->refCount--; + src->node->suffix = src->suff; + src->node->suffix->refCount++; + + if (targ->node == NILGNODE) { + targ->node = Targ_FindNode(targ->file, TARG_CREATE); + } + + SuffApplyTransform(targ->node, src->node, + targ->suff, src->suff); + + if (targ->node != gn) { + /* + * Finish off the dependency-search process for any nodes + * between bottom and gn (no point in questing around the + * filesystem for their implicit source when it's already + * known). Note that the node can't have any sources that + * need expanding, since SuffFindThem will stop on an existing + * node, so all we need to do is set the standard and System V + * variables. + */ + targ->node->type |= OP_DEPS_FOUND; + + Var_Set(PREFIX, targ->pref, targ->node, 0); + + Var_Set(TARGET, targ->node->name, targ->node, 0); + } + } + + if (gn->suffix) + gn->suffix->refCount--; + gn->suffix = src->suff; + gn->suffix->refCount++; + + /* + * Nuke the transformation path and the Src structures left over in the + * two lists. + */ +sfnd_return: + if (bottom) + if (Lst_Member(slst, (ClientData) bottom) == NILLNODE) + Lst_AtEnd(slst, (ClientData) bottom); + + while (SuffRemoveSrc(srcs) || SuffRemoveSrc(targs)) + continue; + + Lst_Concat(slst, srcs, LST_CONCLINK); + Lst_Concat(slst, targs, LST_CONCLINK); +} + + +/*- + *----------------------------------------------------------------------- + * Suff_FindDeps -- + * Find implicit sources for the target described by the graph node + * gn + * + * Results: + * Nothing. + * + * Side Effects: + * Nodes are added to the graph below the passed-in node. The nodes + * are marked to have their IMPSRC variable filled in. The + * PREFIX variable is set for the given node and all its + * implied children. + * + * Notes: + * The path found by this target is the shortest path in the + * transformation graph, which may pass through non-existent targets, + * to an existing target. The search continues on all paths from the + * root suffix until a file is found. I.e. if there's a path + * .o -> .c -> .l -> .l,v from the root and the .l,v file exists but + * the .c and .l files don't, the search will branch out in + * all directions from .o and again from all the nodes on the + * next level until the .l,v node is encountered. + * + *----------------------------------------------------------------------- + */ + +void +Suff_FindDeps(GNode *gn) +{ + + SuffFindDeps(gn, srclist); + while (SuffRemoveSrc(srclist)) + continue; +} + + +/* + * Input: + * gn node we're dealing with + * + */ +static void +SuffFindDeps(GNode *gn, Lst slst) +{ + if (gn->type & (OP_DEPS_FOUND|OP_PHONY)) { + /* + * If dependencies already found, no need to do it again... + * If this is a .PHONY target, we do not apply suffix rules. + */ + return; + } else { + gn->type |= OP_DEPS_FOUND; + } + + if (DEBUG(SUFF)) { + printf("SuffFindDeps (%s)\n", gn->name); + } + + if (gn->type & OP_ARCHV) { + SuffFindArchiveDeps(gn, slst); + } else if (gn->type & OP_LIB) { + /* + * If the node is a library, it is the arch module's job to find it + * and set the TARGET variable accordingly. We merely provide the + * search path, assuming all libraries end in ".a" (if the suffix + * hasn't been defined, there's nothing we can do for it, so we just + * set the TARGET variable to the node's name in order to give it a + * value). + */ + LstNode ln; + Suff *s; + + ln = Lst_Find(sufflist, (ClientData)UNCONST(LIBSUFF), + SuffSuffHasNameP); + if (gn->suffix) + gn->suffix->refCount--; + if (ln != NILLNODE) { + gn->suffix = s = (Suff *)Lst_Datum(ln); + gn->suffix->refCount++; + Arch_FindLib(gn, s->searchPath); + } else { + gn->suffix = NULL; + Var_Set(TARGET, gn->name, gn, 0); + } + /* + * Because a library (-lfoo) target doesn't follow the standard + * filesystem conventions, we don't set the regular variables for + * the thing. .PREFIX is simply made empty... + */ + Var_Set(PREFIX, "", gn, 0); + } else { + SuffFindNormalDeps(gn, slst); + } +} + +/*- + *----------------------------------------------------------------------- + * Suff_SetNull -- + * Define which suffix is the null suffix. + * + * Input: + * name Name of null suffix + * + * Results: + * None. + * + * Side Effects: + * 'suffNull' is altered. + * + * Notes: + * Need to handle the changing of the null suffix gracefully so the + * old transformation rules don't just go away. + * + *----------------------------------------------------------------------- + */ +void +Suff_SetNull(char *name) +{ + Suff *s; + LstNode ln; + + ln = Lst_Find(sufflist, (ClientData)name, SuffSuffHasNameP); + if (ln != NILLNODE) { + s = (Suff *)Lst_Datum(ln); + if (suffNull != NULL) { + suffNull->flags &= ~SUFF_NULL; + } + s->flags |= SUFF_NULL; + /* + * XXX: Here's where the transformation mangling would take place + */ + suffNull = s; + } else { + Parse_Error(PARSE_WARNING, "Desired null suffix %s not defined.", + name); + } +} + +/*- + *----------------------------------------------------------------------- + * Suff_Init -- + * Initialize suffixes module + * + * Results: + * None + * + * Side Effects: + * Many + *----------------------------------------------------------------------- + */ +void +Suff_Init(void) +{ + sufflist = Lst_Init(FALSE); +#ifdef CLEANUP + suffClean = Lst_Init(FALSE); +#endif + srclist = Lst_Init(FALSE); + transforms = Lst_Init(FALSE); + + sNum = 0; + /* + * Create null suffix for single-suffix rules (POSIX). The thing doesn't + * actually go on the suffix list or everyone will think that's its + * suffix. + */ + emptySuff = suffNull = emalloc(sizeof(Suff)); + + suffNull->name = estrdup(""); + suffNull->nameLen = 0; + suffNull->searchPath = Lst_Init(FALSE); + Dir_Concat(suffNull->searchPath, dirSearchPath); + suffNull->children = Lst_Init(FALSE); + suffNull->parents = Lst_Init(FALSE); + suffNull->ref = Lst_Init(FALSE); + suffNull->sNum = sNum++; + suffNull->flags = SUFF_NULL; + suffNull->refCount = 1; + +} + + +/*- + *---------------------------------------------------------------------- + * Suff_End -- + * Cleanup the this module + * + * Results: + * None + * + * Side Effects: + * The memory is free'd. + *---------------------------------------------------------------------- + */ + +void +Suff_End(void) +{ +#ifdef CLEANUP + Lst_Destroy(sufflist, SuffFree); + Lst_Destroy(suffClean, SuffFree); + if (suffNull) + SuffFree(suffNull); + Lst_Destroy(srclist, NOFREE); + Lst_Destroy(transforms, NOFREE); +#endif +} + + +/********************* DEBUGGING FUNCTIONS **********************/ + +static int SuffPrintName(ClientData s, ClientData dummy) +{ + printf("%s ", ((Suff *)s)->name); + return (dummy ? 0 : 0); +} + +static int +SuffPrintSuff(ClientData sp, ClientData dummy) +{ + Suff *s = (Suff *)sp; + int flags; + int flag; + + printf("# `%s' [%d] ", s->name, s->refCount); + + flags = s->flags; + if (flags) { + fputs(" (", stdout); + while (flags) { + flag = 1 << (ffs(flags) - 1); + flags &= ~flag; + switch (flag) { + case SUFF_NULL: + printf("NULL"); + break; + case SUFF_INCLUDE: + printf("INCLUDE"); + break; + case SUFF_LIBRARY: + printf("LIBRARY"); + break; + } + fputc(flags ? '|' : ')', stdout); + } + } + fputc('\n', stdout); + printf("#\tTo: "); + Lst_ForEach(s->parents, SuffPrintName, (ClientData)0); + fputc('\n', stdout); + printf("#\tFrom: "); + Lst_ForEach(s->children, SuffPrintName, (ClientData)0); + fputc('\n', stdout); + printf("#\tSearch Path: "); + Dir_PrintPath(s->searchPath); + fputc('\n', stdout); + return (dummy ? 0 : 0); +} + +static int +SuffPrintTrans(ClientData tp, ClientData dummy) +{ + GNode *t = (GNode *)tp; + + printf("%-16s: ", t->name); + Targ_PrintType(t->type); + fputc('\n', stdout); + Lst_ForEach(t->commands, Targ_PrintCmd, (ClientData)0); + fputc('\n', stdout); + return(dummy ? 0 : 0); +} + +void +Suff_PrintAll(void) +{ + printf("#*** Suffixes:\n"); + Lst_ForEach(sufflist, SuffPrintSuff, (ClientData)0); + + printf("#*** Transformations:\n"); + Lst_ForEach(transforms, SuffPrintTrans, (ClientData)0); +} diff --git a/devel/bmake/files/targ.c b/devel/bmake/files/targ.c new file mode 100644 index 0000000000000..3d8606885cb51 --- /dev/null +++ b/devel/bmake/files/targ.c @@ -0,0 +1,757 @@ +/* $NetBSD: targ.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: targ.c,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)targ.c 8.2 (Berkeley) 3/19/94"; +#else +__RCSID("$NetBSD: targ.c,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * targ.c -- + * Functions for maintaining the Lst allTargets. Target nodes are + * kept in two structures: a Lst, maintained by the list library, and a + * hash table, maintained by the hash library. + * + * Interface: + * Targ_Init Initialization procedure. + * + * Targ_End Cleanup the module + * + * Targ_List Return the list of all targets so far. + * + * Targ_NewGN Create a new GNode for the passed target + * (string). The node is *not* placed in the + * hash table, though all its fields are + * initialized. + * + * Targ_FindNode Find the node for a given target, creating + * and storing it if it doesn't exist and the + * flags are right (TARG_CREATE) + * + * Targ_FindList Given a list of names, find nodes for all + * of them. If a name doesn't exist and the + * TARG_NOCREATE flag was given, an error message + * is printed. Else, if a name doesn't exist, + * its node is created. + * + * Targ_Ignore Return TRUE if errors should be ignored when + * creating the given target. + * + * Targ_Silent Return TRUE if we should be silent when + * creating the given target. + * + * Targ_Precious Return TRUE if the target is precious and + * should not be removed if we are interrupted. + * + * Debugging: + * Targ_PrintGraph Print out the entire graphm all variables + * and statistics for the directory cache. Should + * print something for suffixes, too, but... + */ + +#include +#include + +#include "make.h" +#include "hash.h" +#include "dir.h" + +static Lst allTargets; /* the list of all targets found so far */ +#ifdef CLEANUP +static Lst allGNs; /* List of all the GNodes */ +#endif +static Hash_Table targets; /* a hash table of same */ + +#define HTSIZE 191 /* initial size of hash table */ + +static int TargPrintOnlySrc(ClientData, ClientData); +static int TargPrintName(ClientData, ClientData); +static int TargPrintNode(ClientData, ClientData); +#ifdef CLEANUP +static void TargFreeGN(ClientData); +#endif +static int TargPropagateCohort(ClientData, ClientData); +static int TargPropagateNode(ClientData, ClientData); + +/*- + *----------------------------------------------------------------------- + * Targ_Init -- + * Initialize this module + * + * Results: + * None + * + * Side Effects: + * The allTargets list and the targets hash table are initialized + *----------------------------------------------------------------------- + */ +void +Targ_Init(void) +{ + allTargets = Lst_Init(FALSE); + Hash_InitTable(&targets, HTSIZE); +} + +/*- + *----------------------------------------------------------------------- + * Targ_End -- + * Finalize this module + * + * Results: + * None + * + * Side Effects: + * All lists and gnodes are cleared + *----------------------------------------------------------------------- + */ +void +Targ_End(void) +{ +#ifdef CLEANUP + Lst_Destroy(allTargets, NOFREE); + if (allGNs) + Lst_Destroy(allGNs, TargFreeGN); + Hash_DeleteTable(&targets); +#endif +} + +/*- + *----------------------------------------------------------------------- + * Targ_List -- + * Return the list of all targets + * + * Results: + * The list of all targets. + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +Lst +Targ_List(void) +{ + return allTargets; +} + +/*- + *----------------------------------------------------------------------- + * Targ_NewGN -- + * Create and initialize a new graph node + * + * Input: + * name the name to stick in the new node + * + * Results: + * An initialized graph node with the name field filled with a copy + * of the passed name + * + * Side Effects: + * The gnode is added to the list of all gnodes. + *----------------------------------------------------------------------- + */ +GNode * +Targ_NewGN(const char *name) +{ + GNode *gn; + + gn = emalloc(sizeof(GNode)); + gn->name = estrdup(name); + gn->uname = NULL; + gn->path = NULL; + if (name[0] == '-' && name[1] == 'l') { + gn->type = OP_LIB; + } else { + gn->type = 0; + } + gn->unmade = 0; + gn->unmade_cohorts = 0; + gn->centurion = NULL; + gn->made = UNMADE; + gn->flags = 0; + gn->order = 0; + gn->mtime = gn->cmtime = 0; + gn->iParents = Lst_Init(FALSE); + gn->cohorts = Lst_Init(FALSE); + gn->parents = Lst_Init(FALSE); + gn->children = Lst_Init(FALSE); + gn->successors = Lst_Init(FALSE); + gn->preds = Lst_Init(FALSE); + Hash_InitTable(&gn->context, 0); + gn->commands = Lst_Init(FALSE); + gn->suffix = NULL; + gn->lineno = 0; + gn->fname = NULL; + +#ifdef CLEANUP + if (allGNs == NULL) + allGNs = Lst_Init(FALSE); + Lst_AtEnd(allGNs, (ClientData) gn); +#endif + + return (gn); +} + +#ifdef CLEANUP +/*- + *----------------------------------------------------------------------- + * TargFreeGN -- + * Destroy a GNode + * + * Results: + * None. + * + * Side Effects: + * None. + *----------------------------------------------------------------------- + */ +static void +TargFreeGN(ClientData gnp) +{ + GNode *gn = (GNode *)gnp; + + + free(gn->name); + if (gn->uname) + free(gn->uname); + if (gn->path) + free(gn->path); + if (gn->fname) + free(gn->fname); + + Lst_Destroy(gn->iParents, NOFREE); + Lst_Destroy(gn->cohorts, NOFREE); + Lst_Destroy(gn->parents, NOFREE); + Lst_Destroy(gn->children, NOFREE); + Lst_Destroy(gn->successors, NOFREE); + Lst_Destroy(gn->preds, NOFREE); + Hash_DeleteTable(&gn->context); + Lst_Destroy(gn->commands, NOFREE); + free(gn); +} +#endif + + +/*- + *----------------------------------------------------------------------- + * Targ_FindNode -- + * Find a node in the list using the given name for matching + * + * Input: + * name the name to find + * flags flags governing events when target not + * found + * + * Results: + * The node in the list if it was. If it wasn't, return NILGNODE of + * flags was TARG_NOCREATE or the newly created and initialized node + * if it was TARG_CREATE + * + * Side Effects: + * Sometimes a node is created and added to the list + *----------------------------------------------------------------------- + */ +GNode * +Targ_FindNode(const char *name, int flags) +{ + GNode *gn; /* node in that element */ + Hash_Entry *he; /* New or used hash entry for node */ + Boolean isNew; /* Set TRUE if Hash_CreateEntry had to create */ + /* an entry for the node */ + + + if (flags & TARG_CREATE) { + he = Hash_CreateEntry(&targets, name, &isNew); + if (isNew) { + gn = Targ_NewGN(name); + Hash_SetValue(he, gn); + Var_Append(".ALLTARGETS", name, VAR_GLOBAL); + (void)Lst_AtEnd(allTargets, (ClientData)gn); + } + } else { + he = Hash_FindEntry(&targets, name); + } + + if (he == NULL) { + return (NILGNODE); + } else { + return ((GNode *)Hash_GetValue(he)); + } +} + +/*- + *----------------------------------------------------------------------- + * Targ_FindList -- + * Make a complete list of GNodes from the given list of names + * + * Input: + * name list of names to find + * flags flags used if no node is found for a given name + * + * Results: + * A complete list of graph nodes corresponding to all instances of all + * the names in names. + * + * Side Effects: + * If flags is TARG_CREATE, nodes will be created for all names in + * names which do not yet have graph nodes. If flags is TARG_NOCREATE, + * an error message will be printed for each name which can't be found. + * ----------------------------------------------------------------------- + */ +Lst +Targ_FindList(Lst names, int flags) +{ + Lst nodes; /* result list */ + LstNode ln; /* name list element */ + GNode *gn; /* node in tLn */ + char *name; + + nodes = Lst_Init(FALSE); + + if (Lst_Open(names) == FAILURE) { + return (nodes); + } + while ((ln = Lst_Next(names)) != NILLNODE) { + name = (char *)Lst_Datum(ln); + gn = Targ_FindNode(name, flags); + if (gn != NILGNODE) { + /* + * Note: Lst_AtEnd must come before the Lst_Concat so the nodes + * are added to the list in the order in which they were + * encountered in the makefile. + */ + (void)Lst_AtEnd(nodes, (ClientData)gn); + } else if (flags == TARG_NOCREATE) { + Error("\"%s\" -- target unknown.", name); + } + } + Lst_Close(names); + return (nodes); +} + +/*- + *----------------------------------------------------------------------- + * Targ_Ignore -- + * Return true if should ignore errors when creating gn + * + * Input: + * gn node to check for + * + * Results: + * TRUE if should ignore errors + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +Boolean +Targ_Ignore(GNode *gn) +{ + if (ignoreErrors || gn->type & OP_IGNORE) { + return (TRUE); + } else { + return (FALSE); + } +} + +/*- + *----------------------------------------------------------------------- + * Targ_Silent -- + * Return true if be silent when creating gn + * + * Input: + * gn node to check for + * + * Results: + * TRUE if should be silent + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +Boolean +Targ_Silent(GNode *gn) +{ + if (beSilent || gn->type & OP_SILENT) { + return (TRUE); + } else { + return (FALSE); + } +} + +/*- + *----------------------------------------------------------------------- + * Targ_Precious -- + * See if the given target is precious + * + * Input: + * gn the node to check + * + * Results: + * TRUE if it is precious. FALSE otherwise + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +Boolean +Targ_Precious(GNode *gn) +{ + if (allPrecious || (gn->type & (OP_PRECIOUS|OP_DOUBLEDEP))) { + return (TRUE); + } else { + return (FALSE); + } +} + +/******************* DEBUG INFO PRINTING ****************/ + +static GNode *mainTarg; /* the main target, as set by Targ_SetMain */ +/*- + *----------------------------------------------------------------------- + * Targ_SetMain -- + * Set our idea of the main target we'll be creating. Used for + * debugging output. + * + * Input: + * gn The main target we'll create + * + * Results: + * None. + * + * Side Effects: + * "mainTarg" is set to the main target's node. + *----------------------------------------------------------------------- + */ +void +Targ_SetMain(GNode *gn) +{ + mainTarg = gn; +} + +static int +TargPrintName(ClientData gnp, ClientData ppath) +{ + GNode *gn = (GNode *)gnp; + printf("%s ", gn->name); +#ifdef notdef + if (ppath) { + if (gn->path) { + printf("[%s] ", gn->path); + } + if (gn == mainTarg) { + printf("(MAIN NAME) "); + } + } +#endif /* notdef */ + return (ppath ? 0 : 0); +} + + +int +Targ_PrintCmd(ClientData cmd, ClientData dummy) +{ + printf("\t%s\n", (char *)cmd); + return (dummy ? 0 : 0); +} + +/*- + *----------------------------------------------------------------------- + * Targ_FmtTime -- + * Format a modification time in some reasonable way and return it. + * + * Results: + * The time reformatted. + * + * Side Effects: + * The time is placed in a static area, so it is overwritten + * with each call. + * + *----------------------------------------------------------------------- + */ +char * +Targ_FmtTime(time_t tm) +{ + struct tm *parts; + static char buf[128]; + + parts = localtime(&tm); + (void)strftime(buf, sizeof buf, "%k:%M:%S %b %d, %Y", parts); + return(buf); +} + +/*- + *----------------------------------------------------------------------- + * Targ_PrintType -- + * Print out a type field giving only those attributes the user can + * set. + * + * Results: + * + * Side Effects: + * + *----------------------------------------------------------------------- + */ +void +Targ_PrintType(int type) +{ + int tbit; + +#define PRINTBIT(attr) case CONCAT(OP_,attr): printf("." #attr " "); break +#define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG))printf("." #attr " "); break + + type &= ~OP_OPMASK; + + while (type) { + tbit = 1 << (ffs(type) - 1); + type &= ~tbit; + + switch(tbit) { + PRINTBIT(OPTIONAL); + PRINTBIT(USE); + PRINTBIT(EXEC); + PRINTBIT(IGNORE); + PRINTBIT(PRECIOUS); + PRINTBIT(SILENT); + PRINTBIT(MAKE); + PRINTBIT(JOIN); + PRINTBIT(INVISIBLE); + PRINTBIT(NOTMAIN); + PRINTDBIT(LIB); + /*XXX: MEMBER is defined, so CONCAT(OP_,MEMBER) gives OP_"%" */ + case OP_MEMBER: if (DEBUG(TARG))printf(".MEMBER "); break; + PRINTDBIT(ARCHV); + PRINTDBIT(MADE); + PRINTDBIT(PHONY); + } + } +} + +/*- + *----------------------------------------------------------------------- + * TargPrintNode -- + * print the contents of a node + *----------------------------------------------------------------------- + */ +static int +TargPrintNode(ClientData gnp, ClientData passp) +{ + GNode *gn = (GNode *)gnp; + int pass = *(int *)passp; + if (!OP_NOP(gn->type)) { + printf("#\n"); + if (gn == mainTarg) { + printf("# *** MAIN TARGET ***\n"); + } + if (pass == 2) { + if (gn->unmade) { + printf("# %d unmade children\n", gn->unmade); + } else { + printf("# No unmade children\n"); + } + if (! (gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC))) { + if (gn->mtime != 0) { + printf("# last modified %s: %s\n", + Targ_FmtTime(gn->mtime), + (gn->made == UNMADE ? "unmade" : + (gn->made == MADE ? "made" : + (gn->made == UPTODATE ? "up-to-date" : + "error when made")))); + } else if (gn->made != UNMADE) { + printf("# non-existent (maybe): %s\n", + (gn->made == MADE ? "made" : + (gn->made == UPTODATE ? "up-to-date" : + (gn->made == ERROR ? "error when made" : + "aborted")))); + } else { + printf("# unmade\n"); + } + } + if (!Lst_IsEmpty (gn->iParents)) { + printf("# implicit parents: "); + Lst_ForEach(gn->iParents, TargPrintName, (ClientData)0); + fputc('\n', stdout); + } + } + if (!Lst_IsEmpty (gn->parents)) { + printf("# parents: "); + Lst_ForEach(gn->parents, TargPrintName, (ClientData)0); + fputc('\n', stdout); + } + + printf("%-16s", gn->name); + switch (gn->type & OP_OPMASK) { + case OP_DEPENDS: + printf(": "); break; + case OP_FORCE: + printf("! "); break; + case OP_DOUBLEDEP: + printf(":: "); break; + } + Targ_PrintType(gn->type); + Lst_ForEach(gn->children, TargPrintName, (ClientData)0); + fputc('\n', stdout); + Lst_ForEach(gn->commands, Targ_PrintCmd, (ClientData)0); + printf("\n\n"); + if (gn->type & OP_DOUBLEDEP) { + Lst_ForEach(gn->cohorts, TargPrintNode, (ClientData)&pass); + } + } + return (0); +} + +/*- + *----------------------------------------------------------------------- + * TargPrintOnlySrc -- + * Print only those targets that are just a source. + * + * Results: + * 0. + * + * Side Effects: + * The name of each file is printed preceded by #\t + * + *----------------------------------------------------------------------- + */ +static int +TargPrintOnlySrc(ClientData gnp, ClientData dummy) +{ + GNode *gn = (GNode *)gnp; + if (OP_NOP(gn->type)) + printf("#\t%s [%s]\n", gn->name, gn->path ? gn->path : gn->name); + + return (dummy ? 0 : 0); +} + +/*- + *----------------------------------------------------------------------- + * Targ_PrintGraph -- + * print the entire graph. heh heh + * + * Input: + * pass Which pass this is. 1 => no processing + * 2 => processing done + * + * Results: + * none + * + * Side Effects: + * lots o' output + *----------------------------------------------------------------------- + */ +void +Targ_PrintGraph(int pass) +{ + printf("#*** Input graph:\n"); + Lst_ForEach(allTargets, TargPrintNode, (ClientData)&pass); + printf("\n\n"); + printf("#\n# Files that are only sources:\n"); + Lst_ForEach(allTargets, TargPrintOnlySrc, (ClientData) 0); + printf("#*** Global Variables:\n"); + Var_Dump(VAR_GLOBAL); + printf("#*** Command-line Variables:\n"); + Var_Dump(VAR_CMD); + printf("\n"); + Dir_PrintDirectories(); + printf("\n"); + Suff_PrintAll(); +} + +static int +TargPropagateCohort(ClientData cgnp, ClientData pgnp) +{ + GNode *cgn = (GNode *)cgnp; + GNode *pgn = (GNode *)pgnp; + + cgn->type |= pgn->type & ~OP_OPMASK; + return (0); +} + +static int +TargPropagateNode(ClientData gnp, ClientData junk __unused) +{ + GNode *gn = (GNode *)gnp; + if (gn->type & OP_DOUBLEDEP) + Lst_ForEach(gn->cohorts, TargPropagateCohort, gnp); + return (0); +} + +void +Targ_Propagate(void) +{ + Lst_ForEach(allTargets, TargPropagateNode, (ClientData)0); +} diff --git a/devel/bmake/files/trace.c b/devel/bmake/files/trace.c new file mode 100644 index 0000000000000..bc5b91eb9ec70 --- /dev/null +++ b/devel/bmake/files/trace.c @@ -0,0 +1,123 @@ +/* $NetBSD: trace.c,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Bill Sommerfeld + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: trace.c,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $"; +#else +#include +#ifndef lint +__RCSID("$NetBSD: trace.c,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $"); +#endif /* not lint */ +#endif + +/*- + * trace.c -- + * handle logging of trace events generated by various parts of make. + * + * Interface: + * Trace_Init Initialize tracing (called once during + * the lifetime of the process) + * + * Trace_End Finalize tracing (called before make exits) + * + * Trace_Log Log an event about a particular make job. + */ + +#include + +#include +#include + +#include "make.h" +#include "job.h" +#include "trace.h" + +static FILE *trfile; +static pid_t trpid; +char *trwd; + +static const char *evname[] = { + "BEG", + "END", + "ERR", + "JOB", + "DON", + "INT", +}; + +void +Trace_Init(const char *pathname) +{ + char *p1; + if (pathname != NULL) { + trpid = getpid(); + trwd = Var_Value(".CURDIR", VAR_GLOBAL, &p1); + + trfile = fopen(pathname, "a"); + } +} + +void +Trace_Log(TrEvent event, Job *job) +{ + struct timeval rightnow; + + if (trfile == NULL) + return; + + gettimeofday(&rightnow, NULL); + + fprintf(trfile, "%ld.%06d %d %d %s %d %s", + rightnow.tv_sec, (int)rightnow.tv_usec, + jobTokensRunning, jobTokensFree, + evname[event], trpid, trwd); + if (job != NULL) { + fprintf(trfile, " %s %d %x %x", job->node->name, + job->pid, job->flags, job->node->type); + } + fputc('\n', trfile); + fflush(trfile); +} + +void +Trace_End(void) +{ + if (trfile != NULL) + fclose(trfile); +} diff --git a/devel/bmake/files/trace.h b/devel/bmake/files/trace.h new file mode 100644 index 0000000000000..0f1acc9787f77 --- /dev/null +++ b/devel/bmake/files/trace.h @@ -0,0 +1,56 @@ +/* $NetBSD: trace.h,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Bill Sommerfeld + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/*- + * trace.h -- + * Definitions pertaining to the tracing of jobs in parallel mode. + */ + +typedef enum { + MAKESTART, + MAKEEND, + MAKEERROR, + JOBSTART, + JOBEND, + MAKEINTR +} TrEvent; + +void Trace_Init(const char *); +void Trace_Log(TrEvent, Job *); +void Trace_End(void); + diff --git a/devel/bmake/files/unit-tests/Makefile.in b/devel/bmake/files/unit-tests/Makefile.in new file mode 100644 index 0000000000000..d24ff7838bbcf --- /dev/null +++ b/devel/bmake/files/unit-tests/Makefile.in @@ -0,0 +1,67 @@ +# $Id: Makefile.in,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $ +# +# $NetBSD: Makefile.in,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $ +# +# Unit tests for make(1) +# The main targets are: +# +# all: run all the tests +# test: run 'all', capture output and compare to expected results +# accept: move generated output to expected results +# +# Adding a test case. +# Each feature should get its own set of tests in its own suitably +# named makefile which should be added to SUBFILES to hook it in. +# + +srcdir= @srcdir@ + +.MAIN: all + +UNIT_TESTS:= ${srcdir} + +# Simple sub-makefiles - we run them as a black box +# keep the list sorted. +SUBFILES= \ + comment \ + cond1 \ + modmatch \ + modorder \ + modts \ + modword \ + posix \ + ternary \ + varcmd + +all: ${SUBFILES} + +# the tests are actually done with sub-makes. +.PHONY: ${SUBFILES} +${SUBFILES}: + -@${.MAKE} -k -f ${UNIT_TESTS}/$@ + +clean: + rm -f *.out *.fail *.core + +.include + +TEST_MAKE?= ${.MAKE} + +# The driver. +# We always pretend .MAKE was called 'make' +# and strip ${.CURDIR}/ from the output +# and replace anything after 'stopped in' with unit-tests +# so the results can be compared. +test: + @echo "${TEST_MAKE} -f ${MAKEFILE} > ${.TARGET}.out 2>&1" + @cd ${.OBJDIR} && ${TEST_MAKE} -f ${MAKEFILE} 2>&1 | \ + sed -e 's,^${TEST_MAKE:T}:,make:,' \ + -e '/stopped/s, /.*, unit-tests,' \ + -e 's,${.CURDIR}/,,g' \ + -e 's,${UNIT_TESTS}/,,g' > ${.TARGET}.out || { \ + tail ${.TARGET}.out; mv ${.TARGET}.out ${.TARGET}.fail; exit 1; } + diff @diff_u@ ${UNIT_TESTS}/${.TARGET}.exp ${.TARGET}.out + +accept: + mv test.out ${srcdir}/test.exp + diff --git a/devel/bmake/files/unit-tests/comment b/devel/bmake/files/unit-tests/comment new file mode 100644 index 0000000000000..7dd7dbbe28b5d --- /dev/null +++ b/devel/bmake/files/unit-tests/comment @@ -0,0 +1,31 @@ +# This is a comment +.if ${MACHINE_ARCH} == something +FOO=bar +.endif + +#\ + Multiline comment + +BAR=# defined +FOOBAR= # defined + +# This is an escaped comment \ +that keeps going until the end of this line + +# Another escaped comment \ +that \ +goes \ +on + +# This is NOT an escaped comment due to the double backslashes \\ +all: hi foo bar + @echo comment testing done + +hi: + @echo comment testing start + +foo: + @echo this is $@ + +bar: + @echo This is how a comment looks: '# comment' diff --git a/devel/bmake/files/unit-tests/cond1 b/devel/bmake/files/unit-tests/cond1 new file mode 100644 index 0000000000000..04c0a8780d47f --- /dev/null +++ b/devel/bmake/files/unit-tests/cond1 @@ -0,0 +1,102 @@ +# $Id: cond1,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $ + +# hard code these! +TEST_UNAME_S= NetBSD +TEST_UNAME_M= sparc +TEST_MACHINE= i386 + +.if ${TEST_UNAME_S} +Ok=var, +.endif +.if ("${TEST_UNAME_S}") +Ok+=(\"var\"), +.endif +.if (${TEST_UNAME_M} != ${TEST_MACHINE}) +Ok+=(var != var), +.endif +.if ${TEST_UNAME_M} != ${TEST_MACHINE} +Ok+= var != var, +.endif +.if !((${TEST_UNAME_M} != ${TEST_MACHINE}) && defined(X)) +Ok+= !((var != var) && defined(name)), +.endif +# from bsd.obj.mk +MKOBJ?=no +.if ${MKOBJ} == "no" +o= no +Ok+= var == "quoted", +.else +.if defined(notMAKEOBJDIRPREFIX) || defined(norMAKEOBJDIR) +.if defined(notMAKEOBJDIRPREFIX) +o=${MAKEOBJDIRPREFIX}${__curdir} +.else +o= ${MAKEOBJDIR} +.endif +.endif +o= o +.endif + +# repeat the above to check we get the same result +.if ${MKOBJ} == "no" +o2= no +.else +.if defined(notMAKEOBJDIRPREFIX) || defined(norMAKEOBJDIR) +.if defined(notMAKEOBJDIRPREFIX) +o2=${MAKEOBJDIRPREFIX}${__curdir} +.else +o2= ${MAKEOBJDIR} +.endif +.endif +o2= o +.endif + +PRIMES=2 3 5 7 11 +NUMBERS=1 2 3 4 5 + +n=2 +.if ${PRIMES:M$n} == "" +X=not +.else +X= +.endif + +.if ${MACHINE_ARCH} == no-such +A=one +.else +.if ${MACHINE_ARCH} == not-this +.if ${MACHINE_ARCH} == something-else +A=unlikely +.else +A=no +.endif +.endif +A=other +# We expect an extra else warning - we're not skipping here +.else +A=this should be an error +.endif + +.if $X != "" +.if $X == not +B=one +.else +B=other +# We expect an extra else warning - we are skipping here +.else +B=this should be an error +.endif +.else +B=unknown +.endif + +.if "quoted" == quoted +C=clever +.else +C=dim +.endif + +all: + @echo "$n is $X prime" + @echo "A='$A' B='$B' C='$C' o='$o,${o2}'" + @echo "Passed:${.newline} ${Ok:S/,/${.newline}/}" + @echo "${NUMBERS:@n@$n is ${("${PRIMES:M$n}" == ""):?not:} prime${.newline}@}" diff --git a/devel/bmake/files/unit-tests/modmatch b/devel/bmake/files/unit-tests/modmatch new file mode 100644 index 0000000000000..4ada35e35fd69 --- /dev/null +++ b/devel/bmake/files/unit-tests/modmatch @@ -0,0 +1,18 @@ + +X=a b c d e + +.for x in $X +LIB${x:tu}=/tmp/lib$x.a +.endfor + +X_LIBS= ${LIBA} ${LIBD} ${LIBE} + +LIB?=a + +all: + @for x in $X; do ${.MAKE} -f ${MAKEFILE} show LIB=$$x; done + +show: + @echo 'LIB=${LIB} X_LIBS:M$${LIB$${LIB:tu}} is "${X_LIBS:M${LIB${LIB:tu}}}"' + @echo 'LIB=${LIB} X_LIBS:M*/lib$${LIB}.a is "${X_LIBS:M*/lib${LIB}.a}"' + @echo 'LIB=${LIB} X_LIBS:M*/lib$${LIB}.a:tu is "${X_LIBS:M*/lib${LIB}.a:tu}"' diff --git a/devel/bmake/files/unit-tests/modorder b/devel/bmake/files/unit-tests/modorder new file mode 100644 index 0000000000000..276fa9eccfaef --- /dev/null +++ b/devel/bmake/files/unit-tests/modorder @@ -0,0 +1,22 @@ +# $NetBSD: modorder,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $ + +LIST= one two three four five six seven eigth nine ten +LISTX= ${LIST:Ox} +LISTSX:= ${LIST:Ox} +TEST_RESULT= && echo Ok || echo Failed + +# unit-tests have to produce the same results on each run +# so we cannot actually include :Ox output. +all: + @echo "LIST = ${LIST}" + @echo "LIST:O = ${LIST:O}" + # Note that 1 in every 10! trials two independently generated + # randomized orderings will be the same. The test framework doesn't + # support checking probabilistic output, so we accept that the test + # will incorrectly fail with probability 2.8E-7. + @echo "LIST:Ox = `test '${LIST:Ox}' != '${LIST:Ox}' ${TEST_RESULT}`" + @echo "LIST:O:Ox = `test '${LIST:O:Ox}' != '${LIST:O:Ox}' ${TEST_RESULT}`" + @echo "LISTX = `test '${LISTX}' != '${LISTX}' ${TEST_RESULT}`" + @echo "LISTSX = `test '${LISTSX}' = '${LISTSX}' ${TEST_RESULT}`" + @echo "BADMOD 1 = ${LIST:OX}" + @echo "BADMOD 2 = ${LIST:OxXX}" diff --git a/devel/bmake/files/unit-tests/modts b/devel/bmake/files/unit-tests/modts new file mode 100644 index 0000000000000..d0efd6d19c042 --- /dev/null +++ b/devel/bmake/files/unit-tests/modts @@ -0,0 +1,32 @@ + +LIST= one two three +LIST+= four five six + +FU_mod-ts = a / b / cool + +AAA= a a a +B.aaa= Baaa + +all: mod-ts + +mod-ts: + @echo 'LIST="${LIST}"' + @echo 'LIST:ts,="${LIST:ts,}"' + @echo 'LIST:ts/:tu="${LIST:ts/:tu}"' + @echo 'LIST:ts::tu="${LIST:ts::tu}"' + @echo 'LIST:ts:tu="${LIST:ts:tu}"' + @echo 'LIST:tu:ts/="${LIST:tu:ts/}"' + @echo 'LIST:ts:="${LIST:ts:}"' + @echo 'LIST:ts="${LIST:ts}"' + @echo 'LIST:ts:S/two/2/="${LIST:ts:S/two/2/}"' + @echo 'LIST:S/two/2/:ts="${LIST:S/two/2/:ts}"' + @echo 'LIST:ts/:S/two/2/="${LIST:ts/:S/two/2/}"' + @echo "Pretend the '/' in '/n' etc. below are back-slashes." + @echo 'LIST:ts/n="${LIST:ts\n}"' + @echo 'LIST:ts/t="${LIST:ts\t}"' + @echo 'LIST:ts/012:tu="${LIST:ts\012:tu}"' + @echo 'LIST:tx="${LIST:tx}"' + @echo 'LIST:ts/x:tu="${LIST:ts\x:tu}"' + @echo 'FU_$@="${FU_${@:ts}:ts}"' + @echo 'FU_$@:ts:T="${FU_${@:ts}:ts:T}" == cool?' + @echo 'B.$${AAA:ts}="${B.${AAA:ts}}" == Baaa?' diff --git a/devel/bmake/files/unit-tests/modword b/devel/bmake/files/unit-tests/modword new file mode 100644 index 0000000000000..982378a2c9b58 --- /dev/null +++ b/devel/bmake/files/unit-tests/modword @@ -0,0 +1,151 @@ +# $Id: modword,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $ +# +# Test behaviour of new :[] modifier + +all: mod-squarebrackets mod-S-W mod-C-W mod-tW-tw + +LIST= one two three +LIST+= four five six +LONGLIST= 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 + +EMPTY= # the space should be ignored +ESCAPEDSPACE=\ # escaped space before the '#' +REALLYSPACE:=${EMPTY:C/^/ /W} +HASH= \# +AT= @ +STAR= * +ZERO= 0 +ONE= 1 +MINUSONE= -1 + +mod-squarebrackets: mod-squarebrackets-0-star-at \ + mod-squarebrackets-hash \ + mod-squarebrackets-n \ + mod-squarebrackets-start-end \ + mod-squarebrackets-nested + +mod-squarebrackets-0-star-at: + @echo 'LIST:[]="${LIST:[]}" is an error' + @echo 'LIST:[0]="${LIST:[0]}"' + @echo 'LIST:[0x0]="${LIST:[0x0]}"' + @echo 'LIST:[000]="${LIST:[000]}"' + @echo 'LIST:[*]="${LIST:[*]}"' + @echo 'LIST:[@]="${LIST:[@]}"' + @echo 'LIST:[0]:C/ /,/="${LIST:[0]:C/ /,/}"' + @echo 'LIST:[0]:C/ /,/g="${LIST:[0]:C/ /,/g}"' + @echo 'LIST:[0]:C/ /,/1g="${LIST:[0]:C/ /,/1g}"' + @echo 'LIST:[*]:C/ /,/="${LIST:[*]:C/ /,/}"' + @echo 'LIST:[*]:C/ /,/g="${LIST:[*]:C/ /,/g}"' + @echo 'LIST:[*]:C/ /,/1g="${LIST:[*]:C/ /,/1g}"' + @echo 'LIST:[@]:C/ /,/="${LIST:[@]:C/ /,/}"' + @echo 'LIST:[@]:C/ /,/g="${LIST:[@]:C/ /,/g}"' + @echo 'LIST:[@]:C/ /,/1g="${LIST:[@]:C/ /,/1g}"' + @echo 'LIST:[@]:[0]:C/ /,/="${LIST:[@]:[0]:C/ /,/}"' + @echo 'LIST:[0]:[@]:C/ /,/="${LIST:[0]:[@]:C/ /,/}"' + @echo 'LIST:[@]:[*]:C/ /,/="${LIST:[@]:[*]:C/ /,/}"' + @echo 'LIST:[*]:[@]:C/ /,/="${LIST:[*]:[@]:C/ /,/}"' + +mod-squarebrackets-hash: + @echo 'EMPTY="${EMPTY}"' + @echo 'EMPTY:[#]="${EMPTY:[#]}" == 1 ?' + @echo 'ESCAPEDSPACE="${ESCAPEDSPACE}"' + @echo 'ESCAPEDSPACE:[#]="${ESCAPEDSPACE:[#]}" == 1 ?' + @echo 'REALLYSPACE="${REALLYSPACE}"' + @echo 'REALLYSPACE:[#]="${REALLYSPACE:[#]}" == 1 ?' + @echo 'LIST:[#]="${LIST:[#]}"' + @echo 'LIST:[0]:[#]="${LIST:[0]:[#]}" == 1 ?' + @echo 'LIST:[*]:[#]="${LIST:[*]:[#]}" == 1 ?' + @echo 'LIST:[@]:[#]="${LIST:[@]:[#]}"' + @echo 'LIST:[1]:[#]="${LIST:[1]:[#]}"' + @echo 'LIST:[1..3]:[#]="${LIST:[1..3]:[#]}"' + +mod-squarebrackets-n: + @echo 'EMPTY:[1]="${EMPTY:[1]}"' + @echo 'ESCAPEDSPACE="${ESCAPEDSPACE}"' + @echo 'ESCAPEDSPACE:[1]="${ESCAPEDSPACE:[1]}"' + @echo 'REALLYSPACE="${REALLYSPACE}"' + @echo 'REALLYSPACE:[1]="${REALLYSPACE:[1]}" == "" ?' + @echo 'REALLYSPACE:[*]:[1]="${REALLYSPACE:[*]:[1]}" == " " ?' + @echo 'LIST:[1]="${LIST:[1]}"' + @echo 'LIST:[1.]="${LIST:[1.]}" is an error' + @echo 'LIST:[1].="${LIST:[1].}" is an error' + @echo 'LIST:[2]="${LIST:[2]}"' + @echo 'LIST:[6]="${LIST:[6]}"' + @echo 'LIST:[7]="${LIST:[7]}"' + @echo 'LIST:[999]="${LIST:[999]}"' + @echo 'LIST:[-]="${LIST:[-]}" is an error' + @echo 'LIST:[--]="${LIST:[--]}" is an error' + @echo 'LIST:[-1]="${LIST:[-1]}"' + @echo 'LIST:[-2]="${LIST:[-2]}"' + @echo 'LIST:[-6]="${LIST:[-6]}"' + @echo 'LIST:[-7]="${LIST:[-7]}"' + @echo 'LIST:[-999]="${LIST:[-999]}"' + @echo 'LONGLIST:[17]="${LONGLIST:[17]}"' + @echo 'LONGLIST:[0x11]="${LONGLIST:[0x11]}"' + @echo 'LONGLIST:[021]="${LONGLIST:[021]}"' + @echo 'LIST:[0]:[1]="${LIST:[0]:[1]}"' + @echo 'LIST:[*]:[1]="${LIST:[*]:[1]}"' + @echo 'LIST:[@]:[1]="${LIST:[@]:[1]}"' + @echo 'LIST:[0]:[2]="${LIST:[0]:[2]}"' + @echo 'LIST:[*]:[2]="${LIST:[*]:[2]}"' + @echo 'LIST:[@]:[2]="${LIST:[@]:[2]}"' + @echo 'LIST:[*]:C/ /,/:[2]="${LIST:[*]:C/ /,/:[2]}"' + @echo 'LIST:[*]:C/ /,/:[*]:[2]="${LIST:[*]:C/ /,/:[*]:[2]}"' + @echo 'LIST:[*]:C/ /,/:[@]:[2]="${LIST:[*]:C/ /,/:[@]:[2]}"' + +mod-squarebrackets-start-end: + @echo 'LIST:[1.]="${LIST:[1.]}" is an error' + @echo 'LIST:[1..]="${LIST:[1..]}" is an error' + @echo 'LIST:[1..1]="${LIST:[1..1]}"' + @echo 'LIST:[1..1.]="${LIST:[1..1.]}" is an error' + @echo 'LIST:[1..2]="${LIST:[1..2]}"' + @echo 'LIST:[2..1]="${LIST:[2..1]}"' + @echo 'LIST:[3..-2]="${LIST:[3..-2]}"' + @echo 'LIST:[-4..4]="${LIST:[-4..4]}"' + @echo 'LIST:[0..1]="${LIST:[0..1]}" is an error' + @echo 'LIST:[-1..0]="${LIST:[-1..0]}" is an error' + @echo 'LIST:[-1..1]="${LIST:[-1..1]}"' + @echo 'LIST:[0..0]="${LIST:[0..0]}"' + @echo 'LIST:[3..99]="${LIST:[3..99]}"' + @echo 'LIST:[-3..-99]="${LIST:[-3..-99]}"' + @echo 'LIST:[-99..-3]="${LIST:[-99..-3]}"' + +mod-squarebrackets-nested: + @echo 'HASH="${HASH}" == "#" ?' + @echo 'LIST:[$${HASH}]="${LIST:[${HASH}]}"' + @echo 'LIST:[$${ZERO}]="${LIST:[${ZERO}]}"' + @echo 'LIST:[$${ZERO}x$${ONE}]="${LIST:[${ZERO}x${ONE}]}"' + @echo 'LIST:[$${ONE}]="${LIST:[${ONE}]}"' + @echo 'LIST:[$${MINUSONE}]="${LIST:[${MINUSONE}]}"' + @echo 'LIST:[$${STAR}]="${LIST:[${STAR}]}"' + @echo 'LIST:[$${AT}]="${LIST:[${AT}]}"' + @echo 'LIST:[$${EMPTY}]="${LIST:[${EMPTY}]}" is an error' + @echo 'LIST:[$${LONGLIST:[21]:S/2//}]="${LIST:[${LONGLIST:[21]:S/2//}]}"' + @echo 'LIST:[$${LIST:[#]}]="${LIST:[${LIST:[#]}]}"' + @echo 'LIST:[$${LIST:[$${HASH}]}]="${LIST:[${LIST:[${HASH}]}]}"' + +mod-C-W: + @echo 'LIST:C/ /,/="${LIST:C/ /,/}"' + @echo 'LIST:C/ /,/W="${LIST:C/ /,/W}"' + @echo 'LIST:C/ /,/gW="${LIST:C/ /,/gW}"' + @echo 'EMPTY:C/^/,/="${EMPTY:C/^/,/}"' + @echo 'EMPTY:C/^/,/W="${EMPTY:C/^/,/W}"' + +mod-S-W: + @echo 'LIST:S/ /,/="${LIST:S/ /,/}"' + @echo 'LIST:S/ /,/W="${LIST:S/ /,/W}"' + @echo 'LIST:S/ /,/gW="${LIST:S/ /,/gW}"' + @echo 'EMPTY:S/^/,/="${EMPTY:S/^/,/}"' + @echo 'EMPTY:S/^/,/W="${EMPTY:S/^/,/W}"' + +mod-tW-tw: + @echo 'LIST:tW="${LIST:tW}"' + @echo 'LIST:tw="${LIST:tw}"' + @echo 'LIST:tW:C/ /,/="${LIST:tW:C/ /,/}"' + @echo 'LIST:tW:C/ /,/g="${LIST:tW:C/ /,/g}"' + @echo 'LIST:tW:C/ /,/1g="${LIST:tW:C/ /,/1g}"' + @echo 'LIST:tw:C/ /,/="${LIST:tw:C/ /,/}"' + @echo 'LIST:tw:C/ /,/g="${LIST:tw:C/ /,/g}"' + @echo 'LIST:tw:C/ /,/1g="${LIST:tw:C/ /,/1g}"' + @echo 'LIST:tw:tW:C/ /,/="${LIST:tw:tW:C/ /,/}"' + @echo 'LIST:tW:tw:C/ /,/="${LIST:tW:tw:C/ /,/}"' diff --git a/devel/bmake/files/unit-tests/posix b/devel/bmake/files/unit-tests/posix new file mode 100644 index 0000000000000..7915112c88387 --- /dev/null +++ b/devel/bmake/files/unit-tests/posix @@ -0,0 +1,24 @@ +# $Id: posix,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $ + +all: x plus subs err + +x: + @echo "Posix says we should execute the command as if run by system(3)" + @echo "Expect 'Hello,' and 'World!'" + @echo Hello,; false; echo "World!" + +plus: + @echo a command + +@echo "a command prefixed by '+' executes even with -n" + @echo another command + +subs: + @echo make -n + @${.MAKE} -f ${MAKEFILE} -n plus + @echo make -n -j1 + @${.MAKE} -f ${MAKEFILE} -n -j1 plus + +err: + @(echo Now we expect an error...; exit 1) + @echo "Oops! you shouldn't see this!" + diff --git a/devel/bmake/files/unit-tests/ternary b/devel/bmake/files/unit-tests/ternary new file mode 100644 index 0000000000000..77f834981c6ce --- /dev/null +++ b/devel/bmake/files/unit-tests/ternary @@ -0,0 +1,8 @@ + +all: + @for x in "" A= A=42; do ${.MAKE} -f ${MAKEFILE} show $$x; done + +show: + @echo "The answer is ${A:?known:unknown}" + @echo "The answer is ${A:?$A:unknown}" + @echo "The answer is ${empty(A):?empty:$A}" diff --git a/devel/bmake/files/unit-tests/test.exp b/devel/bmake/files/unit-tests/test.exp new file mode 100644 index 0000000000000..c21619bca86cb --- /dev/null +++ b/devel/bmake/files/unit-tests/test.exp @@ -0,0 +1,240 @@ +comment testing start +this is foo +This is how a comment looks: # comment +comment testing done +make: "cond1" line 75: warning: extra else +make: "cond1" line 85: warning: extra else +2 is prime +A='other' B='unknown' C='clever' o='no,no' +Passed: + var + ("var") + (var != var) + var != var + !((var != var) && defined(name)) + var == quoted + +1 is not prime +2 is prime +3 is prime +4 is not prime +5 is prime + +LIB=a X_LIBS:M${LIB${LIB:tu}} is "/tmp/liba.a" +LIB=a X_LIBS:M*/lib${LIB}.a is "/tmp/liba.a" +LIB=a X_LIBS:M*/lib${LIB}.a:tu is "/TMP/LIBA.A" +LIB=b X_LIBS:M${LIB${LIB:tu}} is "" +LIB=b X_LIBS:M*/lib${LIB}.a is "" +LIB=b X_LIBS:M*/lib${LIB}.a:tu is "" +LIB=c X_LIBS:M${LIB${LIB:tu}} is "" +LIB=c X_LIBS:M*/lib${LIB}.a is "" +LIB=c X_LIBS:M*/lib${LIB}.a:tu is "" +LIB=d X_LIBS:M${LIB${LIB:tu}} is "/tmp/libd.a" +LIB=d X_LIBS:M*/lib${LIB}.a is "/tmp/libd.a" +LIB=d X_LIBS:M*/lib${LIB}.a:tu is "/TMP/LIBD.A" +LIB=e X_LIBS:M${LIB${LIB:tu}} is "/tmp/libe.a" +LIB=e X_LIBS:M*/lib${LIB}.a is "/tmp/libe.a" +LIB=e X_LIBS:M*/lib${LIB}.a:tu is "/TMP/LIBE.A" +LIST = one two three four five six seven eigth nine ten +LIST:O = eigth five four nine one seven six ten three two +# Note that 1 in every 10! trials two independently generated +# randomized orderings will be the same. The test framework doesn't +# support checking probabilistic output, so we accept that the test +# will incorrectly fail with probability 2.8E-7. +LIST:Ox = Ok +LIST:O:Ox = Ok +LISTX = Ok +LISTSX = Ok +make: Bad modifier `:OX' for LIST +BADMOD 1 = } +make: Bad modifier `:OxXX' for LIST +BADMOD 2 = XX} +LIST="one two three four five six" +LIST:ts,="one,two,three,four,five,six" +LIST:ts/:tu="ONE/TWO/THREE/FOUR/FIVE/SIX" +LIST:ts::tu="ONE:TWO:THREE:FOUR:FIVE:SIX" +LIST:ts:tu="ONETWOTHREEFOURFIVESIX" +LIST:tu:ts/="ONE/TWO/THREE/FOUR/FIVE/SIX" +LIST:ts:="one:two:three:four:five:six" +LIST:ts="onetwothreefourfivesix" +LIST:ts:S/two/2/="one2threefourfivesix" +LIST:S/two/2/:ts="one2threefourfivesix" +LIST:ts/:S/two/2/="one/2/three/four/five/six" +Pretend the '/' in '/n' etc. below are back-slashes. +LIST:ts/n="one +two +three +four +five +six" +LIST:ts/t="one two three four five six" +LIST:ts/012:tu="ONE +TWO +THREE +FOUR +FIVE +SIX" +make: Bad modifier `:tx' for LIST +LIST:tx="}" +make: Bad modifier `:ts\x' for LIST +LIST:ts/x:tu="\x:tu}" +FU_mod-ts="a/b/cool" +FU_mod-ts:ts:T="cool" == cool? +B.${AAA:ts}="Baaa" == Baaa? +make: Bad modifier `:[]' for LIST +LIST:[]="" is an error +LIST:[0]="one two three four five six" +LIST:[0x0]="one two three four five six" +LIST:[000]="one two three four five six" +LIST:[*]="one two three four five six" +LIST:[@]="one two three four five six" +LIST:[0]:C/ /,/="one,two three four five six" +LIST:[0]:C/ /,/g="one,two,three,four,five,six" +LIST:[0]:C/ /,/1g="one,two,three,four,five,six" +LIST:[*]:C/ /,/="one,two three four five six" +LIST:[*]:C/ /,/g="one,two,three,four,five,six" +LIST:[*]:C/ /,/1g="one,two,three,four,five,six" +LIST:[@]:C/ /,/="one two three four five six" +LIST:[@]:C/ /,/g="one two three four five six" +LIST:[@]:C/ /,/1g="one two three four five six" +LIST:[@]:[0]:C/ /,/="one,two three four five six" +LIST:[0]:[@]:C/ /,/="one two three four five six" +LIST:[@]:[*]:C/ /,/="one,two three four five six" +LIST:[*]:[@]:C/ /,/="one two three four five six" +EMPTY="" +EMPTY:[#]="1" == 1 ? +ESCAPEDSPACE="\ " +ESCAPEDSPACE:[#]="1" == 1 ? +REALLYSPACE=" " +REALLYSPACE:[#]="1" == 1 ? +LIST:[#]="6" +LIST:[0]:[#]="1" == 1 ? +LIST:[*]:[#]="1" == 1 ? +LIST:[@]:[#]="6" +LIST:[1]:[#]="1" +LIST:[1..3]:[#]="3" +EMPTY:[1]="" +ESCAPEDSPACE="\ " +ESCAPEDSPACE:[1]="\ " +REALLYSPACE=" " +REALLYSPACE:[1]="" == "" ? +REALLYSPACE:[*]:[1]=" " == " " ? +LIST:[1]="one" +make: Bad modifier `:[1.]' for LIST +LIST:[1.]="" is an error +make: Bad modifier `:[1].' for LIST +LIST:[1].="}" is an error +LIST:[2]="two" +LIST:[6]="six" +LIST:[7]="" +LIST:[999]="" +make: Bad modifier `:[-]' for LIST +LIST:[-]="" is an error +make: Bad modifier `:[--]' for LIST +LIST:[--]="" is an error +LIST:[-1]="six" +LIST:[-2]="five" +LIST:[-6]="one" +LIST:[-7]="" +LIST:[-999]="" +LONGLIST:[17]="17" +LONGLIST:[0x11]="17" +LONGLIST:[021]="17" +LIST:[0]:[1]="one two three four five six" +LIST:[*]:[1]="one two three four five six" +LIST:[@]:[1]="one" +LIST:[0]:[2]="" +LIST:[*]:[2]="" +LIST:[@]:[2]="two" +LIST:[*]:C/ /,/:[2]="" +LIST:[*]:C/ /,/:[*]:[2]="" +LIST:[*]:C/ /,/:[@]:[2]="three" +make: Bad modifier `:[1.]' for LIST +LIST:[1.]="" is an error +make: Bad modifier `:[1..]' for LIST +LIST:[1..]="" is an error +LIST:[1..1]="one" +make: Bad modifier `:[1..1.]' for LIST +LIST:[1..1.]="" is an error +LIST:[1..2]="one two" +LIST:[2..1]="two one" +LIST:[3..-2]="three four five" +LIST:[-4..4]="three four" +make: Bad modifier `:[0..1]' for LIST +LIST:[0..1]="" is an error +make: Bad modifier `:[-1..0]' for LIST +LIST:[-1..0]="" is an error +LIST:[-1..1]="six five four three two one" +LIST:[0..0]="one two three four five six" +LIST:[3..99]="three four five six" +LIST:[-3..-99]="four three two one" +LIST:[-99..-3]="one two three four" +HASH="#" == "#" ? +LIST:[${HASH}]="6" +LIST:[${ZERO}]="one two three four five six" +LIST:[${ZERO}x${ONE}]="one" +LIST:[${ONE}]="one" +LIST:[${MINUSONE}]="six" +LIST:[${STAR}]="one two three four five six" +LIST:[${AT}]="one two three four five six" +make: Bad modifier `:[${EMPTY' for LIST +LIST:[${EMPTY}]="" is an error +LIST:[${LONGLIST:[21]:S/2//}]="one" +LIST:[${LIST:[#]}]="six" +LIST:[${LIST:[${HASH}]}]="six" +LIST:S/ /,/="one two three four five six" +LIST:S/ /,/W="one,two three four five six" +LIST:S/ /,/gW="one,two,three,four,five,six" +EMPTY:S/^/,/="," +EMPTY:S/^/,/W="," +LIST:C/ /,/="one two three four five six" +LIST:C/ /,/W="one,two three four five six" +LIST:C/ /,/gW="one,two,three,four,five,six" +EMPTY:C/^/,/="," +EMPTY:C/^/,/W="," +LIST:tW="one two three four five six" +LIST:tw="one two three four five six" +LIST:tW:C/ /,/="one,two three four five six" +LIST:tW:C/ /,/g="one,two,three,four,five,six" +LIST:tW:C/ /,/1g="one,two,three,four,five,six" +LIST:tw:C/ /,/="one two three four five six" +LIST:tw:C/ /,/g="one two three four five six" +LIST:tw:C/ /,/1g="one two three four five six" +LIST:tw:tW:C/ /,/="one,two three four five six" +LIST:tW:tw:C/ /,/="one two three four five six" +Posix says we should execute the command as if run by system(3) +Expect 'Hello,' and 'World!' +Hello, +World! +a command +a command prefixed by '+' executes even with -n +another command +make -n +echo a command +echo "a command prefixed by '+' executes even with -n" +a command prefixed by '+' executes even with -n +echo another command +make -n -j1 +{ echo a command +} || exit $? +echo "a command prefixed by '+' executes even with -n" +a command prefixed by '+' executes even with -n +{ echo another command +} || exit $? +Now we expect an error... +*** Error code 1 (continuing) +`all' not remade because of errors. +The answer is unknown +The answer is unknown +The answer is empty +The answer is known +The answer is +The answer is empty +The answer is known +The answer is 42 +The answer is 42 +default FU=fu FOO=foo VAR= +two FU=bar FOO=goo VAR= +three FU=bar FOO=goo VAR= +four FU=bar FOO=goo VAR=Internal +show FU=bar FOO=goo VAR=Internal diff --git a/devel/bmake/files/unit-tests/varcmd b/devel/bmake/files/unit-tests/varcmd new file mode 100644 index 0000000000000..62062f95be9b9 --- /dev/null +++ b/devel/bmake/files/unit-tests/varcmd @@ -0,0 +1,33 @@ +# $Id: varcmd,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $ +# +# Test behaviour of recursive make and vars set on command line. + +FU=fu +FOO?=foo +.if !empty(.TARGETS) +TAG=${.TARGETS} +.endif +TAG?=default + +all: one + +show: + @echo "${TAG} FU=${FU} FOO=${FOO} VAR=${VAR}" + +one: show + @${.MAKE} -f ${MAKEFILE} FU=bar FOO=goo two + +two: show + @${.MAKE} -f ${MAKEFILE} three + +three: show + @${.MAKE} -f ${MAKEFILE} four + + +.ifmake four +VAR=Internal +.MAKEOVERRIDES+= VAR +.endif + +four: show + @${.MAKE} -f ${MAKEFILE} show diff --git a/devel/bmake/files/util.c b/devel/bmake/files/util.c new file mode 100644 index 0000000000000..f2a5024895eb9 --- /dev/null +++ b/devel/bmake/files/util.c @@ -0,0 +1,512 @@ +/* $NetBSD: util.c,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $ */ + +/* + * Missing stuff from OS's + * + * $Id: util.c,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $ + */ + +#include "make.h" + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: util.c,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $"; +#else +#ifndef lint +__RCSID("$NetBSD: util.c,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $"); +#endif +#endif + +#include +#include + +#if !defined(HAVE_STRERROR) +extern int errno, sys_nerr; +extern char *sys_errlist[]; + +char * +strerror(int e) +{ + static char buf[100]; + if (e < 0 || e >= sys_nerr) { + snprintf(buf, sizeof(buf), "Unknown error %d", e); + return buf; + } + else + return sys_errlist[e]; +} +#endif + +#if !defined(HAVE_STRDUP) +#include + +/* strdup + * + * Make a duplicate of a string. + * For systems which lack this function. + */ +char * +strdup(const char *str) +{ + size_t len; + char *p; + + if (str == NULL) + return NULL; + len = strlen(str) + 1; + p = emalloc(len); + + return memcpy(p, str, len); +} +#endif + +#if !defined(HAVE_SETENV) +int +setenv(const char *name, const char *value, int dum) +{ + char *p; + int len = strlen(name) + strlen(value) + 2; /* = \0 */ + char *ptr = emalloc(len); + + (void) dum; + + if (ptr == NULL) + return -1; + + p = ptr; + + while (*name) + *p++ = *name++; + + *p++ = '='; + + while (*value) + *p++ = *value++; + + *p = '\0'; + + len = putenv(ptr); +/* free(ptr); */ + return len; +} +#endif + +#if defined(__hpux__) || defined(__hpux) +/* strrcpy(): + * Like strcpy, going backwards and returning the new pointer + */ +static char * +strrcpy(char *ptr, char *str) +{ + int len = strlen(str); + + while (len) + *--ptr = str[--len]; + + return (ptr); +} /* end strrcpy */ + + +char *sys_siglist[] = { + "Signal 0", + "Hangup", /* SIGHUP */ + "Interrupt", /* SIGINT */ + "Quit", /* SIGQUIT */ + "Illegal instruction", /* SIGILL */ + "Trace/BPT trap", /* SIGTRAP */ + "IOT trap", /* SIGIOT */ + "EMT trap", /* SIGEMT */ + "Floating point exception", /* SIGFPE */ + "Killed", /* SIGKILL */ + "Bus error", /* SIGBUS */ + "Segmentation fault", /* SIGSEGV */ + "Bad system call", /* SIGSYS */ + "Broken pipe", /* SIGPIPE */ + "Alarm clock", /* SIGALRM */ + "Terminated", /* SIGTERM */ + "User defined signal 1", /* SIGUSR1 */ + "User defined signal 2", /* SIGUSR2 */ + "Child exited", /* SIGCLD */ + "Power-fail restart", /* SIGPWR */ + "Virtual timer expired", /* SIGVTALRM */ + "Profiling timer expired", /* SIGPROF */ + "I/O possible", /* SIGIO */ + "Window size changes", /* SIGWINDOW */ + "Stopped (signal)", /* SIGSTOP */ + "Stopped", /* SIGTSTP */ + "Continued", /* SIGCONT */ + "Stopped (tty input)", /* SIGTTIN */ + "Stopped (tty output)", /* SIGTTOU */ + "Urgent I/O condition", /* SIGURG */ + "Remote lock lost (NFS)", /* SIGLOST */ + "Signal 31", /* reserved */ + "DIL signal" /* SIGDIL */ +}; +#endif /* __hpux__ || __hpux */ + +#if defined(__hpux__) || defined(__hpux) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int +killpg(int pid, int sig) +{ + return kill(-pid, sig); +} + +#if !defined(__hpux__) && !defined(__hpux) +void +srandom(long seed) +{ + srand48(seed); +} + +long +random(void) +{ + return lrand48(); +} +#endif + +/* turn into bsd signals */ +void (* +signal(int s, void (*a)(int)))(int) +{ + struct sigvec osv, sv; + + (void)sigvector(s, NULL, &osv); + sv = osv; + sv.sv_handler = a; +#ifdef SV_BSDSIG + sv.sv_flags = SV_BSDSIG; +#endif + + if (sigvector(s, &sv, NULL) == -1) + return (BADSIG); + return (osv.sv_handler); +} + +#if !defined(__hpux__) && !defined(__hpux) +int +utimes(char *file, struct timeval tvp[2]) +{ + struct utimbuf t; + + t.actime = tvp[0].tv_sec; + t.modtime = tvp[1].tv_sec; + return(utime(file, &t)); +} +#endif + +#if !defined(BSD) && !defined(d_fileno) +# define d_fileno d_ino +#endif + +#ifndef DEV_DEV_COMPARE +# define DEV_DEV_COMPARE(a, b) ((a) == (b)) +#endif +#define ISDOT(c) ((c)[0] == '.' && (((c)[1] == '\0') || ((c)[1] == '/'))) +#define ISDOTDOT(c) ((c)[0] == '.' && ISDOT(&((c)[1]))) + +char * +getwd(char *pathname) +{ + DIR *dp; + struct dirent *d; + extern int errno; + + struct stat st_root, st_cur, st_next, st_dotdot; + char pathbuf[MAXPATHLEN], nextpathbuf[MAXPATHLEN * 2]; + char *pathptr, *nextpathptr, *cur_name_add; + + /* find the inode of root */ + if (stat("/", &st_root) == -1) { + (void)sprintf(pathname, + "getwd: Cannot stat \"/\" (%s)", strerror(errno)); + return (NULL); + } + pathbuf[MAXPATHLEN - 1] = '\0'; + pathptr = &pathbuf[MAXPATHLEN - 1]; + nextpathbuf[MAXPATHLEN - 1] = '\0'; + cur_name_add = nextpathptr = &nextpathbuf[MAXPATHLEN - 1]; + + /* find the inode of the current directory */ + if (lstat(".", &st_cur) == -1) { + (void)sprintf(pathname, + "getwd: Cannot stat \".\" (%s)", strerror(errno)); + return (NULL); + } + nextpathptr = strrcpy(nextpathptr, "../"); + + /* Descend to root */ + for (;;) { + + /* look if we found root yet */ + if (st_cur.st_ino == st_root.st_ino && + DEV_DEV_COMPARE(st_cur.st_dev, st_root.st_dev)) { + (void)strcpy(pathname, *pathptr != '/' ? "/" : pathptr); + return (pathname); + } + + /* open the parent directory */ + if (stat(nextpathptr, &st_dotdot) == -1) { + (void)sprintf(pathname, + "getwd: Cannot stat directory \"%s\" (%s)", + nextpathptr, strerror(errno)); + return (NULL); + } + if ((dp = opendir(nextpathptr)) == NULL) { + (void)sprintf(pathname, + "getwd: Cannot open directory \"%s\" (%s)", + nextpathptr, strerror(errno)); + return (NULL); + } + + /* look in the parent for the entry with the same inode */ + if (DEV_DEV_COMPARE(st_dotdot.st_dev, st_cur.st_dev)) { + /* Parent has same device. No need to stat every member */ + for (d = readdir(dp); d != NULL; d = readdir(dp)) + if (d->d_fileno == st_cur.st_ino) + break; + } + else { + /* + * Parent has a different device. This is a mount point so we + * need to stat every member + */ + for (d = readdir(dp); d != NULL; d = readdir(dp)) { + if (ISDOT(d->d_name) || ISDOTDOT(d->d_name)) + continue; + (void)strcpy(cur_name_add, d->d_name); + if (lstat(nextpathptr, &st_next) == -1) { + (void)sprintf(pathname, + "getwd: Cannot stat \"%s\" (%s)", + d->d_name, strerror(errno)); + (void)closedir(dp); + return (NULL); + } + /* check if we found it yet */ + if (st_next.st_ino == st_cur.st_ino && + DEV_DEV_COMPARE(st_next.st_dev, st_cur.st_dev)) + break; + } + } + if (d == NULL) { + (void)sprintf(pathname, + "getwd: Cannot find \".\" in \"..\""); + (void)closedir(dp); + return (NULL); + } + st_cur = st_dotdot; + pathptr = strrcpy(pathptr, d->d_name); + pathptr = strrcpy(pathptr, "/"); + nextpathptr = strrcpy(nextpathptr, "../"); + (void)closedir(dp); + *cur_name_add = '\0'; + } +} /* end getwd */ + +#endif /* __hpux */ + +#if !defined(HAVE_GETCWD) +char * +getcwd(path, sz) + char *path; + int sz; +{ + return getwd(path); +} +#endif + +#if defined(sun) && defined(__svr4__) +#include + +/* turn into bsd signals */ +void (* +signal(int s, void (*a)(int)))(int) +{ + struct sigaction sa, osa; + + sa.sa_handler = a; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + + if (sigaction(s, &sa, &osa) == -1) + return SIG_ERR; + else + return osa.sa_handler; +} +#endif + +#if !defined(HAVE_VSNPRINTF) || !defined(HAVE_VASPRINTF) +#include +#endif + +#if !defined(HAVE_VSNPRINTF) +#if !defined(__osf__) +#ifdef _IOSTRG +#define STRFLAG (_IOSTRG|_IOWRT) /* no _IOWRT: avoid stdio bug */ +#else +#if 0 +#define STRFLAG (_IOREAD) /* XXX: Assume svr4 stdio */ +#endif +#endif /* _IOSTRG */ +#endif /* __osf__ */ + +int +vsnprintf(char *s, size_t n, const char *fmt, va_list args) +{ +#ifdef STRFLAG + FILE fakebuf; + + fakebuf._flag = STRFLAG; + /* + * Some os's are char * _ptr, others are unsigned char *_ptr... + * We cast to void * to make everyone happy. + */ + fakebuf._ptr = (void *)s; + fakebuf._cnt = n-1; + fakebuf._file = -1; + _doprnt(fmt, args, &fakebuf); + fakebuf._cnt++; + putc('\0', &fakebuf); + if (fakebuf._cnt<0) + fakebuf._cnt = 0; + return (n-fakebuf._cnt-1); +#else +#ifndef _PATH_DEVNULL +# define _PATH_DEVNULL "/dev/null" +#endif + /* + * Rats... we don't want to clobber anything... + * do a printf to /dev/null to see how much space we need. + */ + static FILE *nullfp; + int need = 0; /* XXX what's a useful error return? */ + + if (!nullfp) + nullfp = fopen(_PATH_DEVNULL, "w"); + if (nullfp) { + need = vfprintf(nullfp, fmt, args); + if (need < n) + (void)vsprintf(s, fmt, args); + } + return need; +#endif +} +#endif + +#if !defined(HAVE_SNPRINTF) +int +snprintf(char *s, size_t n, const char *fmt, ...) +{ + va_list ap; + int rv; + + va_start(ap, fmt); + rv = vsnprintf(s, n, fmt, ap); + va_end(ap); + return rv; +} +#endif + +#if !defined(HAVE_VASPRINTF) +int +vasprintf(char **s, const char *fmt, va_list ap) +{ + char buf[128]; + int rv; + + rv = vsnprintf(buf, sizeof(buf), fmt, ap); + if (rv < sizeof(buf)) + *s = strdup(buf); + else { + if ((*s = malloc(rv + 1))) + vsnprintf(*s, rv + 1, fmt, ap); + } + va_end(ap); + return *s ? rv : -1; +} +#endif + +#if !defined(HAVE_ASPRINTF) +int +asprintf(char **s, const char *fmt, ...) +{ + va_list ap; + int rv; + + va_start(ap, fmt); + rv = vasprintf(s, fmt, ap); + va_end(ap); + return rv; +} +#endif + +#if !defined(HAVE_STRFTIME) +size_t +strftime(char *buf, size_t len, const char *fmt, const struct tm *tm) +{ + static char months[][4] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + + size_t s; + char *b = buf; + + while (*fmt) { + if (len == 0) + return buf - b; + if (*fmt != '%') { + *buf++ = *fmt++; + len--; + continue; + } + switch (*fmt++) { + case '%': + *buf++ = '%'; + len--; + if (len == 0) return buf - b; + /*FALLTHROUGH*/ + case '\0': + *buf = '%'; + s = 1; + break; + case 'k': + s = snprintf(buf, len, "%d", tm->tm_hour); + break; + case 'M': + s = snprintf(buf, len, "%02d", tm->tm_min); + break; + case 'S': + s = snprintf(buf, len, "%02d", tm->tm_sec); + break; + case 'b': + if (tm->tm_mon >= 12) + return buf - b; + s = snprintf(buf, len, "%s", months[tm->tm_mon]); + break; + case 'd': + s = snprintf(buf, len, "%s", tm->tm_mday); + break; + case 'Y': + s = snprintf(buf, len, "%s", 1900 + tm->tm_year); + break; + default: + s = snprintf(buf, len, "Unsupported format %c", + fmt[-1]); + break; + } + buf += s; + len -= s; + } +} +#endif diff --git a/devel/bmake/files/var.c b/devel/bmake/files/var.c new file mode 100644 index 0000000000000..ead8dbaac5973 --- /dev/null +++ b/devel/bmake/files/var.c @@ -0,0 +1,3524 @@ +/* $NetBSD: var.c,v 1.1.1.1 2005/12/02 00:03:00 sjg 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: var.c,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 3/19/94"; +#else +__RCSID("$NetBSD: var.c,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * var.c -- + * Variable-handling functions + * + * Interface: + * Var_Set Set the value of a variable in the given + * context. The variable is created if it doesn't + * yet exist. The value and variable name need not + * be preserved. + * + * Var_Append Append more characters to an existing variable + * in the given context. The variable needn't + * exist already -- it will be created if it doesn't. + * A space is placed between the old value and the + * new one. + * + * Var_Exists See if a variable exists. + * + * Var_Value Return the value of a variable in a context or + * NULL if the variable is undefined. + * + * Var_Subst Substitute named variable, or all variables if + * NULL in a string using + * the given context as the top-most one. If the + * third argument is non-zero, Parse_Error is + * called if any variables are undefined. + * + * Var_Parse Parse a variable expansion from a string and + * return the result and the number of characters + * consumed. + * + * Var_Delete Delete a variable in a context. + * + * Var_Init Initialize this module. + * + * Debugging: + * Var_Dump Print out all variables defined in the given + * context. + * + * XXX: There's a lot of duplication in these functions. + */ + +#ifndef NO_REGEX +#include +#include +#endif +#include +#include +#include + +#include "make.h" +#include "buf.h" + +/* + * This is a harmless return value for Var_Parse that can be used by Var_Subst + * to determine if there was an error in parsing -- easier than returning + * a flag, as things outside this module don't give a hoot. + */ +char var_Error[] = ""; + +/* + * Similar to var_Error, but returned when the 'err' flag for Var_Parse is + * set false. Why not just use a constant? Well, gcc likes to condense + * identical string instances... + */ +static char varNoError[] = ""; + +/* + * Internally, variables are contained in four different contexts. + * 1) the environment. They may not be changed. If an environment + * variable is appended-to, the result is placed in the global + * context. + * 2) the global context. Variables set in the Makefile are located in + * the global context. It is the penultimate context searched when + * substituting. + * 3) the command-line context. All variables set on the command line + * are placed in this context. They are UNALTERABLE once placed here. + * 4) the local context. Each target has associated with it a context + * list. On this list are located the structures describing such + * local variables as $(@) and $(*) + * The four contexts are searched in the reverse order from which they are + * listed. + */ +GNode *VAR_GLOBAL; /* variables from the makefile */ +GNode *VAR_CMD; /* variables defined on the command-line */ + +#define FIND_CMD 0x1 /* look in VAR_CMD when searching */ +#define FIND_GLOBAL 0x2 /* look in VAR_GLOBAL as well */ +#define FIND_ENV 0x4 /* look in the environment also */ + +typedef struct Var { + char *name; /* the variable's name */ + Buffer val; /* its value */ + int flags; /* miscellaneous status flags */ +#define VAR_IN_USE 1 /* Variable's value currently being used. + * Used to avoid recursion */ +#define VAR_FROM_ENV 2 /* Variable comes from the environment */ +#define VAR_JUNK 4 /* Variable is a junk variable that + * should be destroyed when done with + * it. Used by Var_Parse for undefined, + * modified variables */ +#define VAR_KEEP 8 /* Variable is VAR_JUNK, but we found + * a use for it in some modifier and + * the value is therefore valid */ +} Var; + + +/* Var*Pattern flags */ +#define VAR_SUB_GLOBAL 0x01 /* Apply substitution globally */ +#define VAR_SUB_ONE 0x02 /* Apply substitution to one word */ +#define VAR_SUB_MATCHED 0x04 /* There was a match */ +#define VAR_MATCH_START 0x08 /* Match at start of word */ +#define VAR_MATCH_END 0x10 /* Match at end of word */ +#define VAR_NOSUBST 0x20 /* don't expand vars in VarGetPattern */ + +/* Var_Set flags */ +#define VAR_NO_EXPORT 0x01 /* do not export */ + +typedef struct { + /* + * The following fields are set by Var_Parse() when it + * encounters modifiers that need to keep state for use by + * subsequent modifiers within the same variable expansion. + */ + Byte varSpace; /* Word separator in expansions */ + Boolean oneBigWord; /* TRUE if we will treat the variable as a + * single big word, even if it contains + * embedded spaces (as opposed to the + * usual behaviour of treating it as + * several space-separated words). */ +} Var_Parse_State; + +/* struct passed as ClientData to VarSubstitute() for ":S/lhs/rhs/", + * to VarSYSVMatch() for ":lhs=rhs". */ +typedef struct { + const char *lhs; /* String to match */ + int leftLen; /* Length of string */ + const char *rhs; /* Replacement string (w/ &'s removed) */ + int rightLen; /* Length of replacement */ + int flags; +} VarPattern; + +/* struct passed as ClientData to VarLoopExpand() for ":@tvar@str@" */ +typedef struct { + GNode *ctxt; /* variable context */ + char *tvar; /* name of temp var */ + int tvarLen; + char *str; /* string to expand */ + int strLen; + int err; /* err for not defined */ +} VarLoop_t; + +#ifndef NO_REGEX +/* struct passed as ClientData to VarRESubstitute() for ":C///" */ +typedef struct { + regex_t re; + int nsub; + regmatch_t *matches; + char *replace; + int flags; +} VarREPattern; +#endif + +/* struct passed to VarSelectWords() for ":[start..end]" */ +typedef struct { + int start; /* first word to select */ + int end; /* last word to select */ +} VarSelectWords_t; + +static Var *VarFind(const char *, GNode *, int); +static void VarAdd(const char *, const char *, GNode *); +static Boolean VarHead(GNode *, Var_Parse_State *, + char *, Boolean, Buffer, ClientData); +static Boolean VarTail(GNode *, Var_Parse_State *, + char *, Boolean, Buffer, ClientData); +static Boolean VarSuffix(GNode *, Var_Parse_State *, + char *, Boolean, Buffer, ClientData); +static Boolean VarRoot(GNode *, Var_Parse_State *, + char *, Boolean, Buffer, ClientData); +static Boolean VarMatch(GNode *, Var_Parse_State *, + char *, Boolean, Buffer, ClientData); +#ifdef SYSVVARSUB +static Boolean VarSYSVMatch(GNode *, Var_Parse_State *, + char *, Boolean, Buffer, ClientData); +#endif +static Boolean VarNoMatch(GNode *, Var_Parse_State *, + char *, Boolean, Buffer, ClientData); +#ifndef NO_REGEX +static void VarREError(int, regex_t *, const char *); +static Boolean VarRESubstitute(GNode *, Var_Parse_State *, + char *, Boolean, Buffer, ClientData); +#endif +static Boolean VarSubstitute(GNode *, Var_Parse_State *, + char *, Boolean, Buffer, ClientData); +static Boolean VarLoopExpand(GNode *, Var_Parse_State *, + char *, Boolean, Buffer, ClientData); +static char *VarGetPattern(GNode *, Var_Parse_State *, + int, const char **, int, int *, int *, + VarPattern *); +static char *VarQuote(char *); +static char *VarChangeCase(char *, int); +static char *VarModify(GNode *, Var_Parse_State *, + const char *, + Boolean (*)(GNode *, Var_Parse_State *, char *, Boolean, Buffer, ClientData), + ClientData); +static char *VarOrder(const char *, const char); +static char *VarUniq(const char *); +static int VarWordCompare(const void *, const void *); +static void VarPrintVar(ClientData); + +#define WR(a) ((char *)UNCONST(a)) + +#define BROPEN '{' +#define BRCLOSE '}' +#define PROPEN '(' +#define PRCLOSE ')' + +/*- + *----------------------------------------------------------------------- + * VarFind -- + * Find the given variable in the given context and any other contexts + * indicated. + * + * Input: + * name name to find + * ctxt context in which to find it + * flags FIND_GLOBAL set means to look in the + * VAR_GLOBAL context as well. FIND_CMD set means + * to look in the VAR_CMD context also. FIND_ENV + * set means to look in the environment + * + * Results: + * A pointer to the structure describing the desired variable or + * NIL if the variable does not exist. + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +static Var * +VarFind(const char *name, GNode *ctxt, int flags) +{ + Hash_Entry *var; + Var *v; + + /* + * If the variable name begins with a '.', it could very well be one of + * the local ones. We check the name against all the local variables + * and substitute the short version in for 'name' if it matches one of + * them. + */ + if (*name == '.' && isupper((unsigned char) name[1])) + switch (name[1]) { + case 'A': + if (!strcmp(name, ".ALLSRC")) + name = ALLSRC; + if (!strcmp(name, ".ARCHIVE")) + name = ARCHIVE; + break; + case 'I': + if (!strcmp(name, ".IMPSRC")) + name = IMPSRC; + break; + case 'M': + if (!strcmp(name, ".MEMBER")) + name = MEMBER; + break; + case 'O': + if (!strcmp(name, ".OODATE")) + name = OODATE; + break; + case 'P': + if (!strcmp(name, ".PREFIX")) + name = PREFIX; + break; + case 'T': + if (!strcmp(name, ".TARGET")) + name = TARGET; + break; + } + /* + * First look for the variable in the given context. If it's not there, + * look for it in VAR_CMD, VAR_GLOBAL and the environment, in that order, + * depending on the FIND_* flags in 'flags' + */ + var = Hash_FindEntry(&ctxt->context, name); + + if ((var == NULL) && (flags & FIND_CMD) && (ctxt != VAR_CMD)) { + var = Hash_FindEntry(&VAR_CMD->context, name); + } + if (!checkEnvFirst && (var == NULL) && (flags & FIND_GLOBAL) && + (ctxt != VAR_GLOBAL)) + { + var = Hash_FindEntry(&VAR_GLOBAL->context, name); + } + if ((var == NULL) && (flags & FIND_ENV)) { + char *env; + + if ((env = getenv(name)) != NULL) { + int len; + + v = emalloc(sizeof(Var)); + v->name = estrdup(name); + + len = strlen(env); + + v->val = Buf_Init(len); + Buf_AddBytes(v->val, len, (Byte *)env); + + v->flags = VAR_FROM_ENV; + return (v); + } else if (checkEnvFirst && (flags & FIND_GLOBAL) && + (ctxt != VAR_GLOBAL)) + { + var = Hash_FindEntry(&VAR_GLOBAL->context, name); + if (var == NULL) { + return ((Var *)NIL); + } else { + return ((Var *)Hash_GetValue(var)); + } + } else { + return((Var *)NIL); + } + } else if (var == NULL) { + return ((Var *)NIL); + } else { + return ((Var *)Hash_GetValue(var)); + } +} + +/*- + *----------------------------------------------------------------------- + * VarAdd -- + * Add a new variable of name name and value val to the given context + * + * Input: + * name name of variable to add + * val value to set it to + * ctxt context in which to set it + * + * Results: + * None + * + * Side Effects: + * The new variable is placed at the front of the given context + * The name and val arguments are duplicated so they may + * safely be freed. + *----------------------------------------------------------------------- + */ +static void +VarAdd(const char *name, const char *val, GNode *ctxt) +{ + Var *v; + int len; + Hash_Entry *h; + + v = emalloc(sizeof(Var)); + + len = val ? strlen(val) : 0; + v->val = Buf_Init(len+1); + Buf_AddBytes(v->val, len, (const Byte *)val); + + v->flags = 0; + + h = Hash_CreateEntry(&ctxt->context, name, NULL); + Hash_SetValue(h, v); + v->name = h->name; + if (DEBUG(VAR)) { + printf("%s:%s = %s\n", ctxt->name, name, val); + } +} + +/*- + *----------------------------------------------------------------------- + * Var_Delete -- + * Remove a variable from a context. + * + * Results: + * None. + * + * Side Effects: + * The Var structure is removed and freed. + * + *----------------------------------------------------------------------- + */ +void +Var_Delete(const char *name, GNode *ctxt) +{ + Hash_Entry *ln; + + if (DEBUG(VAR)) { + printf("%s:delete %s\n", ctxt->name, name); + } + ln = Hash_FindEntry(&ctxt->context, name); + if (ln != NULL) { + Var *v; + + v = (Var *)Hash_GetValue(ln); + if (v->name != ln->name) + free(v->name); + Hash_DeleteEntry(&ctxt->context, ln); + Buf_Destroy(v->val, TRUE); + free(v); + } +} + +/*- + *----------------------------------------------------------------------- + * Var_Set -- + * Set the variable name to the value val in the given context. + * + * Input: + * name name of variable to set + * val value to give to the variable + * ctxt context in which to set it + * + * Results: + * None. + * + * Side Effects: + * If the variable doesn't yet exist, a new record is created for it. + * Else the old value is freed and the new one stuck in its place + * + * Notes: + * The variable is searched for only in its context before being + * created in that context. I.e. if the context is VAR_GLOBAL, + * only VAR_GLOBAL->context is searched. Likewise if it is VAR_CMD, only + * VAR_CMD->context is searched. This is done to avoid the literally + * thousands of unnecessary strcmp's that used to be done to + * set, say, $(@) or $(<). + *----------------------------------------------------------------------- + */ +void +Var_Set(const char *name, const char *val, GNode *ctxt, int flags) +{ + Var *v; + const char *cp = name; + + /* + * We only look for a variable in the given context since anything set + * here will override anything in a lower context, so there's not much + * point in searching them all just to save a bit of memory... + */ + if ((name = strchr(cp, '$'))) { + name = Var_Subst(NULL, cp, ctxt, 0); + } else + name = cp; + v = VarFind(name, ctxt, 0); + if (v == (Var *)NIL) { + VarAdd(name, val, ctxt); + } else { + Buf_Discard(v->val, Buf_Size(v->val)); + Buf_AddBytes(v->val, strlen(val), (const Byte *)val); + + if (DEBUG(VAR)) { + printf("%s:%s = %s\n", ctxt->name, name, val); + } + } + /* + * Any variables given on the command line are automatically exported + * to the environment (as per POSIX standard) + */ + if (ctxt == VAR_CMD && (flags & VAR_NO_EXPORT) == 0) { + + /* + * If requested, don't export these in the environment + * individually. We still put them in MAKEOVERRIDES so + * that the command-line settings continue to override + * Makefile settings. + */ + if (varNoExportEnv != TRUE) + setenv(name, val, 1); + + Var_Append(MAKEOVERRIDES, name, VAR_GLOBAL); + } + if (name != cp) + free(UNCONST(name)); +} + +/*- + *----------------------------------------------------------------------- + * Var_Append -- + * The variable of the given name has the given value appended to it in + * the given context. + * + * Input: + * name name of variable to modify + * val String to append to it + * ctxt Context in which this should occur + * + * Results: + * None + * + * Side Effects: + * If the variable doesn't exist, it is created. Else the strings + * are concatenated (with a space in between). + * + * Notes: + * Only if the variable is being sought in the global context is the + * environment searched. + * XXX: Knows its calling circumstances in that if called with ctxt + * an actual target, it will only search that context since only + * a local variable could be being appended to. This is actually + * a big win and must be tolerated. + *----------------------------------------------------------------------- + */ +void +Var_Append(const char *name, const char *val, GNode *ctxt) +{ + Var *v; + Hash_Entry *h; + const char *cp = name; + + if ((name = strchr(cp, '$'))) { + name = Var_Subst(NULL, cp, ctxt, 0); + } else + name = cp; + + v = VarFind(name, ctxt, (ctxt == VAR_GLOBAL) ? FIND_ENV : 0); + + if (v == (Var *)NIL) { + VarAdd(name, val, ctxt); + } else { + Buf_AddByte(v->val, (Byte)' '); + Buf_AddBytes(v->val, strlen(val), (const Byte *)val); + + if (DEBUG(VAR)) { + printf("%s:%s = %s\n", ctxt->name, name, + (char *)Buf_GetAll(v->val, NULL)); + } + + if (v->flags & VAR_FROM_ENV) { + /* + * If the original variable came from the environment, we + * have to install it in the global context (we could place + * it in the environment, but then we should provide a way to + * export other variables...) + */ + v->flags &= ~VAR_FROM_ENV; + h = Hash_CreateEntry(&ctxt->context, name, NULL); + Hash_SetValue(h, v); + } + } + if (name != cp) + free(UNCONST(name)); +} + +/*- + *----------------------------------------------------------------------- + * Var_Exists -- + * See if the given variable exists. + * + * Input: + * name Variable to find + * ctxt Context in which to start search + * + * Results: + * TRUE if it does, FALSE if it doesn't + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +Boolean +Var_Exists(const char *name, GNode *ctxt) +{ + Var *v; + + v = VarFind(name, ctxt, FIND_CMD|FIND_GLOBAL|FIND_ENV); + + if (v == (Var *)NIL) { + return(FALSE); + } else if (v->flags & VAR_FROM_ENV) { + free(v->name); + Buf_Destroy(v->val, TRUE); + free(v); + } + return(TRUE); +} + +/*- + *----------------------------------------------------------------------- + * Var_Value -- + * Return the value of the named variable in the given context + * + * Input: + * name name to find + * ctxt context in which to search for it + * + * Results: + * The value if the variable exists, NULL if it doesn't + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +char * +Var_Value(const char *name, GNode *ctxt, char **frp) +{ + Var *v; + + v = VarFind(name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); + *frp = NULL; + if (v != (Var *)NIL) { + char *p = ((char *)Buf_GetAll(v->val, NULL)); + if (v->flags & VAR_FROM_ENV) { + free(v->name); + Buf_Destroy(v->val, FALSE); + free(v); + *frp = p; + } + return p; + } else { + return (NULL); + } +} + +/*- + *----------------------------------------------------------------------- + * VarHead -- + * Remove the tail of the given word and place the result in the given + * buffer. + * + * Input: + * word Word to trim + * addSpace True if need to add a space to the buffer + * before sticking in the head + * buf Buffer in which to store it + * + * Results: + * TRUE if characters were added to the buffer (a space needs to be + * added to the buffer before the next word). + * + * Side Effects: + * The trimmed word is added to the buffer. + * + *----------------------------------------------------------------------- + */ +static Boolean +VarHead(GNode *ctx __unused, Var_Parse_State *vpstate, + char *word, Boolean addSpace, Buffer buf, + ClientData dummy) +{ + char *slash; + + slash = strrchr(word, '/'); + if (slash != NULL) { + if (addSpace && vpstate->varSpace) { + Buf_AddByte(buf, vpstate->varSpace); + } + *slash = '\0'; + Buf_AddBytes(buf, strlen(word), (Byte *)word); + *slash = '/'; + return (TRUE); + } else { + /* + * If no directory part, give . (q.v. the POSIX standard) + */ + if (addSpace && vpstate->varSpace) + Buf_AddByte(buf, vpstate->varSpace); + Buf_AddByte(buf, (Byte)'.'); + } + return(dummy ? TRUE : TRUE); +} + +/*- + *----------------------------------------------------------------------- + * VarTail -- + * Remove the head of the given word and place the result in the given + * buffer. + * + * Input: + * word Word to trim + * addSpace True if need to add a space to the buffer + * before adding the tail + * buf Buffer in which to store it + * + * Results: + * TRUE if characters were added to the buffer (a space needs to be + * added to the buffer before the next word). + * + * Side Effects: + * The trimmed word is added to the buffer. + * + *----------------------------------------------------------------------- + */ +static Boolean +VarTail(GNode *ctx __unused, Var_Parse_State *vpstate, + char *word, Boolean addSpace, Buffer buf, + ClientData dummy) +{ + char *slash; + + if (addSpace && vpstate->varSpace) { + Buf_AddByte(buf, vpstate->varSpace); + } + + slash = strrchr(word, '/'); + if (slash != NULL) { + *slash++ = '\0'; + Buf_AddBytes(buf, strlen(slash), (Byte *)slash); + slash[-1] = '/'; + } else { + Buf_AddBytes(buf, strlen(word), (Byte *)word); + } + return (dummy ? TRUE : TRUE); +} + +/*- + *----------------------------------------------------------------------- + * VarSuffix -- + * Place the suffix of the given word in the given buffer. + * + * Input: + * word Word to trim + * addSpace TRUE if need to add a space before placing the + * suffix in the buffer + * buf Buffer in which to store it + * + * Results: + * TRUE if characters were added to the buffer (a space needs to be + * added to the buffer before the next word). + * + * Side Effects: + * The suffix from the word is placed in the buffer. + * + *----------------------------------------------------------------------- + */ +static Boolean +VarSuffix(GNode *ctx __unused, Var_Parse_State *vpstate, + char *word, Boolean addSpace, Buffer buf, + ClientData dummy) +{ + char *dot; + + dot = strrchr(word, '.'); + if (dot != NULL) { + if (addSpace && vpstate->varSpace) { + Buf_AddByte(buf, vpstate->varSpace); + } + *dot++ = '\0'; + Buf_AddBytes(buf, strlen(dot), (Byte *)dot); + dot[-1] = '.'; + addSpace = TRUE; + } + return (dummy ? addSpace : addSpace); +} + +/*- + *----------------------------------------------------------------------- + * VarRoot -- + * Remove the suffix of the given word and place the result in the + * buffer. + * + * Input: + * word Word to trim + * addSpace TRUE if need to add a space to the buffer + * before placing the root in it + * buf Buffer in which to store it + * + * Results: + * TRUE if characters were added to the buffer (a space needs to be + * added to the buffer before the next word). + * + * Side Effects: + * The trimmed word is added to the buffer. + * + *----------------------------------------------------------------------- + */ +static Boolean +VarRoot(GNode *ctx __unused, Var_Parse_State *vpstate, + char *word, Boolean addSpace, Buffer buf, + ClientData dummy) +{ + char *dot; + + if (addSpace && vpstate->varSpace) { + Buf_AddByte(buf, vpstate->varSpace); + } + + dot = strrchr(word, '.'); + if (dot != NULL) { + *dot = '\0'; + Buf_AddBytes(buf, strlen(word), (Byte *)word); + *dot = '.'; + } else { + Buf_AddBytes(buf, strlen(word), (Byte *)word); + } + return (dummy ? TRUE : TRUE); +} + +/*- + *----------------------------------------------------------------------- + * VarMatch -- + * Place the word in the buffer if it matches the given pattern. + * Callback function for VarModify to implement the :M modifier. + * + * Input: + * word Word to examine + * addSpace TRUE if need to add a space to the buffer + * before adding the word, if it matches + * buf Buffer in which to store it + * pattern Pattern the word must match + * + * Results: + * TRUE if a space should be placed in the buffer before the next + * word. + * + * Side Effects: + * The word may be copied to the buffer. + * + *----------------------------------------------------------------------- + */ +static Boolean +VarMatch(GNode *ctx __unused, Var_Parse_State *vpstate, + char *word, Boolean addSpace, Buffer buf, + ClientData pattern) +{ + if (Str_Match(word, (char *)pattern)) { + if (addSpace && vpstate->varSpace) { + Buf_AddByte(buf, vpstate->varSpace); + } + addSpace = TRUE; + Buf_AddBytes(buf, strlen(word), (Byte *)word); + } + return(addSpace); +} + +#ifdef SYSVVARSUB +/*- + *----------------------------------------------------------------------- + * VarSYSVMatch -- + * Place the word in the buffer if it matches the given pattern. + * Callback function for VarModify to implement the System V % + * modifiers. + * + * Input: + * word Word to examine + * addSpace TRUE if need to add a space to the buffer + * before adding the word, if it matches + * buf Buffer in which to store it + * patp Pattern the word must match + * + * Results: + * TRUE if a space should be placed in the buffer before the next + * word. + * + * Side Effects: + * The word may be copied to the buffer. + * + *----------------------------------------------------------------------- + */ +static Boolean +VarSYSVMatch(GNode *ctx, Var_Parse_State *vpstate, + char *word, Boolean addSpace, Buffer buf, + ClientData patp) +{ + int len; + char *ptr; + VarPattern *pat = (VarPattern *)patp; + char *varexp; + + if (addSpace && vpstate->varSpace) + Buf_AddByte(buf, vpstate->varSpace); + + addSpace = TRUE; + + if ((ptr = Str_SYSVMatch(word, pat->lhs, &len)) != NULL) { + varexp = Var_Subst(NULL, pat->rhs, ctx, 0); + Str_SYSVSubst(buf, varexp, ptr, len); + free(varexp); + } else { + Buf_AddBytes(buf, strlen(word), (Byte *)word); + } + + return(addSpace); +} +#endif + + +/*- + *----------------------------------------------------------------------- + * VarNoMatch -- + * Place the word in the buffer if it doesn't match the given pattern. + * Callback function for VarModify to implement the :N modifier. + * + * Input: + * word Word to examine + * addSpace TRUE if need to add a space to the buffer + * before adding the word, if it matches + * buf Buffer in which to store it + * pattern Pattern the word must match + * + * Results: + * TRUE if a space should be placed in the buffer before the next + * word. + * + * Side Effects: + * The word may be copied to the buffer. + * + *----------------------------------------------------------------------- + */ +static Boolean +VarNoMatch(GNode *ctx __unused, Var_Parse_State *vpstate, + char *word, Boolean addSpace, Buffer buf, + ClientData pattern) +{ + if (!Str_Match(word, (char *)pattern)) { + if (addSpace && vpstate->varSpace) { + Buf_AddByte(buf, vpstate->varSpace); + } + addSpace = TRUE; + Buf_AddBytes(buf, strlen(word), (Byte *)word); + } + return(addSpace); +} + + +/*- + *----------------------------------------------------------------------- + * VarSubstitute -- + * Perform a string-substitution on the given word, placing the + * result in the passed buffer. + * + * Input: + * word Word to modify + * addSpace True if space should be added before + * other characters + * buf Buffer for result + * patternp Pattern for substitution + * + * Results: + * TRUE if a space is needed before more characters are added. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static Boolean +VarSubstitute(GNode *ctx __unused, Var_Parse_State *vpstate, + char *word, Boolean addSpace, Buffer buf, + ClientData patternp) +{ + int wordLen; /* Length of word */ + char *cp; /* General pointer */ + VarPattern *pattern = (VarPattern *)patternp; + + wordLen = strlen(word); + if ((pattern->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) != + (VAR_SUB_ONE|VAR_SUB_MATCHED)) { + /* + * Still substituting -- break it down into simple anchored cases + * and if none of them fits, perform the general substitution case. + */ + if ((pattern->flags & VAR_MATCH_START) && + (strncmp(word, pattern->lhs, pattern->leftLen) == 0)) { + /* + * Anchored at start and beginning of word matches pattern + */ + if ((pattern->flags & VAR_MATCH_END) && + (wordLen == pattern->leftLen)) { + /* + * Also anchored at end and matches to the end (word + * is same length as pattern) add space and rhs only + * if rhs is non-null. + */ + if (pattern->rightLen != 0) { + if (addSpace && vpstate->varSpace) { + Buf_AddByte(buf, vpstate->varSpace); + } + addSpace = TRUE; + Buf_AddBytes(buf, pattern->rightLen, + (const Byte *)pattern->rhs); + } + pattern->flags |= VAR_SUB_MATCHED; + } else if (pattern->flags & VAR_MATCH_END) { + /* + * Doesn't match to end -- copy word wholesale + */ + goto nosub; + } else { + /* + * Matches at start but need to copy in trailing characters + */ + if ((pattern->rightLen + wordLen - pattern->leftLen) != 0){ + if (addSpace && vpstate->varSpace) { + Buf_AddByte(buf, vpstate->varSpace); + } + addSpace = TRUE; + } + Buf_AddBytes(buf, pattern->rightLen, + (const Byte *)pattern->rhs); + Buf_AddBytes(buf, wordLen - pattern->leftLen, + (Byte *)(word + pattern->leftLen)); + pattern->flags |= VAR_SUB_MATCHED; + } + } else if (pattern->flags & VAR_MATCH_START) { + /* + * Had to match at start of word and didn't -- copy whole word. + */ + goto nosub; + } else if (pattern->flags & VAR_MATCH_END) { + /* + * Anchored at end, Find only place match could occur (leftLen + * characters from the end of the word) and see if it does. Note + * that because the $ will be left at the end of the lhs, we have + * to use strncmp. + */ + cp = word + (wordLen - pattern->leftLen); + if ((cp >= word) && + (strncmp(cp, pattern->lhs, pattern->leftLen) == 0)) { + /* + * Match found. If we will place characters in the buffer, + * add a space before hand as indicated by addSpace, then + * stuff in the initial, unmatched part of the word followed + * by the right-hand-side. + */ + if (((cp - word) + pattern->rightLen) != 0) { + if (addSpace && vpstate->varSpace) { + Buf_AddByte(buf, vpstate->varSpace); + } + addSpace = TRUE; + } + Buf_AddBytes(buf, cp - word, (const Byte *)word); + Buf_AddBytes(buf, pattern->rightLen, + (const Byte *)pattern->rhs); + pattern->flags |= VAR_SUB_MATCHED; + } else { + /* + * Had to match at end and didn't. Copy entire word. + */ + goto nosub; + } + } else { + /* + * Pattern is unanchored: search for the pattern in the word using + * String_FindSubstring, copying unmatched portions and the + * right-hand-side for each match found, handling non-global + * substitutions correctly, etc. When the loop is done, any + * remaining part of the word (word and wordLen are adjusted + * accordingly through the loop) is copied straight into the + * buffer. + * addSpace is set FALSE as soon as a space is added to the + * buffer. + */ + Boolean done; + int origSize; + + done = FALSE; + origSize = Buf_Size(buf); + while (!done) { + cp = Str_FindSubstring(word, pattern->lhs); + if (cp != NULL) { + if (addSpace && (((cp - word) + pattern->rightLen) != 0)){ + Buf_AddByte(buf, vpstate->varSpace); + addSpace = FALSE; + } + Buf_AddBytes(buf, cp-word, (const Byte *)word); + Buf_AddBytes(buf, pattern->rightLen, + (const Byte *)pattern->rhs); + wordLen -= (cp - word) + pattern->leftLen; + word = cp + pattern->leftLen; + if (wordLen == 0) { + done = TRUE; + } + if ((pattern->flags & VAR_SUB_GLOBAL) == 0) { + done = TRUE; + } + pattern->flags |= VAR_SUB_MATCHED; + } else { + done = TRUE; + } + } + if (wordLen != 0) { + if (addSpace && vpstate->varSpace) { + Buf_AddByte(buf, vpstate->varSpace); + } + Buf_AddBytes(buf, wordLen, (Byte *)word); + } + /* + * If added characters to the buffer, need to add a space + * before we add any more. If we didn't add any, just return + * the previous value of addSpace. + */ + return ((Buf_Size(buf) != origSize) || addSpace); + } + return (addSpace); + } + nosub: + if (addSpace && vpstate->varSpace) { + Buf_AddByte(buf, vpstate->varSpace); + } + Buf_AddBytes(buf, wordLen, (Byte *)word); + return(TRUE); +} + +#ifndef NO_REGEX +/*- + *----------------------------------------------------------------------- + * VarREError -- + * Print the error caused by a regcomp or regexec call. + * + * Results: + * None. + * + * Side Effects: + * An error gets printed. + * + *----------------------------------------------------------------------- + */ +static void +VarREError(int err, regex_t *pat, const char *str) +{ + char *errbuf; + int errlen; + + errlen = regerror(err, pat, 0, 0); + errbuf = emalloc(errlen); + regerror(err, pat, errbuf, errlen); + Error("%s: %s", str, errbuf); + free(errbuf); +} + + +/*- + *----------------------------------------------------------------------- + * VarRESubstitute -- + * Perform a regex substitution on the given word, placing the + * result in the passed buffer. + * + * Results: + * TRUE if a space is needed before more characters are added. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static Boolean +VarRESubstitute(GNode *ctx __unused, Var_Parse_State *vpstate __unused, + char *word, Boolean addSpace, Buffer buf, + ClientData patternp) +{ + VarREPattern *pat; + int xrv; + char *wp; + char *rp; + int added; + int flags = 0; + +#define MAYBE_ADD_SPACE() \ + if (addSpace && !added) \ + Buf_AddByte(buf, ' '); \ + added = 1 + + added = 0; + wp = word; + pat = patternp; + + if ((pat->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) == + (VAR_SUB_ONE|VAR_SUB_MATCHED)) + xrv = REG_NOMATCH; + else { + tryagain: + xrv = regexec(&pat->re, wp, pat->nsub, pat->matches, flags); + } + + switch (xrv) { + case 0: + pat->flags |= VAR_SUB_MATCHED; + if (pat->matches[0].rm_so > 0) { + MAYBE_ADD_SPACE(); + Buf_AddBytes(buf, pat->matches[0].rm_so, wp); + } + + for (rp = pat->replace; *rp; rp++) { + if ((*rp == '\\') && ((rp[1] == '&') || (rp[1] == '\\'))) { + MAYBE_ADD_SPACE(); + Buf_AddByte(buf,rp[1]); + rp++; + } + else if ((*rp == '&') || + ((*rp == '\\') && isdigit((unsigned char)rp[1]))) { + int n; + const char *subbuf; + int sublen; + char errstr[3]; + + if (*rp == '&') { + n = 0; + errstr[0] = '&'; + errstr[1] = '\0'; + } else { + n = rp[1] - '0'; + errstr[0] = '\\'; + errstr[1] = rp[1]; + errstr[2] = '\0'; + rp++; + } + + if (n > pat->nsub) { + Error("No subexpression %s", &errstr[0]); + subbuf = ""; + sublen = 0; + } else if ((pat->matches[n].rm_so == -1) && + (pat->matches[n].rm_eo == -1)) { + Error("No match for subexpression %s", &errstr[0]); + subbuf = ""; + sublen = 0; + } else { + subbuf = wp + pat->matches[n].rm_so; + sublen = pat->matches[n].rm_eo - pat->matches[n].rm_so; + } + + if (sublen > 0) { + MAYBE_ADD_SPACE(); + Buf_AddBytes(buf, sublen, subbuf); + } + } else { + MAYBE_ADD_SPACE(); + Buf_AddByte(buf, *rp); + } + } + wp += pat->matches[0].rm_eo; + if (pat->flags & VAR_SUB_GLOBAL) { + flags |= REG_NOTBOL; + if (pat->matches[0].rm_so == 0 && pat->matches[0].rm_eo == 0) { + MAYBE_ADD_SPACE(); + Buf_AddByte(buf, *wp); + wp++; + + } + if (*wp) + goto tryagain; + } + if (*wp) { + MAYBE_ADD_SPACE(); + Buf_AddBytes(buf, strlen(wp), wp); + } + break; + default: + VarREError(xrv, &pat->re, "Unexpected regex error"); + /* fall through */ + case REG_NOMATCH: + if (*wp) { + MAYBE_ADD_SPACE(); + Buf_AddBytes(buf,strlen(wp),wp); + } + break; + } + return(addSpace||added); +} +#endif + + + +/*- + *----------------------------------------------------------------------- + * VarLoopExpand -- + * Implements the :@@@ modifier of ODE make. + * We set the temp variable named in pattern.lhs to word and expand + * pattern.rhs storing the result in the passed buffer. + * + * Input: + * word Word to modify + * addSpace True if space should be added before + * other characters + * buf Buffer for result + * pattern Datafor substitution + * + * Results: + * TRUE if a space is needed before more characters are added. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static Boolean +VarLoopExpand(GNode *ctx __unused, Var_Parse_State *vpstate __unused, + char *word, Boolean addSpace, Buffer buf, + ClientData loopp) +{ + VarLoop_t *loop = (VarLoop_t *)loopp; + char *s; + int slen; + + if (word && *word) { + Var_Set(loop->tvar, word, loop->ctxt, VAR_NO_EXPORT); + s = Var_Subst(NULL, loop->str, loop->ctxt, loop->err); + if (s != NULL && *s != '\0') { + if (addSpace && *s != '\n') + Buf_AddByte(buf, ' '); + Buf_AddBytes(buf, (slen = strlen(s)), (Byte *)s); + addSpace = (slen > 0 && s[slen - 1] != '\n'); + free(s); + } + } + return addSpace; +} + + +/*- + *----------------------------------------------------------------------- + * VarSelectWords -- + * Implements the :[start..end] modifier. + * This is a special case of VarModify since we want to be able + * to scan the list backwards if start > end. + * + * Input: + * str String whose words should be trimmed + * seldata words to select + * + * Results: + * A string of all the words selected. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static char * +VarSelectWords(GNode *ctx __unused, Var_Parse_State *vpstate, + const char *str, VarSelectWords_t *seldata) +{ + Buffer buf; /* Buffer for the new string */ + Boolean addSpace; /* TRUE if need to add a space to the + * buffer before adding the trimmed + * word */ + char **av; /* word list */ + char *as; /* word list memory */ + int ac, i; + int start, end, step; + + buf = Buf_Init(0); + addSpace = FALSE; + + if (vpstate->oneBigWord) { + /* fake what brk_string() would do if there were only one word */ + ac = 1; + av = emalloc((ac + 1) * sizeof(char *)); + as = strdup(str); + av[0] = as; + av[1] = NULL; + } else { + av = brk_string(str, &ac, FALSE, &as); + } + + /* + * Now sanitize seldata. + * If seldata->start or seldata->end are negative, convert them to + * the positive equivalents (-1 gets converted to argc, -2 gets + * converted to (argc-1), etc.). + */ + if (seldata->start < 0) + seldata->start = ac + seldata->start + 1; + if (seldata->end < 0) + seldata->end = ac + seldata->end + 1; + + /* + * We avoid scanning more of the list than we need to. + */ + if (seldata->start > seldata->end) { + start = MIN(ac, seldata->start) - 1; + end = MAX(0, seldata->end - 1); + step = -1; + } else { + start = MAX(0, seldata->start - 1); + end = MIN(ac, seldata->end); + step = 1; + } + + for (i = start; + (step < 0 && i >= end) || (step > 0 && i < end); + i += step) { + if (av[i] && *av[i]) { + if (addSpace && vpstate->varSpace) { + Buf_AddByte(buf, vpstate->varSpace); + } + Buf_AddBytes(buf, strlen(av[i]), (Byte *)av[i]); + addSpace = TRUE; + } + } + + free(as); + free(av); + + Buf_AddByte(buf, '\0'); + as = (char *)Buf_GetAll(buf, NULL); + Buf_Destroy(buf, FALSE); + return (as); +} + +/*- + *----------------------------------------------------------------------- + * VarModify -- + * Modify each of the words of the passed string using the given + * function. Used to implement all modifiers. + * + * Input: + * str String whose words should be trimmed + * modProc Function to use to modify them + * datum Datum to pass it + * + * Results: + * A string of all the words modified appropriately. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static char * +VarModify(GNode *ctx, Var_Parse_State *vpstate, + const char *str, + Boolean (*modProc)(GNode *, Var_Parse_State *, char *, + Boolean, Buffer, ClientData), + ClientData datum) +{ + Buffer buf; /* Buffer for the new string */ + Boolean addSpace; /* TRUE if need to add a space to the + * buffer before adding the trimmed + * word */ + char **av; /* word list */ + char *as; /* word list memory */ + int ac, i; + + buf = Buf_Init(0); + addSpace = FALSE; + + if (vpstate->oneBigWord) { + /* fake what brk_string() would do if there were only one word */ + ac = 1; + av = emalloc((ac + 1) * sizeof(char *)); + as = strdup(str); + av[0] = as; + av[1] = NULL; + } else { + av = brk_string(str, &ac, FALSE, &as); + } + + for (i = 0; i < ac; i++) { + addSpace = (*modProc)(ctx, vpstate, av[i], addSpace, buf, datum); + } + + free(as); + free(av); + + Buf_AddByte(buf, '\0'); + as = (char *)Buf_GetAll(buf, NULL); + Buf_Destroy(buf, FALSE); + return (as); +} + + +static int +VarWordCompare(const void *a, const void *b) +{ + int r = strcmp(*(const char * const *)a, *(const char * const *)b); + return r; +} + +/*- + *----------------------------------------------------------------------- + * VarOrder -- + * Order the words in the string. + * + * Input: + * str String whose words should be sorted. + * otype How to order: s - sort, x - random. + * + * Results: + * A string containing the words ordered. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static char * +VarOrder(const char *str, const char otype) +{ + Buffer buf; /* Buffer for the new string */ + char **av; /* word list [first word does not count] */ + char *as; /* word list memory */ + int ac, i; + + buf = Buf_Init(0); + + av = brk_string(str, &ac, FALSE, &as); + + if (ac > 0) + switch (otype) { + case 's': /* sort alphabetically */ + qsort(av, ac, sizeof(char *), VarWordCompare); + break; + case 'x': /* randomize */ + { + int rndidx; + char *t; + + /* + * We will use [ac..2] range for mod factors. This will produce + * random numbers in [(ac-1)..0] interval, and minimal + * reasonable value for mod factor is 2 (the mod 1 will produce + * 0 with probability 1). + */ + for (i = ac-1; i > 0; i--) { + rndidx = random() % (i + 1); + if (i != rndidx) { + t = av[i]; + av[i] = av[rndidx]; + av[rndidx] = t; + } + } + } + } /* end of switch */ + + for (i = 0; i < ac; i++) { + Buf_AddBytes(buf, strlen(av[i]), (Byte *)av[i]); + if (i != ac - 1) + Buf_AddByte(buf, ' '); + } + + free(as); + free(av); + + Buf_AddByte(buf, '\0'); + as = (char *)Buf_GetAll(buf, NULL); + Buf_Destroy(buf, FALSE); + return (as); +} + + +/*- + *----------------------------------------------------------------------- + * VarUniq -- + * Remove adjacent duplicate words. + * + * Input: + * str String whose words should be sorted + * + * Results: + * A string containing the resulting words. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static char * +VarUniq(const char *str) +{ + Buffer buf; /* Buffer for new string */ + char **av; /* List of words to affect */ + char *as; /* Word list memory */ + int ac, i, j; + + buf = Buf_Init(0); + av = brk_string(str, &ac, FALSE, &as); + + if (ac > 1) { + for (j = 0, i = 1; i < ac; i++) + if (strcmp(av[i], av[j]) != 0 && (++j != i)) + av[j] = av[i]; + ac = j + 1; + } + + for (i = 0; i < ac; i++) { + Buf_AddBytes(buf, strlen(av[i]), (Byte *)av[i]); + if (i != ac - 1) + Buf_AddByte(buf, ' '); + } + + free(as); + free(av); + + Buf_AddByte(buf, '\0'); + as = (char *)Buf_GetAll(buf, NULL); + Buf_Destroy(buf, FALSE); + return as; +} + + +/*- + *----------------------------------------------------------------------- + * VarGetPattern -- + * Pass through the tstr looking for 1) escaped delimiters, + * '$'s and backslashes (place the escaped character in + * uninterpreted) and 2) unescaped $'s that aren't before + * the delimiter (expand the variable substitution unless flags + * has VAR_NOSUBST set). + * Return the expanded string or NULL if the delimiter was missing + * If pattern is specified, handle escaped ampersands, and replace + * unescaped ampersands with the lhs of the pattern. + * + * Results: + * A string of all the words modified appropriately. + * If length is specified, return the string length of the buffer + * If flags is specified and the last character of the pattern is a + * $ set the VAR_MATCH_END bit of flags. + * + * Side Effects: + * None. + *----------------------------------------------------------------------- + */ +static char * +VarGetPattern(GNode *ctxt, Var_Parse_State *vpstate __unused, + int err, const char **tstr, int delim, int *flags, + int *length, VarPattern *pattern) +{ + const char *cp; + Buffer buf = Buf_Init(0); + int junk; + if (length == NULL) + length = &junk; + +#define IS_A_MATCH(cp, delim) \ + ((cp[0] == '\\') && ((cp[1] == delim) || \ + (cp[1] == '\\') || (cp[1] == '$') || (pattern && (cp[1] == '&')))) + + /* + * Skim through until the matching delimiter is found; + * pick up variable substitutions on the way. Also allow + * backslashes to quote the delimiter, $, and \, but don't + * touch other backslashes. + */ + for (cp = *tstr; *cp && (*cp != delim); cp++) { + if (IS_A_MATCH(cp, delim)) { + Buf_AddByte(buf, (Byte)cp[1]); + cp++; + } else if (*cp == '$') { + if (cp[1] == delim) { + if (flags == NULL) + Buf_AddByte(buf, (Byte)*cp); + else + /* + * Unescaped $ at end of pattern => anchor + * pattern at end. + */ + *flags |= VAR_MATCH_END; + } else { + if (flags == NULL || (*flags & VAR_NOSUBST) == 0) { + char *cp2; + int len; + Boolean freeIt; + + /* + * If unescaped dollar sign not before the + * delimiter, assume it's a variable + * substitution and recurse. + */ + cp2 = Var_Parse(cp, ctxt, err, &len, &freeIt); + Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2); + if (freeIt) + free(cp2); + cp += len - 1; + } else { + const char *cp2 = &cp[1]; + + if (*cp2 == PROPEN || *cp2 == BROPEN) { + /* + * Find the end of this variable reference + * and suck it in without further ado. + * It will be interperated later. + */ + int have = *cp2; + int want = (*cp2 == PROPEN) ? PRCLOSE : BRCLOSE; + int depth = 1; + + for (++cp2; *cp2 != '\0' && depth > 0; ++cp2) { + if (cp2[-1] != '\\') { + if (*cp2 == have) + ++depth; + if (*cp2 == want) + --depth; + } + } + Buf_AddBytes(buf, cp2 - cp, (const Byte *)cp); + cp = --cp2; + } else + Buf_AddByte(buf, (Byte)*cp); + } + } + } + else if (pattern && *cp == '&') + Buf_AddBytes(buf, pattern->leftLen, (const Byte *)pattern->lhs); + else + Buf_AddByte(buf, (Byte)*cp); + } + + Buf_AddByte(buf, (Byte)'\0'); + + if (*cp != delim) { + *tstr = cp; + *length = 0; + return NULL; + } + else { + char *rstr; + *tstr = ++cp; + rstr = (char *)Buf_GetAll(buf, length); + *length -= 1; /* Don't count the NULL */ + Buf_Destroy(buf, FALSE); + return rstr; + } +} + +/*- + *----------------------------------------------------------------------- + * VarQuote -- + * Quote shell meta-characters in the string + * + * Results: + * The quoted string + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static char * +VarQuote(char *str) +{ + + Buffer buf; + /* This should cover most shells :-( */ + static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~"; + + buf = Buf_Init(MAKE_BSIZE); + for (; *str; str++) { + if (strchr(meta, *str) != NULL) + Buf_AddByte(buf, (Byte)'\\'); + Buf_AddByte(buf, (Byte)*str); + } + Buf_AddByte(buf, (Byte)'\0'); + str = (char *)Buf_GetAll(buf, NULL); + Buf_Destroy(buf, FALSE); + return str; +} + +/*- + *----------------------------------------------------------------------- + * VarChangeCase -- + * Change the string to all uppercase or all lowercase + * + * Input: + * str String to modify + * upper TRUE -> uppercase, else lowercase + * + * Results: + * The string with case changed + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static char * +VarChangeCase(char *str, int upper) +{ + Buffer buf; + int (*modProc)(int); + + modProc = (upper ? toupper : tolower); + buf = Buf_Init(MAKE_BSIZE); + for (; *str ; str++) { + Buf_AddByte(buf, (Byte)modProc(*str)); + } + Buf_AddByte(buf, (Byte)'\0'); + str = (char *)Buf_GetAll(buf, NULL); + Buf_Destroy(buf, FALSE); + return str; +} + +/*- + *----------------------------------------------------------------------- + * Var_Parse -- + * Given the start of a variable invocation, extract the variable + * name and find its value, then modify it according to the + * specification. + * + * Input: + * str The string to parse + * ctxt The context for the variable + * err TRUE if undefined variables are an error + * lengthPtr OUT: The length of the specification + * freePtr OUT: TRUE if caller should free result + * + * Results: + * The (possibly-modified) value of the variable or var_Error if the + * specification is invalid. The length of the specification is + * placed in *lengthPtr (for invalid specifications, this is just + * 2...?). + * A Boolean in *freePtr telling whether the returned string should + * be freed by the caller. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +char * +Var_Parse(const char *str, GNode *ctxt, Boolean err, int *lengthPtr, + Boolean *freePtr) +{ + const char *tstr; /* Pointer into str */ + Var *v; /* Variable in invocation */ + const char *cp; /* Secondary pointer into str (place marker + * for tstr) */ + Boolean haveModifier;/* TRUE if have modifiers for the variable */ + char endc; /* Ending character when variable in parens + * or braces */ + char startc=0; /* Starting character when variable in parens + * or braces */ + int cnt; /* Used to count brace pairs when variable in + * in parens or braces */ + int vlen; /* Length of variable name */ + const char *start; + char delim; + char *nstr; + Boolean dynamic; /* TRUE if the variable is local and we're + * expanding it in a non-local context. This + * is done to support dynamic sources. The + * result is just the invocation, unaltered */ + Var_Parse_State parsestate; /* Flags passed to helper functions */ + + *freePtr = FALSE; + dynamic = FALSE; + start = str; + parsestate.oneBigWord = FALSE; + parsestate.varSpace = ' '; /* word separator */ + + if (str[1] != PROPEN && str[1] != BROPEN) { + /* + * If it's not bounded by braces of some sort, life is much simpler. + * We just need to check for the first character and return the + * value if it exists. + */ + char name[2]; + + name[0] = str[1]; + name[1] = '\0'; + + v = VarFind(name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); + if (v == (Var *)NIL) { + *lengthPtr = 2; + + if ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)) { + /* + * If substituting a local variable in a non-local context, + * assume it's for dynamic source stuff. We have to handle + * this specially and return the longhand for the variable + * with the dollar sign escaped so it makes it back to the + * caller. Only four of the local variables are treated + * specially as they are the only four that will be set + * when dynamic sources are expanded. + */ + switch (str[1]) { + case '@': + return UNCONST("$(.TARGET)"); + case '%': + return UNCONST("$(.ARCHIVE)"); + case '*': + return UNCONST("$(.PREFIX)"); + case '!': + return UNCONST("$(.MEMBER)"); + } + } + /* + * Error + */ + return (err ? var_Error : varNoError); + } else { + haveModifier = FALSE; + tstr = &str[1]; + endc = str[1]; + } + } else if (str[1] == '\0') { + *lengthPtr = 1; + return (err ? var_Error : varNoError); + } else { + Buffer buf; /* Holds the variable name */ + + startc = str[1]; + endc = startc == PROPEN ? PRCLOSE : BRCLOSE; + buf = Buf_Init(MAKE_BSIZE); + + /* + * Skip to the end character or a colon, whichever comes first. + */ + for (tstr = str + 2; + *tstr != '\0' && *tstr != endc && *tstr != ':'; + tstr++) + { + /* + * A variable inside a variable, expand + */ + if (*tstr == '$') { + int rlen; + Boolean rfree; + char *rval = Var_Parse(tstr, ctxt, err, &rlen, &rfree); + if (rval != NULL) { + Buf_AddBytes(buf, strlen(rval), (Byte *)rval); + if (rfree) + free(rval); + } + tstr += rlen - 1; + } + else + Buf_AddByte(buf, (Byte)*tstr); + } + if (*tstr == ':') { + haveModifier = TRUE; + } else if (*tstr != '\0') { + haveModifier = FALSE; + } else { + /* + * If we never did find the end character, return NULL + * right now, setting the length to be the distance to + * the end of the string, since that's what make does. + */ + *lengthPtr = tstr - str; + return (var_Error); + } + *WR(tstr) = '\0'; + Buf_AddByte(buf, (Byte)'\0'); + str = Buf_GetAll(buf, NULL); + vlen = strlen(str); + + v = VarFind(str, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); + if ((v == (Var *)NIL) && (ctxt != VAR_CMD) && (ctxt != VAR_GLOBAL) && + (vlen == 2) && (str[1] == 'F' || str[1] == 'D')) + { + /* + * Check for bogus D and F forms of local variables since we're + * in a local context and the name is the right length. + */ + switch(*str) { + case '@': + case '%': + case '*': + case '!': + case '>': + case '<': + { + char vname[2]; + char *val; + + /* + * Well, it's local -- go look for it. + */ + vname[0] = *str; + vname[1] = '\0'; + v = VarFind(vname, ctxt, 0); + + if (v != (Var *)NIL) { + /* + * No need for nested expansion or anything, as we're + * the only one who sets these things and we sure don't + * but nested invocations in them... + */ + val = (char *)Buf_GetAll(v->val, NULL); + + if (str[1] == 'D') { + val = VarModify(ctxt, &parsestate, val, VarHead, + (ClientData)0); + } else { + val = VarModify(ctxt, &parsestate, val, VarTail, + (ClientData)0); + } + /* + * Resulting string is dynamically allocated, so + * tell caller to free it. + */ + *freePtr = TRUE; + *lengthPtr = tstr-start+1; + *WR(tstr) = endc; + Buf_Destroy(buf, TRUE); + return(val); + } + break; + } + } + } + + if (v == (Var *)NIL) { + if (((vlen == 1) || + (((vlen == 2) && (str[1] == 'F' || + str[1] == 'D')))) && + ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL))) + { + /* + * If substituting a local variable in a non-local context, + * assume it's for dynamic source stuff. We have to handle + * this specially and return the longhand for the variable + * with the dollar sign escaped so it makes it back to the + * caller. Only four of the local variables are treated + * specially as they are the only four that will be set + * when dynamic sources are expanded. + */ + switch (*str) { + case '@': + case '%': + case '*': + case '!': + dynamic = TRUE; + break; + } + } else if ((vlen > 2) && (*str == '.') && + isupper((unsigned char) str[1]) && + ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL))) + { + int len; + + len = vlen - 1; + if ((strncmp(str, ".TARGET", len) == 0) || + (strncmp(str, ".ARCHIVE", len) == 0) || + (strncmp(str, ".PREFIX", len) == 0) || + (strncmp(str, ".MEMBER", len) == 0)) + { + dynamic = TRUE; + } + } + + if (!haveModifier) { + /* + * No modifiers -- have specification length so we can return + * now. + */ + *lengthPtr = tstr - start + 1; + *WR(tstr) = endc; + if (dynamic) { + char *pstr = emalloc(*lengthPtr + 1); + strncpy(pstr, start, *lengthPtr); + pstr[*lengthPtr] = '\0'; + *freePtr = TRUE; + Buf_Destroy(buf, TRUE); + return(pstr); + } else { + Buf_Destroy(buf, TRUE); + return (err ? var_Error : varNoError); + } + } else { + /* + * Still need to get to the end of the variable specification, + * so kludge up a Var structure for the modifications + */ + v = emalloc(sizeof(Var)); + v->name = UNCONST(str); + v->val = Buf_Init(1); + v->flags = VAR_JUNK; + Buf_Destroy(buf, FALSE); + } + } else + Buf_Destroy(buf, TRUE); + } + + + if (v->flags & VAR_IN_USE) { + Fatal("Variable %s is recursive.", v->name); + /*NOTREACHED*/ + } else { + v->flags |= VAR_IN_USE; + } + /* + * Before doing any modification, we have to make sure the value + * has been fully expanded. If it looks like recursion might be + * necessary (there's a dollar sign somewhere in the variable's value) + * we just call Var_Subst to do any other substitutions that are + * necessary. Note that the value returned by Var_Subst will have + * been dynamically-allocated, so it will need freeing when we + * return. + */ + nstr = (char *)Buf_GetAll(v->val, NULL); + if (strchr(nstr, '$') != NULL) { + nstr = Var_Subst(NULL, nstr, ctxt, err); + *freePtr = TRUE; + } + + v->flags &= ~VAR_IN_USE; + + /* + * Now we need to apply any modifiers the user wants applied. + * These are: + * :M words which match the given . + * is of the standard file + * wildcarding form. + * :N words which do not match the given . + * :S[1gW] + * Substitute for in the value + * :C[1gW] + * Substitute for regex in the value + * :H Substitute the head of each word + * :T Substitute the tail of each word + * :E Substitute the extension (minus '.') of + * each word + * :R Substitute the root of each word + * (pathname minus the suffix). + * :O ("Order") Alphabeticaly sort words in variable. + * :Ox ("intermiX") Randomize words in variable. + * :u ("uniq") Remove adjacent duplicate words. + * :tu Converts the variable contents to uppercase. + * :tl Converts the variable contents to lowercase. + * :ts[c] Sets varSpace - the char used to + * separate words to 'c'. If 'c' is + * omitted then no separation is used. + * :tW Treat the variable contents as a single + * word, even if it contains spaces. + * (Mnemonic: one big 'W'ord.) + * :tw Treat the variable contents as multiple + * space-separated words. + * (Mnemonic: many small 'w'ords.) + * :[index] Select a single word from the value. + * :[start..end] Select multiple words from the value. + * :[*] or :[0] Select the entire value, as a single + * word. Equivalent to :tW. + * :[@] Select the entire value, as multiple + * words. Undoes the effect of :[*]. + * Equivalent to :tw. + * :[#] Returns the number of words in the value. + * + * :?: + * If the variable evaluates to true, return + * true value, else return the second value. + * :lhs=rhs Like :S, but the rhs goes to the end of + * the invocation. + * :sh Treat the current value as a command + * to be run, new value is its output. + * The following added so we can handle ODE makefiles. + * :@@@ + * Assign a temporary local variable + * to the current value of each word in turn + * and replace each word with the result of + * evaluating + * :D Use as value if variable defined + * :U Use as value if variable undefined + * :L Use the name of the variable as the value. + * :P Use the path of the node that has the same + * name as the variable as the value. This + * basically includes an implied :L so that + * the common method of refering to the path + * of your dependent 'x' in a rule is to use + * the form '${x:P}'. + * :!! Run cmd much the same as :sh run's the + * current value of the variable. + * The ::= modifiers, actually assign a value to the variable. + * Their main purpose is in supporting modifiers of .for loop + * iterators and other obscure uses. They always expand to + * nothing. In a target rule that would otherwise expand to an + * empty line they can be preceded with @: to keep make happy. + * Eg. + * + * foo: .USE + * .for i in ${.TARGET} ${.TARGET:R}.gz + * @: ${t::=$i} + * @echo blah ${t:T} + * .endfor + * + * ::= Assigns as the new value of variable. + * ::?= Assigns as value of variable if + * it was not already set. + * ::+= Appends to variable. + * ::!= Assigns output of as the new value of + * variable. + */ + if ((nstr != NULL) && haveModifier) { + /* + * Skip initial colon while putting it back. + */ + *WR(tstr) = ':'; + tstr++; + delim = '\0'; + + while (*tstr && *tstr != endc) { + char *newStr; /* New value to return */ + char termc; /* Character which terminated scan */ + + if (DEBUG(VAR)) { + printf("Applying :%c to \"%s\"\n", *tstr, nstr); + } + newStr = var_Error; + switch (*tstr) { + case ':': + { + if (tstr[1] == '=' || + (tstr[2] == '=' && + (tstr[1] == '!' || tstr[1] == '+' || tstr[1] == '?'))) { + /* + * "::=", "::!=", "::+=", or "::?=" + */ + GNode *v_ctxt; /* context where v belongs */ + const char *emsg; + char *sv_name; + VarPattern pattern; + int how; + + v_ctxt = ctxt; + sv_name = NULL; + ++tstr; + if (v->flags & VAR_JUNK) { + /* + * We need to strdup() it incase + * VarGetPattern() recurses. + */ + sv_name = v->name; + v->name = strdup(v->name); + } else if (ctxt != VAR_GLOBAL) { + if (VarFind(v->name, ctxt, 0) == (Var *)NIL) + v_ctxt = VAR_GLOBAL; + } + + switch ((how = *tstr)) { + case '+': + case '?': + case '!': + cp = &tstr[2]; + break; + default: + cp = ++tstr; + break; + } + delim = BRCLOSE; + pattern.flags = 0; + + pattern.rhs = VarGetPattern(ctxt, &parsestate, err, + &cp, delim, NULL, + &pattern.rightLen, + NULL); + if (v->flags & VAR_JUNK) { + /* restore original name */ + free(v->name); + v->name = sv_name; + } + if (pattern.rhs == NULL) + goto cleanup; + + termc = *--cp; + delim = '\0'; + + switch (how) { + case '+': + Var_Append(v->name, pattern.rhs, v_ctxt); + break; + case '!': + newStr = Cmd_Exec(pattern.rhs, &emsg); + if (emsg) + Error(emsg, nstr); + else + Var_Set(v->name, newStr, v_ctxt, 0); + if (newStr) + free(newStr); + break; + case '?': + if ((v->flags & VAR_JUNK) == 0) + break; + /* FALLTHROUGH */ + default: + Var_Set(v->name, pattern.rhs, v_ctxt, 0); + break; + } + free(UNCONST(pattern.rhs)); + newStr = var_Error; + break; + } + goto default_case; /* "::" */ + } + case '@': + { + VarLoop_t loop; + int flags = VAR_NOSUBST; + + cp = ++tstr; + delim = '@'; + if ((loop.tvar = VarGetPattern(ctxt, &parsestate, err, + &cp, delim, + &flags, &loop.tvarLen, + NULL)) == NULL) + goto cleanup; + + if ((loop.str = VarGetPattern(ctxt, &parsestate, err, + &cp, delim, + &flags, &loop.strLen, + NULL)) == NULL) + goto cleanup; + + termc = *cp; + delim = '\0'; + + loop.err = err; + loop.ctxt = ctxt; + newStr = VarModify(ctxt, &parsestate, nstr, VarLoopExpand, + (ClientData)&loop); + free(loop.tvar); + free(loop.str); + break; + } + case 'D': + case 'U': + { + Buffer buf; /* Buffer for patterns */ + int wantit; /* want data in buffer */ + + /* + * Pass through tstr looking for 1) escaped delimiters, + * '$'s and backslashes (place the escaped character in + * uninterpreted) and 2) unescaped $'s that aren't before + * the delimiter (expand the variable substitution). + * The result is left in the Buffer buf. + */ + buf = Buf_Init(0); + for (cp = tstr + 1; + *cp != endc && *cp != ':' && *cp != '\0'; + cp++) { + if ((*cp == '\\') && + ((cp[1] == ':') || + (cp[1] == '$') || + (cp[1] == endc) || + (cp[1] == '\\'))) + { + Buf_AddByte(buf, (Byte)cp[1]); + cp++; + } else if (*cp == '$') { + /* + * If unescaped dollar sign, assume it's a + * variable substitution and recurse. + */ + char *cp2; + int len; + Boolean freeIt; + + cp2 = Var_Parse(cp, ctxt, err, &len, &freeIt); + Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2); + if (freeIt) + free(cp2); + cp += len - 1; + } else { + Buf_AddByte(buf, (Byte)*cp); + } + } + Buf_AddByte(buf, (Byte)'\0'); + + termc = *cp; + + if (*tstr == 'U') + wantit = ((v->flags & VAR_JUNK) != 0); + else + wantit = ((v->flags & VAR_JUNK) == 0); + if ((v->flags & VAR_JUNK) != 0) + v->flags |= VAR_KEEP; + if (wantit) { + newStr = (char *)Buf_GetAll(buf, NULL); + Buf_Destroy(buf, FALSE); + } else { + newStr = nstr; + Buf_Destroy(buf, TRUE); + } + break; + } + case 'L': + { + if ((v->flags & VAR_JUNK) != 0) + v->flags |= VAR_KEEP; + newStr = strdup(v->name); + cp = ++tstr; + termc = *tstr; + break; + } + case 'P': + { + GNode *gn; + + if ((v->flags & VAR_JUNK) != 0) + v->flags |= VAR_KEEP; + gn = Targ_FindNode(v->name, TARG_NOCREATE); + if (gn == NILGNODE || gn->path == NULL) + newStr = strdup(v->name); + else + newStr = strdup(gn->path); + cp = ++tstr; + termc = *tstr; + break; + } + case '!': + { + const char *emsg; + VarPattern pattern; + pattern.flags = 0; + + delim = '!'; + + cp = ++tstr; + if ((pattern.rhs = VarGetPattern(ctxt, &parsestate, err, + &cp, delim, + NULL, &pattern.rightLen, + NULL)) == NULL) + goto cleanup; + newStr = Cmd_Exec(pattern.rhs, &emsg); + free(UNCONST(pattern.rhs)); + if (emsg) + Error(emsg, nstr); + termc = *cp; + delim = '\0'; + if (v->flags & VAR_JUNK) { + v->flags |= VAR_KEEP; + } + break; + } + case '[': + { + /* + * Look for the closing ']', recursively + * expanding any embedded variables. + * + * estr is a pointer to the expanded result, + * which we must free(). + */ + char *estr; + + cp = tstr+1; /* point to char after '[' */ + delim = ']'; /* look for closing ']' */ + estr = VarGetPattern(ctxt, &parsestate, + err, &cp, delim, + NULL, NULL, NULL); + if (estr == NULL) + goto cleanup; /* report missing ']' */ + /* now cp points just after the closing ']' */ + delim = '\0'; + if (cp[0] != ':' && cp[0] != endc) { + /* Found junk after ']' */ + free(estr); + goto bad_modifier; + } + if (estr[0] == '\0') { + /* Found empty square brackets in ":[]". */ + free(estr); + goto bad_modifier; + } else if (estr[0] == '#' && estr[1] == '\0') { + /* Found ":[#]" */ + + /* + * We will need enough space for the decimal + * representation of an int. We calculate the + * space needed for the octal representation, + * and add enough slop to cope with a '-' sign + * (which should never be needed) and a '\0' + * string terminator. + */ + int newStrSize = + (sizeof(int) * CHAR_BIT + 2) / 3 + 2; + + newStr = emalloc(newStrSize); + if (parsestate.oneBigWord) { + strncpy(newStr, "1", newStrSize); + } else { + /* XXX: brk_string() is a rather expensive + * way of counting words. */ + char **av; + char *as; + int ac; + + av = brk_string(nstr, &ac, FALSE, &as); + snprintf(newStr, newStrSize, "%d", ac); + free(as); + free(av); + } + termc = *cp; + free(estr); + break; + } else if (estr[0] == '*' && estr[1] == '\0') { + /* Found ":[*]" */ + parsestate.oneBigWord = TRUE; + newStr = nstr; + termc = *cp; + free(estr); + break; + } else if (estr[0] == '@' && estr[1] == '\0') { + /* Found ":[@]" */ + parsestate.oneBigWord = FALSE; + newStr = nstr; + termc = *cp; + free(estr); + break; + } else { + /* + * We expect estr to contain a single + * integer for :[N], or two integers + * separated by ".." for :[start..end]. + */ + char *ep; + + VarSelectWords_t seldata = { 0, 0 }; + + seldata.start = strtol(estr, &ep, 0); + if (ep == estr) { + /* Found junk instead of a number */ + free(estr); + goto bad_modifier; + } else if (ep[0] == '\0') { + /* Found only one integer in :[N] */ + seldata.end = seldata.start; + } else if (ep[0] == '.' && ep[1] == '.' && + ep[2] != '\0') { + /* Expecting another integer after ".." */ + ep += 2; + seldata.end = strtol(ep, &ep, 0); + if (ep[0] != '\0') { + /* Found junk after ".." */ + free(estr); + goto bad_modifier; + } + } else { + /* Found junk instead of ".." */ + free(estr); + goto bad_modifier; + } + /* + * Now seldata is properly filled in, + * but we still have to check for 0 as + * a special case. + */ + if (seldata.start == 0 && seldata.end == 0) { + /* ":[0]" or perhaps ":[0..0]" */ + parsestate.oneBigWord = TRUE; + newStr = nstr; + termc = *cp; + free(estr); + break; + } else if (seldata.start == 0 || + seldata.end == 0) { + /* ":[0..N]" or ":[N..0]" */ + free(estr); + goto bad_modifier; + } + /* + * Normal case: select the words + * described by seldata. + */ + newStr = VarSelectWords(ctxt, &parsestate, + nstr, &seldata); + + termc = *cp; + free(estr); + break; + } + + } + case 't': + { + cp = tstr + 1; /* make sure it is set */ + if (tstr[1] != endc && tstr[1] != ':') { + if (tstr[1] == 's') { + /* + * Use the char (if any) at tstr[2] + * as the word separator. + */ + VarPattern pattern; + + if (tstr[2] != endc && + (tstr[3] == endc || tstr[3] == ':')) { + /* ":ts" or + * ":ts:" */ + parsestate.varSpace = tstr[2]; + cp = tstr + 3; + } else if (tstr[2] == endc || tstr[2] == ':') { + /* ":ts" or ":ts:" */ + parsestate.varSpace = 0; /* no separator */ + cp = tstr + 2; + } else if (tstr[2] == '\\') { + switch (tstr[3]) { + case 'n': + parsestate.varSpace = '\n'; + cp = tstr + 4; + break; + case 't': + parsestate.varSpace = '\t'; + cp = tstr + 4; + break; + default: + if (isdigit((unsigned char)tstr[3])) { + char *ep; + + parsestate.varSpace = + strtoul(&tstr[3], &ep, 0); + if (*ep != ':' && *ep != endc) + goto bad_modifier; + cp = ep; + } else { + /* + * ":ts". + */ + goto bad_modifier; + } + break; + } + } else { + /* + * Found ":ts". + */ + goto bad_modifier; + } + + termc = *cp; + + /* + * We cannot be certain that VarModify + * will be used - even if there is a + * subsequent modifier, so do a no-op + * VarSubstitute now to for str to be + * re-expanded without the spaces. + */ + pattern.flags = VAR_SUB_ONE; + pattern.lhs = pattern.rhs = "\032"; + pattern.leftLen = pattern.rightLen = 1; + + newStr = VarModify(ctxt, &parsestate, nstr, + VarSubstitute, + (ClientData)&pattern); + } else if (tstr[2] == endc || tstr[2] == ':') { + /* + * Check for two-character options: + * ":tu", ":tl" + */ + if (tstr[1] == 'u' || tstr[1] == 'l') { + newStr = VarChangeCase(nstr, (tstr[1] == 'u')); + cp = tstr + 2; + termc = *cp; + } else if (tstr[1] == 'W' || tstr[1] == 'w') { + parsestate.oneBigWord = (tstr[1] == 'W'); + newStr = nstr; + cp = tstr + 2; + termc = *cp; + } else { + /* Found ":t:" or + * ":t". */ + goto bad_modifier; + } + } else { + /* + * Found ":t". + */ + goto bad_modifier; + } + } else { + /* + * Found ":t" or ":t:". + */ + goto bad_modifier; + } + break; + } + case 'N': + case 'M': + { + char *pattern; + char *cp2; + Boolean copy; + int nest; + + copy = FALSE; + nest = 1; + /* + * In the loop below, ignore ':' unless we are at + * (or back to) the original brace level. + * XXX This will likely not work right if $() and ${} + * are intermixed. + */ + for (cp = tstr + 1; + *cp != '\0' && !(*cp == ':' && nest == 1); + cp++) + { + if (*cp == '\\' && + (cp[1] == ':' || + cp[1] == endc || cp[1] == startc)) { + copy = TRUE; + cp++; + continue; + } + if (*cp == startc) + ++nest; + if (*cp == endc) { + --nest; + if (nest == 0) + break; + } + } + termc = *cp; + *WR(cp) = '\0'; + if (copy) { + /* + * Need to compress the \:'s out of the pattern, so + * allocate enough room to hold the uncompressed + * pattern (note that cp started at tstr+1, so + * cp - tstr takes the null byte into account) and + * compress the pattern into the space. + */ + pattern = emalloc(cp - tstr); + for (cp2 = pattern, cp = tstr + 1; + *cp != '\0'; + cp++, cp2++) + { + if ((*cp == '\\') && + (cp[1] == ':' || cp[1] == endc)) { + cp++; + } + *cp2 = *cp; + } + *cp2 = '\0'; + } else { + pattern = UNCONST(&tstr[1]); + } + if ((cp2 = strchr(pattern, '$'))) { + cp2 = pattern; + pattern = Var_Subst(NULL, cp2, ctxt, err); + if (copy) + free(cp2); + copy = TRUE; + } + if (*tstr == 'M' || *tstr == 'm') { + newStr = VarModify(ctxt, &parsestate, nstr, VarMatch, + (ClientData)pattern); + } else { + newStr = VarModify(ctxt, &parsestate, nstr, VarNoMatch, + (ClientData)pattern); + } + if (copy) { + free(pattern); + } + break; + } + case 'S': + { + VarPattern pattern; + Var_Parse_State tmpparsestate; + + pattern.flags = 0; + tmpparsestate = parsestate; + delim = tstr[1]; + tstr += 2; + + /* + * If pattern begins with '^', it is anchored to the + * start of the word -- skip over it and flag pattern. + */ + if (*tstr == '^') { + pattern.flags |= VAR_MATCH_START; + tstr += 1; + } + + cp = tstr; + if ((pattern.lhs = VarGetPattern(ctxt, &parsestate, err, + &cp, delim, + &pattern.flags, + &pattern.leftLen, + NULL)) == NULL) + goto cleanup; + + if ((pattern.rhs = VarGetPattern(ctxt, &parsestate, err, + &cp, delim, NULL, + &pattern.rightLen, + &pattern)) == NULL) + goto cleanup; + + /* + * Check for global substitution. If 'g' after the final + * delimiter, substitution is global and is marked that + * way. + */ + for (;; cp++) { + switch (*cp) { + case 'g': + pattern.flags |= VAR_SUB_GLOBAL; + continue; + case '1': + pattern.flags |= VAR_SUB_ONE; + continue; + case 'W': + tmpparsestate.oneBigWord = TRUE; + continue; + } + break; + } + + termc = *cp; + newStr = VarModify(ctxt, &tmpparsestate, nstr, + VarSubstitute, + (ClientData)&pattern); + + /* + * Free the two strings. + */ + free(UNCONST(pattern.lhs)); + free(UNCONST(pattern.rhs)); + delim = '\0'; + break; + } + case '?': + { + VarPattern pattern; + Boolean value; + + /* find ':', and then substitute accordingly */ + + pattern.flags = 0; + + cp = ++tstr; + delim = ':'; + if ((pattern.lhs = VarGetPattern(ctxt, &parsestate, err, + &cp, delim, NULL, + &pattern.leftLen, + NULL)) == NULL) + goto cleanup; + + /* BROPEN or PROPEN */ + delim = endc; + if ((pattern.rhs = VarGetPattern(ctxt, &parsestate, err, + &cp, delim, NULL, + &pattern.rightLen, + NULL)) == NULL) + goto cleanup; + + termc = *--cp; + delim = '\0'; + if (Cond_EvalExpression(1, v->name, &value, 0) + == COND_INVALID) { + Error("Bad conditional expression `%s' in %s?%s:%s", + v->name, v->name, pattern.lhs, pattern.rhs); + goto cleanup; + } + + if (value) { + newStr = UNCONST(pattern.lhs); + free(UNCONST(pattern.rhs)); + } else { + newStr = UNCONST(pattern.rhs); + free(UNCONST(pattern.lhs)); + } + if (v->flags & VAR_JUNK) { + v->flags |= VAR_KEEP; + } + break; + } +#ifndef NO_REGEX + case 'C': + { + VarREPattern pattern; + char *re; + int error; + Var_Parse_State tmpparsestate; + + pattern.flags = 0; + tmpparsestate = parsestate; + delim = tstr[1]; + tstr += 2; + + cp = tstr; + + if ((re = VarGetPattern(ctxt, &parsestate, err, &cp, delim, + NULL, NULL, NULL)) == NULL) + goto cleanup; + + if ((pattern.replace = VarGetPattern(ctxt, &parsestate, + err, &cp, delim, NULL, + NULL, NULL)) == NULL){ + free(re); + goto cleanup; + } + + for (;; cp++) { + switch (*cp) { + case 'g': + pattern.flags |= VAR_SUB_GLOBAL; + continue; + case '1': + pattern.flags |= VAR_SUB_ONE; + continue; + case 'W': + tmpparsestate.oneBigWord = TRUE; + continue; + } + break; + } + + termc = *cp; + + error = regcomp(&pattern.re, re, REG_EXTENDED); + free(re); + if (error) { + *lengthPtr = cp - start + 1; + VarREError(error, &pattern.re, "RE substitution error"); + free(pattern.replace); + return (var_Error); + } + + pattern.nsub = pattern.re.re_nsub + 1; + if (pattern.nsub < 1) + pattern.nsub = 1; + if (pattern.nsub > 10) + pattern.nsub = 10; + pattern.matches = emalloc(pattern.nsub * + sizeof(regmatch_t)); + newStr = VarModify(ctxt, &tmpparsestate, nstr, + VarRESubstitute, + (ClientData) &pattern); + regfree(&pattern.re); + free(pattern.replace); + free(pattern.matches); + delim = '\0'; + break; + } +#endif + case 'Q': + if (tstr[1] == endc || tstr[1] == ':') { + newStr = VarQuote(nstr); + cp = tstr + 1; + termc = *cp; + break; + } + goto default_case; + case 'T': + if (tstr[1] == endc || tstr[1] == ':') { + newStr = VarModify(ctxt, &parsestate, nstr, VarTail, + (ClientData)0); + cp = tstr + 1; + termc = *cp; + break; + } + goto default_case; + case 'H': + if (tstr[1] == endc || tstr[1] == ':') { + newStr = VarModify(ctxt, &parsestate, nstr, VarHead, + (ClientData)0); + cp = tstr + 1; + termc = *cp; + break; + } + goto default_case; + case 'E': + if (tstr[1] == endc || tstr[1] == ':') { + newStr = VarModify(ctxt, &parsestate, nstr, VarSuffix, + (ClientData)0); + cp = tstr + 1; + termc = *cp; + break; + } + goto default_case; + case 'R': + if (tstr[1] == endc || tstr[1] == ':') { + newStr = VarModify(ctxt, &parsestate, nstr, VarRoot, + (ClientData)0); + cp = tstr + 1; + termc = *cp; + break; + } + goto default_case; + case 'O': + { + char otype; + + cp = tstr + 1; /* skip to the rest in any case */ + if (tstr[1] == endc || tstr[1] == ':') { + otype = 's'; + termc = *cp; + } else if ( (tstr[1] == 'x') && + (tstr[2] == endc || tstr[2] == ':') ) { + otype = tstr[1]; + cp = tstr + 2; + termc = *cp; + } else { + goto bad_modifier; + } + newStr = VarOrder(nstr, otype); + break; + } + case 'u': + if (tstr[1] == endc || tstr[1] == ':') { + newStr = VarUniq(nstr); + cp = tstr + 1; + termc = *cp; + break; + } + goto default_case; +#ifdef SUNSHCMD + case 's': + if (tstr[1] == 'h' && (tstr[2] == endc || tstr[2] == ':')) { + const char *emsg; + newStr = Cmd_Exec(nstr, &emsg); + if (emsg) + Error(emsg, nstr); + cp = tstr + 2; + termc = *cp; + break; + } + goto default_case; +#endif + default: + default_case: + { +#ifdef SYSVVARSUB + /* + * This can either be a bogus modifier or a System-V + * substitution command. + */ + VarPattern pattern; + Boolean eqFound; + + pattern.flags = 0; + eqFound = FALSE; + /* + * First we make a pass through the string trying + * to verify it is a SYSV-make-style translation: + * it must be: =) + */ + cp = tstr; + cnt = 1; + while (*cp != '\0' && cnt) { + if (*cp == '=') { + eqFound = TRUE; + /* continue looking for endc */ + } + else if (*cp == endc) + cnt--; + else if (*cp == startc) + cnt++; + if (cnt) + cp++; + } + if (*cp == endc && eqFound) { + + /* + * Now we break this sucker into the lhs and + * rhs. We must null terminate them of course. + */ + delim='='; + cp = tstr; + if ((pattern.lhs = VarGetPattern(ctxt, &parsestate, + err, &cp, delim, &pattern.flags, + &pattern.leftLen, NULL)) == NULL) + goto cleanup; + delim = endc; + if ((pattern.rhs = VarGetPattern(ctxt, &parsestate, + err, &cp, delim, NULL, &pattern.rightLen, + &pattern)) == NULL) + goto cleanup; + + /* + * SYSV modifications happen through the whole + * string. Note the pattern is anchored at the end. + */ + termc = *--cp; + delim = '\0'; + newStr = VarModify(ctxt, &parsestate, nstr, + VarSYSVMatch, + (ClientData)&pattern); + free(UNCONST(pattern.lhs)); + free(UNCONST(pattern.rhs)); + } else +#endif + { + Error("Unknown modifier '%c'", *tstr); + for (cp = tstr+1; + *cp != ':' && *cp != endc && *cp != '\0'; + cp++) + continue; + termc = *cp; + newStr = var_Error; + } + } + } + if (DEBUG(VAR)) { + printf("Result is \"%s\"\n", newStr); + } + + if (newStr != nstr) { + if (*freePtr) { + free(nstr); + } + nstr = newStr; + if (nstr != var_Error && nstr != varNoError) { + *freePtr = TRUE; + } else { + *freePtr = FALSE; + } + } + if (termc == '\0') { + Error("Unclosed variable specification for %s", v->name); + } else if (termc == ':') { + *WR(cp) = termc; + cp++; + } else { + *WR(cp) = termc; + } + tstr = cp; + } + *lengthPtr = tstr - start + 1; + } else { + *lengthPtr = tstr - start + 1; + *WR(tstr) = endc; + } + + if (v->flags & VAR_FROM_ENV) { + Boolean destroy = FALSE; + + if (nstr != (char *)Buf_GetAll(v->val, NULL)) { + destroy = TRUE; + } else { + /* + * Returning the value unmodified, so tell the caller to free + * the thing. + */ + *freePtr = TRUE; + } + if (nstr != (char *)Buf_GetAll(v->val, NULL)) + Buf_Destroy(v->val, destroy); + free(v->name); + free(v); + } else if (v->flags & VAR_JUNK) { + /* + * Perform any free'ing needed and set *freePtr to FALSE so the caller + * doesn't try to free a static pointer. + * If VAR_KEEP is also set then we want to keep str as is. + */ + if (!(v->flags & VAR_KEEP)) { + if (*freePtr) { + free(nstr); + } + *freePtr = FALSE; + if (dynamic) { + nstr = emalloc(*lengthPtr + 1); + strncpy(nstr, start, *lengthPtr); + nstr[*lengthPtr] = '\0'; + *freePtr = TRUE; + } else { + nstr = var_Error; + } + } + if (nstr != (char *)Buf_GetAll(v->val, NULL)) + Buf_Destroy(v->val, TRUE); + free(v->name); + free(v); + } + return (nstr); + + bad_modifier: + /* "{(" */ + Error("Bad modifier `:%.*s' for %s", (int)strcspn(tstr, ":)}"), tstr, + v->name); + +cleanup: + *lengthPtr = cp - start + 1; + if (*freePtr) + free(nstr); + if (delim != '\0') + Error("Unclosed substitution for %s (%c missing)", + v->name, delim); + return (var_Error); +} + +/*- + *----------------------------------------------------------------------- + * Var_Subst -- + * Substitute for all variables in the given string in the given context + * If undefErr is TRUE, Parse_Error will be called when an undefined + * variable is encountered. + * + * Input: + * var Named variable || NULL for all + * str the string which to substitute + * ctxt the context wherein to find variables + * undefErr TRUE if undefineds are an error + * + * Results: + * The resulting string. + * + * Side Effects: + * None. The old string must be freed by the caller + *----------------------------------------------------------------------- + */ +char * +Var_Subst(const char *var, const char *str, GNode *ctxt, Boolean undefErr) +{ + Buffer buf; /* Buffer for forming things */ + char *val; /* Value to substitute for a variable */ + int length; /* Length of the variable invocation */ + Boolean trailingBslash; /* variable ends in \ */ + Boolean doFree; /* Set true if val should be freed */ + static Boolean errorReported; /* Set true if an error has already + * been reported to prevent a plethora + * of messages when recursing */ + + buf = Buf_Init(MAKE_BSIZE); + errorReported = FALSE; + trailingBslash = FALSE; + + while (*str) { + if (*str == '\n' && trailingBslash) + Buf_AddByte(buf, ' '); + if (var == NULL && (*str == '$') && (str[1] == '$')) { + /* + * A dollar sign may be escaped either with another dollar sign. + * In such a case, we skip over the escape character and store the + * dollar sign into the buffer directly. + */ + str++; + Buf_AddByte(buf, (Byte)*str); + str++; + } else if (*str != '$') { + /* + * Skip as many characters as possible -- either to the end of + * the string or to the next dollar sign (variable invocation). + */ + const char *cp; + + for (cp = str++; *str != '$' && *str != '\0'; str++) + continue; + Buf_AddBytes(buf, str - cp, (const Byte *)cp); + } else { + if (var != NULL) { + int expand; + for (;;) { + if (str[1] == '\0') { + /* A trailing $ is kind of a special case */ + Buf_AddByte(buf, str[0]); + str++; + expand = FALSE; + } else if (str[1] != PROPEN && str[1] != BROPEN) { + if (str[1] != *var || strlen(var) > 1) { + Buf_AddBytes(buf, 2, (const Byte *)str); + str += 2; + expand = FALSE; + } + else + expand = TRUE; + break; + } + else { + const char *p; + + /* + * Scan up to the end of the variable name. + */ + for (p = &str[2]; *p && + *p != ':' && *p != PRCLOSE && *p != BRCLOSE; p++) + if (*p == '$') + break; + /* + * A variable inside the variable. We cannot expand + * the external variable yet, so we try again with + * the nested one + */ + if (*p == '$') { + Buf_AddBytes(buf, p - str, (const Byte *)str); + str = p; + continue; + } + + if (strncmp(var, str + 2, p - str - 2) != 0 || + var[p - str - 2] != '\0') { + /* + * Not the variable we want to expand, scan + * until the next variable + */ + for (;*p != '$' && *p != '\0'; p++) + continue; + Buf_AddBytes(buf, p - str, (const Byte *)str); + str = p; + expand = FALSE; + } + else + expand = TRUE; + break; + } + } + if (!expand) + continue; + } + + val = Var_Parse(str, ctxt, undefErr, &length, &doFree); + + /* + * When we come down here, val should either point to the + * value of this variable, suitably modified, or be NULL. + * Length should be the total length of the potential + * variable invocation (from $ to end character...) + */ + if (val == var_Error || val == varNoError) { + /* + * If performing old-time variable substitution, skip over + * the variable and continue with the substitution. Otherwise, + * store the dollar sign and advance str so we continue with + * the string... + */ + if (oldVars) { + str += length; + } else if (undefErr) { + /* + * If variable is undefined, complain and skip the + * variable. The complaint will stop us from doing anything + * when the file is parsed. + */ + if (!errorReported) { + Parse_Error(PARSE_FATAL, + "Undefined variable \"%.*s\"",length,str); + } + str += length; + errorReported = TRUE; + } else { + Buf_AddByte(buf, (Byte)*str); + str += 1; + } + } else { + /* + * We've now got a variable structure to store in. But first, + * advance the string pointer. + */ + str += length; + + /* + * Copy all the characters from the variable value straight + * into the new string. + */ + length = strlen(val); + Buf_AddBytes(buf, length, (Byte *)val); + trailingBslash = length > 0 && val[length - 1] == '\\'; + if (doFree) { + free(val); + } + } + } + } + + Buf_AddByte(buf, '\0'); + val = (char *)Buf_GetAll(buf, NULL); + Buf_Destroy(buf, FALSE); + return (val); +} + +/*- + *----------------------------------------------------------------------- + * Var_GetTail -- + * Return the tail from each of a list of words. Used to set the + * System V local variables. + * + * Input: + * file Filename to modify + * + * Results: + * The resulting string. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +#if 0 +char * +Var_GetTail(char *file) +{ + return(VarModify(file, VarTail, (ClientData)0)); +} + +/*- + *----------------------------------------------------------------------- + * Var_GetHead -- + * Find the leading components of a (list of) filename(s). + * XXX: VarHead does not replace foo by ., as (sun) System V make + * does. + * + * Input: + * file Filename to manipulate + * + * Results: + * The leading components. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +char * +Var_GetHead(char *file) +{ + return(VarModify(file, VarHead, (ClientData)0)); +} +#endif + +/*- + *----------------------------------------------------------------------- + * Var_Init -- + * Initialize the module + * + * Results: + * None + * + * Side Effects: + * The VAR_CMD and VAR_GLOBAL contexts are created + *----------------------------------------------------------------------- + */ +void +Var_Init(void) +{ + VAR_GLOBAL = Targ_NewGN("Global"); + VAR_CMD = Targ_NewGN("Command"); + +} + + +void +Var_End(void) +{ +} + + +/****************** PRINT DEBUGGING INFO *****************/ +static void +VarPrintVar(ClientData vp) +{ + Var *v = (Var *)vp; + printf("%-16s = %s\n", v->name, (char *)Buf_GetAll(v->val, NULL)); +} + +/*- + *----------------------------------------------------------------------- + * Var_Dump -- + * print all variables in a context + *----------------------------------------------------------------------- + */ +void +Var_Dump(GNode *ctxt) +{ + Hash_Search search; + Hash_Entry *h; + + for (h = Hash_EnumFirst(&ctxt->context, &search); + h != NULL; + h = Hash_EnumNext(&search)) { + VarPrintVar(Hash_GetValue(h)); + } +} diff --git a/devel/bmake/files/wait.h b/devel/bmake/files/wait.h new file mode 100644 index 0000000000000..deab3de759be0 --- /dev/null +++ b/devel/bmake/files/wait.h @@ -0,0 +1,81 @@ +/* NAME: + * wait.h - compensate for what vendors leave out + * + * AUTHOR: + * Simon J. Gerraty + */ +/* + * RCSid: + * $Id: wait.h,v 1.1.1.1 2005/12/02 00:03:00 sjg Exp $ + * + * @(#)Copyright (c) 1994, Simon J. Gerraty. + * + * This is free software. It comes with NO WARRANTY. + * Permission to use, modify and distribute this source code + * is granted subject to the following conditions. + * 1/ that the above copyright notice and this notice + * are preserved in all copies and that due credit be given + * to the author. + * 2/ that any changes to this code are clearly commented + * as such so that the author does not get blamed for bugs + * other than his own. + * + * Please send copies of changes and bug-fixes to: + * sjg@crufty.net + */ + +#include + +#ifdef sun386 +# define UNION_WAIT +# define WEXITSTATUS(x) ((&x)->w_retcode) +# define WTERMSIG(x) ((&x)->w_termsig) +# define WSTOPSIG(x) ((&x)->w_stopsig) +# define HAVE_WAIT4 +#endif + +#ifndef WAIT_T +# ifdef UNION_WAIT +# define WAIT_T union wait +# define WAIT_STATUS(x) (x).w_status +# else +# define WAIT_T int +# define WAIT_STATUS(x) x +# endif +#endif + +#ifndef WEXITSTATUS +# define WEXITSTATUS(_X) (((int)(_X)>>8)&0377) +#endif +#ifndef WSTOPPED +# define WSTOPPED 0177 +#endif +#ifndef WSTOPSIG +# define WSTOPSIG(x) WSTOPPED +#endif + +#ifdef UNION_WAIT +#ifndef WSET_STOPCODE +#define WSET_STOPCODE(x, sig) ((&x)->w_stopsig = (sig)) +#endif +#ifndef WSET_EXITCODE +#define WSET_EXITCODE(x, ret, sig) ((&x)->w_termsig = (sig), (&x)->w_retcode = (ret)) +#endif +#else +#ifndef WSET_STOPCODE +#define WSET_STOPCODE(x, sig) ((x) = ((sig) << 8) | 0177) +#endif +#ifndef WSET_EXITCODE +#define WSET_EXITCODE(x, ret, sig) ((x) = (ret << 8) | (sig)) +#endif +#endif + +#ifndef HAVE_WAITPID +# ifdef HAVE_WAIT4 +# define waitpid(pid, statusp, flags) wait4(pid, statusp, flags, (char *)0) +# else +# ifdef HAVE_WAIT3 +# define waitpid(pid, statusp, flags) wait3(statusp, flags, (char *)0) +# endif +# endif +#endif