diff options
author | obrien <obrien@FreeBSD.org> | 2012-07-06 00:03:48 +0000 |
---|---|---|
committer | obrien <obrien@FreeBSD.org> | 2012-07-06 00:03:48 +0000 |
commit | 4affa3ad6b54ff9b50a35eb0b3de56dc812b7034 (patch) | |
tree | 7147ca52ac3d60bb73805193c0a5449e96d960b8 | |
parent | 65ae55af13be5349deaf3aec65b2c630a79becc5 (diff) | |
download | FreeBSD-src-4affa3ad6b54ff9b50a35eb0b3de56dc812b7034.zip FreeBSD-src-4affa3ad6b54ff9b50a35eb0b3de56dc812b7034.tar.gz |
"Tag" the 20120704 Portable BSD make import.
120 files changed, 54382 insertions, 0 deletions
diff --git a/20120704/ChangeLog b/20120704/ChangeLog new file mode 100644 index 0000000..3f82c8d --- /dev/null +++ b/20120704/ChangeLog @@ -0,0 +1,1383 @@ +2012-07-04 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20120704 + Merge with NetBSD make, pick up + o Job_ParseShell should call Shell_Init if it has been + previously called. + * Makefile.in: set USE_META based on configure result. + also .PARSEDIR is safer indicator of bmake. + +2012-06-26 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in: bump version to 20120626 + ensure CPPFLAGS is in CFLAGS + * meta.c: avoid nested externs + * bsd.after-import.mk: avoid ${.CURDIR}/Makefile as target + +2012-06-20 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20120620 + Merge with NetBSD make, pick up + o make_malloc.c: avoid including make_malloc.h again + + * Makefile.in: avoid bmake only syntax or protect with + .if defined(.MAKE.LEVEL) + * bsd.after-import.mk: replace .-include with .sinclude + ensure? SRCTOP gets a value + * configure.in: look for filemon.h in /usr/include/dev/filemon first. + +2012-06-19 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20120612 + Merge with NetBSD make, pick up + o use MAKE_ATTR_* rather than those defined by cdefs.h or compiler + for greater portability. + o unit-tests/forloop: check that .for works as expected wrt + number of times and with "quoted strings". + +2012-06-06 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20120606 + Merge with NetBSD make, pick up + o compat.c: use kill(2) rather than raise(3). + * configure.in: look for sys/dev/filemon + * bsd.after-import.mk: add a .-include "Makefile.inc" to Makefile + and pass BOOTSTRAP_XTRAS to boot-strap. + +2012-06-04 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20120604 + Merge with NetBSD make, pick up + o util.c and var.c share same var for tracking if environ + has been reallocated. + o util.c provide getenv with setenv. + * Add MAKE_LEVEL_SAFE as an alternate means of passing MAKE_LEVEL + when the shell actively strips .MAKE.* from the environment. + We still refer to the variable always as .MAKE.LEVEL + * util.c fix bug in findenv() was finding prefix of name. + * compat.c: re-raising SIGINT etc after running .INTERRUPT + results in more reliable termination of all activity on many + platforms. + +2012-06-02 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20120602 + Merge with NetBSD make, pick up + o for.c: handle quoted items in .for list + +2012-05-30 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20120530 + Merge with NetBSD make, pick up + o compat.c: ignore empty command. + +2012-05-24 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20120524 + * FILES: add bsd.after-import.mk: + A simple means of integrating bmake into a BSD build system. + +2012-05-20 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20120520 + Merge with NetBSD make, pick up + o increased limit for nested conditionals. + +2012-05-18 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20120518 + Merge with NetBSD make, pick up + o use _exit(2) in signal hanlder + o Don't use the [dir] cache when building nodes that might have + changed since the last exec. + o Avoid nested extern declaration warnings. + +2012-04-27 Simon J. Gerraty <sjg@bad.crufty.net> + + * meta.c (fgetLine): avoid %z - not portable. + * parse.c: Since we moved include of sys/mman.h + and def's of MAP_COPY etc. we got dups from a merge. + +2012-04-24 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20120420 + Merge with NetBSD make, pick up + o restore duplicate supression in .MAKE.MAKEFILES + runtime saving can be significant. + o Var_Subst() uses Buf_DestroyCompact() to reduce memory + consumption up to 20%. + +2012-04-20 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20120420 + Merge with NetBSD make, pick up + o remove duplicate supression in .MAKE.MAKEFILES + o improved dir cache behavior + o gmake'ish export command + +2012-03-25 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20120325 + Merge with NetBSD make, pick up + o fix parsing of :[#] in conditionals. + +2012-02-10 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in: replace use of .Nx in bmake.1 with NetBSD + since some systems cannot cope with .Nx <version> + +2011-11-14 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20111111 + Merge with NetBSD make, pick up + o debug output for .PARSEDIR and .PARSEFILE + +2011-10-10 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20111010 + +2011-10-09 Simon J. Gerraty <sjg@bad.crufty.net> + + * boot-strap: check for an expected file in the dirs we look for. + * make-bootstrap.sh: pass on LDSTATIC + +2011-10-01 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20111001 + Merge with NetBSD make, pick up + o ensure .PREFIX is set for .PHONY + and .TARGET set for .PHONY run via .END + o __dead used consistently + +2011-09-10 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): 20110909 is a better number ;-) + +2011-09-05 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20110905 + Merge with NetBSD make, pick up + o meta_oodate: ignore makeDependfile + +2011-08-28 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20110828 + Merge with NetBSD make, pick up + o silent=yes in .MAKE.MODE causes meta mode to mark targets + as SILENT if a .meta file is created + +2011-08-18 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20110818 + Merge with NetBSD make, pick up + o in meta mode, if target flagged .META a missing .meta file + means target is out-of-date + o fixes for gcc 4.5 warnings + o simplify job printing code + +2011-08-09 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20110808 + Merge with NetBSD make, pick up + o do not touch OP_SPECIAL targets when doing make -t + +2011-06-22 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20110622 + Merge with NetBSD make, pick up + o meta_oodate detect corrupted .meta file and declare oodate. + * configure.in: add check for setsid + +2011-06-07 Simon J. Gerraty <sjg@bad.crufty.net> + + * Merge with NetBSD make, pick up + o unit-tests/modts now works on MirBSD + +2011-06-04 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20110606 + Merge with NetBSD make, pick up + o ApplyModifiers: when we parse a variable which is not + the entire modifier string, or not followed by ':', do not + consider it as containing modifiers. + o loadfile: ensure newline at end of mapped file. + +2011-05-05 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20110505 + Merge with NetBSD make, pick up + o .MAKE.META.BAILIWICK - list of prefixes which define the scope + of make's control. In meta mode, any generated file within + said bailiwick, which is found to be missing, causes current + target to be out-of-date. + +2011-04-11 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20110411 + Merge with NetBSD make, pick up + o when long modifiers fail to match, check sysV style. + - add a test case + +2011-04-10 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20110410 + Merge with NetBSD make, pick up + o :hash - cheap 32bit hash of value + o :localtime, :gmtime - use value as format string for strftime. + +2011-03-30 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20110330 + mostly because its a cooler version. + Merge with NetBSD make, pick up + o NetBSD tags for meta.[ch] + o job.c call meta_job_finish() after meta_job_error(). + o meta_job_error() should call meta_job_finish() to ensure + .meta file is closed, and safe to copy - if .ERROR target wants. + meta_job_finish() is safe to call repeatedly. + +2011-03-29 Simon J. Gerraty <sjg@bad.crufty.net> + + * unit-tests/modts: use printf if it is a builtin, + to save us from MirBSD + + * Makefile.in (MAKE_VERSION): bump version to 20110329 + Merge with NetBSD make, pick up + o fix for use after free() in CondDoExists(). + o meta_oodate() report extra commands and return earlier. + +2011-03-27 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20110327 + Merge with NetBSD make, pick up + o meta.c, if .MAKE.MODE contains curdirOk=yes + allow creating .meta files in .CURDIR + * boot-strap (TOOL_DIFF): aparently at least on linux distro + formats the output of 'type' differently - so eat any "()" + +2011-03-06 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20110306 + Merge with NetBSD make, pick up + o meta.c, only do getcwd() once + +2011-03-05 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20110305 + Merge with NetBSD make, pick up + o correct sysV substitution handling of empty lhs and variable + o correct exists() check for dir with trailing / + o correct handling of modifiers for non-existant variables + during evaluation of conditionals. + o ensure MAP_FILE is defined. + o meta.c use curdir[] now exported by main.c + +2011-02-25 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20110225 + Merge with NetBSD make, pick up + o fix for incorrect .PARSEDIR when .OBJDIR is re-computed after + makefiles have been read. + o fix example of :? modifier in man page. + +2011-02-13 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20110214 + Merge with NetBSD make, pick up + o meta.c handle realpath() failing when generating meta file + name. + + * sigcompat.c: convert to ansi so we can use higher warning levels. + + +2011-02-07 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20110207 + Merge with NetBSD make, pick up + o fix for bug in meta mode. + +2011-01-03 Simon J. Gerraty <sjg@bad.crufty.net> + + * parse.c: SunOS 5.8 at least does not have MAP_FILE + +2011-01-01 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20110101 + Merge with NetBSD make, pick up + o use mmap(2) if available, for reading makefiles + +2010-12-15 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20101215 + Merge with NetBSD make, pick up + o ensure meta_job_error() does not report a previous .meta file + as being culprit. + +2010-12-10 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20101210 + Merge with NetBSD make, pick up + o meta_oodate: track cwd per process, and only consider target + out-of-date if missing file is outside make's CWD. + Ignore files in /tmp/ etc. + o to ensure unit-tests results match, need to control LC_ALL + as well as LANG. + o fix for parsing bug in var.c + +2010-11-26 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20101126 + Merge with NetBSD make, pick up + o if stale dependency is an IMPSRC, search via .PATH + o meta_oodate: if a referenced file is missing, target is + out-of-date. + o meta_oodate: if a target uses .OODATE in its commands, + it (.OODATE) needs to be recomputed. + o keep a pointer to youngest child node, rather than just its + mtime. + +2010-11-02 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20101101 + +2010-10-16 Simon J. Gerraty <sjg@bad.crufty.net> + + * machine.sh: like os.sh, + allow for uname -p producing useless drivel + +2010-09-13 Simon J. Gerraty <sjg@bad.crufty.net> + + * boot-strap: document configure knobs for meta and filemon. + + * Makefile.in (MAKE_VERSION): bump version to 20100911 + Merge with NetBSD make, pick up + o meta.c - meta mode + + * make-bootstrap.sh.in: handle meta.c + * configure.in: add knobs for use_meta and filemon_h + also, look for dirname, str[e]sep and strlcpy + * util.c: add simple err[x] and warn[x] + +2010-08-08 Simon J. Gerraty <sjg@bad.crufty.net> + + * boot-strap (TOOL_DIFF): set this to ensure tests use + the same version of diff that configure tested + + * Makefile.in (MAKE_VERSION): bump version to 20100808 + Merge with NetBSD make, pick up + o in jobs mode, when we discover we cannot make something, + call PrintOnError before exit. + +2010-08-06 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20100806 + Merge with NetBSD make, pick up + o formatting fixes for ignored errors + o ensure jobs are cleaned up regardless of where wait() was called. + +2010-06-28 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20100618 + * os.sh (MACHINE_ARCH): watch out for drivel from uname -p + +2010-06-16 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20100616 + Merge with NetBSD make, pick up + o man page update + o call PrintOnError from JobFinish when we detect an error we + are not ignoring. + +2010-06-06 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20100606 + Merge with NetBSD make, pick up + o man page update + +2010-06-05 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20100605 + Merge with NetBSD make, pick up + o use bmake_signal() which is a wrapper around sigaction() + in place of signal() + o add .export-env to allow exporting variables to environment + without tracking (so no re-export when the internal value is + changed). + +2010-05-24 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20100524 + Merge with NetBSD make, pick up + o fix for .info et al being greedy. + +2010-05-23 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20100520 + Merge with NetBSD make, pick up + o back to using realpath on argv[0] + but only if contains '/' and does not start with '/'. + +2010-05-10 Simon J. Gerraty <sjg@bad.crufty.net> + + * boot-strap: use absolute path for bmake when running tests. + + * Makefile.in (MAKE_VERSION): bump version to 20100510 + Merge with NetBSD make, pick up + o revert use of realpath on argv[0] + too many corner cases. + o print MAKE_PRINT_VAR_ON_ERROR before running .ERROR target. + +2010-05-05 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20100505 + Merge with NetBSD make, pick up + o fix for missed SIGCHLD when compiled with SunPRO + actually for bmake, defining FORCE_POSIX_SIGNALS would have + done the job. + +2010-04-30 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20100430 + Merge with NetBSD make, pick up + o fflush stdout before writing to stdout + +2010-04-23 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20100423 + Merge with NetBSD make, pick up + o updated unit tests for Haiku (this time for sure). + * boot-strap: based on patch from joerg + honor --with-default-sys-path better. + * boot-strap: remove mention of --with-prefix-sys-path + +2010-04-22 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20100422 + * Merge with NetBSD make, pick up + o fix for vfork() on Darwin. + o fix for bogus $TMPDIR. + o set .MAKE.MODE=compat for -B + o set .MAKE.JOBS=max_jobs for -j max_jobs + o allow unit-tests to run without any *.mk + o unit-tests/modmisc be more conservative in dirs presumed to exist. + * boot-strap: ignore /usr/share/mk except on NetBSD. + * unit-tests/Makefile.in: set LANG=C when running unit-tests to + ensure sort(1) behaves as expected. + +2010-04-21 Simon J. Gerraty <sjg@bad.crufty.net> + + * boot-strap: add FindHereOrAbove so we can use -m .../mk + +2010-04-20 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20100420 + * Merge with NetBSD make, pick up + o fix for variable realpath() behavior. + we have to stat(2) the result to be sure. + o fix for .export (all) when nested vars use :sh + +2010-04-14 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20100414 + * Merge with NetBSD make, pick up + o use realpath to resolve argv[0] (for .MAKE) if needed. + o add realpath from libc. + o add :tA to resolve variable via realpath(3) if possible. + +2010-04-08 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20100408 + * Merge with NetBSD make, pick up + o unit tests for .ERROR, .error + o fix for .ERROR to ensure it cannot be default target. + +2010-04-06 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20100406 + * Merge with NetBSD make, pick up + o fix for compat mode "Error code" going to debug_file. + o fix for .ALLSRC being populated twice. + o support for .info, .warning and .error directives + o .MAKE.MODE to control make's operational mode + o .MAKE.MAKEFILE_PREFERENCE to control the preferred makefile + name(s). + o .MAKE.DEPENDFILE to control the name of the depend file + o .ERROR target - run on failure. + +2010-03-18 Simon J. Gerraty <sjg@bad.crufty.net> + + * make-bootstrap.sh.in: extract MAKE_VERSION from Makefile + + * os.sh,arch.c: patch for Haiku from joerg at netbsd + +2010-03-17 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20100222 + * Merge with NetBSD make, pick up + o better error msg for .for with mutiple inter vars + + * boot-strap: + o use make-bootstrap.sh from joerg at netbsd + to avoid the need for a native make when bootstrapping. + o add "" everywhere ;-) + o if /usr/share/tmac/andoc.tmac exists install nroff bmake.1 + otherwise the pre-formated version. + +2010-01-04 Simon J. Gerraty <sjg@bad.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20100102 + * Merge with NetBSD make, pick up: + o fix for -m .../ + +2009-11-18 Simon J. Gerraty <sjg@void.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20091118 + * Merge with NetBSD make, pick up: + o .unexport + o report lines that start with '.' and should have ':' + (catch typo's of .el*if). + +2009-10-30 Simon J. Gerraty <sjg@void.crufty.net> + + * configure.in: Ensure that srcdir and mksrc are absolute paths. + +2009-10-09 Simon J. Gerraty <sjg@void.crufty.net> + + * Makefile.in (MAKE_VERSION): fix version to 20091007 + +2009-10-07 Simon J. Gerraty <sjg@void.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 200910007 + * Merge with NetBSD make, pick up: + o fix for parsing of :S;...;...; applied to .for loop iterator + appearing in a dependency line. + +2009-09-09 Simon J. Gerraty <sjg@void.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20090909 + * Merge with NetBSD make, pick up: + o fix for -C, .CURDIR and .OBJDIR + * boot-strap: + o allow share_dir to be set independent of prefix. + o select default share_dir better when prefix ends in $HOST_TARGET + o if FORCE_BSD_MK etc were set, include them in the suggested + install-mk command. + +2009-09-08 Simon J. Gerraty <sjg@void.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20090908 + * Merge with NetBSD make, pick up: + o .MAKE.LEVEL for recursion tracking + o fix for :M scanning \: + +2009-09-03 Simon J. Gerraty <sjg@void.crufty.net> + + * configure.in: Don't -D__EXTENSIONS__ if + AC_USE_SYSTEM_EXTENSIONS says "no". + +2009-08-26 Simon J. Gerraty <sjg@void.crufty.net> + + * Makefile.in (MAKE_VERSION): bump version to 20090826 + Simplify MAKE_VERSION to just the bare date. + * Merge with NetBSD make, pick up: + o -C directory support. + o support for SIGINFO + o use $TMPDIR for temp files. + o child of vfork should be careful about modifying parent's state. + + +2009-03-26 Simon J. Gerraty <sjg@void.crufty.net> + + * Appy some patches for MiNT from David Brownlee + +2009-02-26 Simon J. Gerraty <sjg@void.crufty.net> + + * Makefile.in (BMAKE_VERSION): bump version to 20090222 + * Merge with NetBSD make, pick up: + o Possible null pointer de-ref in Var_Set. + +2009-02-08 Simon J. Gerraty <sjg@void.crufty.net> + + * Makefile.in (BMAKE_VERSION): bump version to 20090204 + * Merge with NetBSD make, pick up: + o bmake_malloc et al moved to their own .c + o Count both () and {} when looking for the end of a :M pattern + o Change 'Buffer' so that it is the actual struct, not a pointer to it. + o strlist.c - functions for processing extendable arrays of pointers to strings. + o ClientData replaced with void *, so const void * can be used. + o New debug flag C for DEBUG_CWD + +2008-11-11 Simon J. Gerraty <sjg@void.crufty.net> + + * Makefile.in (BMAKE_VERSION): bump version to 20081111 + Apply patch from Joerg Sonnenberge to + configure.in: + o remove some redundant checks + o check for emlloc etc only in libutil and require the whole family. + util.c: + o remove [v]asprintf which is no longer used. + +2008-11-04 Simon J. Gerraty <sjg@void.crufty.net> + + * Makefile.in (BMAKE_VERSION): bump version to 20081101 + * Merge with NetBSD make, pick up: + o util.c: avoid use of putenv() - christos + +2008-10-30 Simon J. Gerraty <sjg@void.crufty.net> + + * Makefile.in (BMAKE_VERSION): bump version to 20081030 + pick up man page tweaks. + +2008-10-29 Simon J. Gerraty <sjg@void.crufty.net> + + * Makefile.in: move processing of LIBOBJS to after is definition! + thus we'll have getenv.c in SRCS only if needed. + + * make.1: add examples of how to use :? + + * Makefile.in (BMAKE_VERSION): bump version to 20081029 + * Merge with NetBSD make, pick up: + o fix for .END processing with -j + o segfault from Parse_Error when no makefile is open + o handle numeric expressions in any variable expansion + o debug output now defaults to stderr, -dF to change it - apb + o make now uses bmake_malloc etc so that it can build natively + on A/UX - wasn't an issue for bmake, but we want to keep in sync. + +2008-09-27 Simon J. Gerraty <sjg@void.crufty.net> + + * Makefile.in (BMAKE_VERSION): bump version to 20080808 + * Merge with NetBSD make, pick up: + o fix for PR/38840: Pierre Pronchery: make crashes while parsing + long lines in Makefiles + o optimizations for VarQuote by joerg + o fix for PR/38756: dominik: make dumps core on invalid makefile + +2008-05-15 Simon J. Gerraty <sjg@void.crufty.net> + + * Makefile.in (BMAKE_VERSION): bump version to 20080515 + * Merge with NetBSD make, pick up: + o fix skip setting vars in VAR_GLOBAL context, to handle + cases where VAR_CMD is used for other than command line vars. + +2008-05-14 Simon J. Gerraty <sjg@void.crufty.net> + + * boot-strap (make_version): we may need to look in + $prefix/share/mk for sys.mk + + * Makefile.in (BMAKE_VERSION): bump version to 20080514 + * Merge with NetBSD make, pick up: + o skip setting vars in VAR_GLOBAL context, when already set in + VAR_CMD which takes precedence. + +2008-03-30 Simon J. Gerraty <sjg@void.crufty.net> + + * Makefile.in (BMAKE_VERSION): bump version to 20080330 + * Merge with NetBSD make, pick up: + o fix for ?= when LHS contains variable reference. + +2008-02-15 Simon J. Gerraty <sjg@void.crufty.net> + + * merge some patches from NetBSD pkgsrc. + + * makefile.boot.in (BOOTSTRAP_SYS_PATH): Allow better control of + the MAKSYSPATH used during bootstrap. + + * Makefile.in (BMAKE_VERSION): bump version to 20080215 + * Merge with NetBSD make, pick up: + o warn if non-space chars follow 'empty' in a conditional. + +2008-01-18 Simon J. Gerraty <sjg@void.crufty.net> + + * Makefile.in (BMAKE_VERSION): bump version to 20080118 + * Merge with NetBSD make, pick up: + o consider dependencies read from .depend as optional - dsl + o remember when buffer for reading makefile grows - dsl + o add -dl (aka LOUD) - David O'Brien + +2007-10-22 Simon J. Gerraty <sjg@void.crufty.net> + + * Makefile.in (BMAKE_VERSION): bump version to 20071022 + * Merge with NetBSD make, pick up: + o Allow .PATH<suffix> to be used for .include "" + + * boot-strap: source default settings from .bmake-boot-strap.rc + +2007-10-16 Simon J. Gerraty <sjg@void.crufty.net> + + * Makefile.in: fix maninstall on various systems + provided that our man.mk is used. + For non-BSD systems we install the preformatted page + into $MANDIR/cat1 + +2007-10-15 Simon J. Gerraty <sjg@void.crufty.net> + + * boot-strap: make bmake.1 too, so maninstall works. + +2007-10-14 Simon J. Gerraty <sjg@void.crufty.net> + + * Makefile.in (BMAKE_VERSION): bump version to 20071014 + * Merge with NetBSD make, pick up: + o revamped handling of defshell - configure no longer needs to + know the content of the shells array - apb + o stop Var_Subst modifying its input - apb + o avoid calling ParseTrackInput too often - dsl + +2007-10-11 Simon J. Gerraty <sjg@void.crufty.net> + + * Makefile.in (BMAKE_VERSION): bump version to 20071011 + * Merge with NetBSD make, pick up: + o fix Shell_Init for case that _BASENAME_DEFSHELL is absolute path. + + * sigcompat.c: some tweaks for HP-UX 11.x based on + patch from Tobias Nygren + + * configure.in: update handling of --with-defshell to match + new make behavior. --with-defshell=/usr/xpg4/bin/sh + will now do what one might hope - provided the chosen shell + behaves enough like sh. + +2007-10-08 Simon J. Gerraty <sjg@void.crufty.net> + + * Makefile.in (BMAKE_VERSION): bump to 20071008 + * Merge with NetBSD make, pick up: + o .MAKE.JOB.PREFIX - control the token output before jobs - sjg + o .export/.MAKE.EXPORTED - export of variables - sjg + o .MAKE.MAKEFILES - track all makefiles read - sjg + o performance improvements - dsl + o revamp parallel job scheduling - dsl + +2006-07-28 Simon J. Gerraty <sjg@void.crufty.net> + + * Makefile.in (BMAKE_VERSION): bump to 20060728 + * Merge with NetBSD make, pick up: + o extra debug info during variable and cond processing - sjg + o shell definition now covers newline - rillig + o minor mem leak in PrintOnError - sjg + +2006-05-11 Simon J. Gerraty <sjg@void.crufty.net> + + * Makefile.in (BMAKE_VERSION): bump to 20060511 + * Merge with NetBSD make, pick up: + o more memory leaks - coverity + o possible overflow in ArchFindMember - coverity + o extract variable modifier code out of Var_Parse() + so it can be called recursively - sjg + o unit-tests/moderrs - sjg + +2006-04-12 Simon J. Gerraty <sjg@void.crufty.net> + + * Makefile.in (BMAKE_VERSION): bump to 20060412 + * Merge with NetBSD make, pick up: + o fixes for some memory leaks - coverity + o only read first sys.mk etc when searching sysIncPath - sjg + + * main.c (ReadMakefile): remove hack for __INTERIX that prevented + setting ${MAKEFILE} - OBATA Akio + +2006-03-18 Simon J. Gerraty <sjg@void.crufty.net> + + * Makefile.in (BMAKE_VERSION): bump to 20060318 + * Merge with NetBSD make, pick up: + o cleanup of job.c to remove remote handling, distcc is more + useful and this code was likely bit-rotting - dsl + o fix for :P modifier - sjg + * boot-strap: set default prefix to something reasonable + (for me anyway). + +2006-03-01 Simon J. Gerraty <sjg@void.crufty.net> + + * Makefile.in (BMAKE_VERSION): bump to 20060301 + * Merge with NetBSD make, pick up: + o make .WAIT apply recursively, document and test case - apb + o allow variable modifiers in a variable appear anywhere in + modifier list, document and test case - sjg + +2006-02-22 Simon J. Gerraty <sjg@void.crufty.net> + + * Makefile.in (BMAKE_VERSION): bump to 20060222 + * Merge with NetBSD make, pick up: + o improved job token handling - dsl + o SIG_DFL the correct signal before exec - dsl + o more debug info during parsing - dsl + o allow variable modifiers to be specified via variable - sjg + * boot-strap: explain why we died if no mksrc + +2005-11-05 Simon J. Gerraty <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * Makefile.in (BMAKE_VERSION): bump to 20051031 + * ranlib.h: skip on OSF too. + (NetBSD PR 31864) + +2005-10-10 Simon J. Gerraty <sjg@void.crufty.net> + + * Makefile.in (BMAKE_VERSION): bump to 20051002 + fix a silly typo + +2005-10-09 Simon J. Gerraty <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * Important to use .MAKE rather than MAKE. + +2005-03-15 Simon J. Gerraty <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * Makefile.in (accept test): use ${.MAKE:S,^./,${.CURDIR}/,} + so that './bmake -f Makefile test' works. + +2004-02-14 Simon J. Gerraty <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * arch.c: don't include ranlib.h on ELF systems + (thanks to Chuck Cranor <chuck@ece.cmu.edu>). + +2004-01-18 Simon J. Gerraty <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * boot-strap: Allow setting configure args on command line. + +2003-07-31 Simon J. Gerraty <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * 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 <grant@netbsd.org> + I may have missed some - since they are based on bmake-3.1.12 + +2002-12-03 Simon J. Gerraty <sjg@void.crufty.net> + + * 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 <hstenn@cisco.com>. + + * bmake.cat1: update the pre-formatted man page! + +2002-11-30 Simon J. Gerraty <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * 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 <sjg@void.crufty.net> + + * 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 <sjg@zen.crufty.net> + + * 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 <sjg@zen.crufty.net> + + * 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 <sjg@zen.quick.com.au> + + * 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 <sjg@zen.quick.com.au> + + * makefile.boot.in: Give bmake.boot a MAKESYSPATH that might work. + +Tue Jun 12 16:48:57 2001 Simon J. Gerraty <sjg@zen.quick.com.au> + + * 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 <sjg@zen.quick.com.au> + + * job.c (Job_CatchChildren): don't call Job_CatchOutput() here, + its the wrong "fix". + +Sat Jun 9 00:11:24 2001 Simon J. Gerraty <sjg@zen.quick.com.au> + + * 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 <sjg@zen.quick.com.au> + + * 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 <sjg@zen.quick.com.au> + + * 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 <sjg@zen.quick.com.au> + + * 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 <sjg@zen.quick.com.au> + + * 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 <sjg@zen.quick.com.au> + + * 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 <sjg@zen.quick.com.au> + + * 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 <sjg@zen.quick.com.au> + + * 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 <sjg@zen.quick.com.au> + + * Merged with NetBSD make + +Sat Jun 10 10:11:08 2000 Simon J. Gerraty <sjg@zen.quick.com.au> + + * Merged with NetBSD make + +Thu Jun 1 10:11:08 2000 Simon J. Gerraty <sjg@zen.quick.com.au> + + * Merged with NetBSD make + +Tue May 30 10:11:08 2000 Simon J. Gerraty <sjg@zen.quick.com.au> + + * Merged with NetBSD make + +Thu Apr 27 00:07:47 2000 Simon J. Gerraty <sjg@zen.quick.com.au> + + * 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 <sjg@zen.quick.com.au> + + * 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 <sjg@zen.quick.com.au> + + * 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 <sjg@zen.quick.com.au> + + * 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/20120704/FILES b/20120704/FILES new file mode 100644 index 0000000..397d3a2 --- /dev/null +++ b/20120704/FILES @@ -0,0 +1,121 @@ +FILES +ChangeLog +bmake.cat1 +boot-strap +bsd.after-import.mk +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 +make_malloc.c +make_malloc.h +config.h.in +configure +aclocal.m4 +configure.in +dir.c +dir.h +find_lib.sh +for.c +getopt.c +hash.c +hash.h +install-sh +job.c +job.h +meta.c +meta.h +dirname.c +realpath.c +strlcpy.c +strlist.c +strlist.h +stresep.c +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/lstPrev.c +lst.lib/lstRemove.c +lst.lib/lstReplace.c +lst.lib/lstSucc.c +machine.sh +main.c +make.1 +bmake.1 +make.c +make.h +make-bootstrap.sh.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/comment +unit-tests/cond1 +unit-tests/doterror +unit-tests/dotwait +unit-tests/error +unit-tests/export +unit-tests/export-all +unit-tests/forloop +unit-tests/forsubst +unit-tests/hash +unit-tests/misc +unit-tests/moderrs +unit-tests/modmatch +unit-tests/modmisc +unit-tests/modorder +unit-tests/modts +unit-tests/modword +unit-tests/phony-end +unit-tests/posix +unit-tests/qequals +unit-tests/sysv +unit-tests/ternary +unit-tests/test.exp +unit-tests/unexport +unit-tests/unexport-env +unit-tests/varcmd diff --git a/20120704/Makefile.in b/20120704/Makefile.in new file mode 100644 index 0000000..5a1f004 --- /dev/null +++ b/20120704/Makefile.in @@ -0,0 +1,187 @@ +# $NetBSD: Makefile,v 1.56 2012/05/30 21:54:23 sjg Exp $ +# @(#)Makefile 5.2 (Berkeley) 12/28/90 + +# $Id: Makefile.in,v 1.168 2012/07/05 04:10:23 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+= strlist.c +SRCS+= make_malloc.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 +SRCS += lstPrev.c + +# you can use this Makefile if you have an earlier version of bmake. +prefix= @prefix@ +srcdir= @srcdir@ +CC?= @CC@ + +# Base version on src date +MAKE_VERSION= 20120704 +MACHINE=@machine@ +MACHINE_ARCH=@machine_arch@ +DEFAULT_SYS_PATH = @default_sys_path@ + +CPPFLAGS+= @CPPFLAGS@ +CFLAGS+= ${CPPFLAGS} +CFLAGS+= -D_PATH_DEFSYSPATH=\"${DEFAULT_SYS_PATH}\" +CFLAGS+= -I. -I${srcdir} @DEFS@ ${XDEFS} -DMAKE_NATIVE +CFLAGS+= ${CFLAGS_${.TARGET:T}} +CFLAGS+= ${COPTS.${.ALLSRC:M*.c:T:u}} +COPTS.main.c+= "-DMAKE_VERSION=\"${MAKE_VERSION}\"" +LDFLAGS= @LDFLAGS@ +LIBOBJS= @LIBOBJS@ +LDADD= @LIBS@ + +.if !empty(LIBOBJS) +SRCS+= ${LIBOBJS:T:.o=.c} +.endif + +USE_META = @use_meta@ +.if ${USE_META} != "no" +SRCS+= meta.c +CPPFLAGS+= -DUSE_META +FILEMON_H ?= @filemon_h@ +.if exists(${FILEMON_H}) && ${FILEMON_H:T} == "filemon.h" +COPTS.meta.c += -DHAVE_FILEMON_H -I${FILEMON_H:H} +.endif +.endif + +.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=4 +.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}\"" +MANTARGET=cat +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 defined(.PARSEDIR) +.if make(obj) || make(clean) +SUBDIR+= unit-tests +.endif +.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 's/^.Nx/NetBSD/' -e '/^.Nm/s/make/${PROG}/' -e '/^.Sh HISTORY/,$$d' ${srcdir}/make.1 > $@ + @(echo ".Sh HISTORY"; \ + echo ".Nm"; \ + echo "is derived from NetBSD"; \ + echo ".Xr make 1 ."; \ + echo It uses autoconf to facilitate portability to other platforms.) >> $@ + +.endif + +.if !empty(isBSD44:M${OS}) +.if "${OS}" != "NetBSD" +MAN1=${MAN} +.endif +MANTARGET?=man +.endif + +MANTARGET?= cat +MANDEST?= ${MANDIR}/${MANTARGET}1 + +.if ${MANTARGET} == "cat" +_mfromdir=${srcdir} +.endif + +.if exists(${srcdir}/../Makefile.inc) +.include "${srcdir}/../Makefile.inc" +.endif +.-include <bsd.prog.mk> +# 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(.PARSEDIR) +.if defined(SUBDIR) && !target(${SUBDIR:[1]}) +.-include <bsd.subdir.mk> +.endif +.endif + +CPPFLAGS+= -DMAKE_NATIVE +COPTS.var.c += -Wno-cast-qual +COPTS.job.c += -Wno-format-nonliteral +COPTS.parse.c += -Wno-format-nonliteral +COPTS.var.c += -Wno-format-nonliteral + +# Force these +BINDIR= ${prefix}/bin +MANDIR= ${prefix}/man + +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 + +.ifdef TOOLDIR +# this is a native netbsd build, +# use libutil rather than the local emalloc etc. +CPPFLAGS+= -DUSE_EMALLOC +LDADD+=-lutil +DPADD+=${LIBUTIL} +.endif + +# A simple unit-test driver to help catch regressions +accept test: + cd ${.CURDIR}/unit-tests && MAKEFLAGS= ${.MAKE} -r -m / TEST_MAKE=${TEST_MAKE:U${.OBJDIR}/${PROG:T}} ${.TARGET} diff --git a/20120704/PSD.doc/Makefile b/20120704/PSD.doc/Makefile new file mode 100644 index 0000000..8e1f1fa --- /dev/null +++ b/20120704/PSD.doc/Makefile @@ -0,0 +1,8 @@ +# $NetBSD: Makefile,v 1.2 1995/06/14 15:20:23 christos Exp $ +# @(#)Makefile 8.1 (Berkeley) 8/14/93 + +DIR= psd/12.make +SRCS= tutorial.ms +MACROS= -ms + +.include <bsd.doc.mk> diff --git a/20120704/PSD.doc/tutorial.ms b/20120704/PSD.doc/tutorial.ms new file mode 100644 index 0000000..c1a6444 --- /dev/null +++ b/20120704/PSD.doc/tutorial.ms @@ -0,0 +1,3773 @@ +.\" $NetBSD: tutorial.ms,v 1.11 2011/08/18 15:19:30 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't 5u' +.sp -1 +\h'50u' +.sp -1 +\D't 3u' +.sp -1 +.sp 7u +\h'53u' +\d\D'p -0.19i 0.0i 0.0i -0.13i 0.30i 0.0i 0.0i 0.13i' +.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' +.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 <file> +.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 = <command.mk> + +#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 <makedepend.mk> +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 = <files in the library> +# +# fish.a: fish.a($(OBJECTS)) MAKELIB +# +# + +#ifndef _MAKELIB_MK +_MAKELIB_MK = + +#include <po.mk> + +\&.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 <shx.mk> +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/20120704/README b/20120704/README new file mode 100644 index 0000000..fb688a3 --- /dev/null +++ b/20120704/README @@ -0,0 +1,47 @@ + 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/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/20120704/aclocal.m4 b/20120704/aclocal.m4 new file mode 100644 index 0000000..2adafba --- /dev/null +++ b/20120704/aclocal.m4 @@ -0,0 +1,77 @@ +dnl RCSid: +dnl $Id: aclocal.m4,v 1.5 2003/03/06 21:21:30 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 <stdlib.h> +], +[ +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/20120704/arch.c b/20120704/arch.c new file mode 100644 index 0000000..943f41e --- /dev/null +++ b/20120704/arch.c @@ -0,0 +1,1403 @@ +/* $NetBSD: arch.c,v 1.63 2012/06/12 19:21:50 joerg 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.63 2012/06/12 19:21:50 joerg Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)arch.c 8.2 (Berkeley) 1/2/94"; +#else +__RCSID("$NetBSD: arch.c,v 1.63 2012/06/12 19:21:50 joerg 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<name> 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 <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/param.h> +#include <ctype.h> +#ifdef HAVE_AR_H +#include <ar.h> +#else +struct ar_hdr { + char ar_name[16]; /* name */ + char ar_date[12]; /* modification time */ + char ar_uid[6]; /* user id */ + char ar_gid[6]; /* group id */ + char ar_mode[8]; /* octal file permissions */ + char ar_size[10]; /* size in bytes */ +#ifndef ARFMAG +#define ARFMAG "`\n" +#endif + char ar_fmag[2]; /* consistency check */ +}; +#endif +#if defined(HAVE_RANLIB_H) && !(defined(__ELF__) || defined(NO_RANLIB)) +#include <ranlib.h> +#endif +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#ifdef HAVE_UTIME_H +#include <utime.h> +#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 <name, struct ar_hdr *> key/value pairs */ + char *fnametab; /* Extended name table strings */ + size_t fnamesize; /* Size of the string table */ +} Arch; + +static int ArchFindArchive(const void *, const void *); +#ifdef CLEANUP +static void ArchFree(void *); +#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 +#ifndef ARMAG +# define ARMAG "!<arch>\n" +#endif +#ifndef SARMAG +# define SARMAG 8 +#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(void *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; + void *freeIt; + char *result; + + result = Var_Parse(cp, ctxt, TRUE, &length, &freeIt); + if (freeIt) + free(freeIt); + if (result == var_Error) { + return(FAILURE); + } else { + subLibName = TRUE; + } + + 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; + void *freeIt; + char *result; + + result = Var_Parse(cp, ctxt, TRUE, &length, &freeIt); + if (freeIt) + free(freeIt); + if (result == var_Error) { + return(FAILURE); + } else { + doSubst = TRUE; + } + + 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 = bmake_malloc(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 == NULL) { + free(buf); + return(FAILURE); + } else { + gn->type |= OP_ARCHV; + (void)Lst_AtEnd(nodeLst, 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 = bmake_malloc(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 = bmake_realloc(nameBuf, sz = nsz * 2); + + snprintf(nameBuf, sz, "%s(%s)", libName, member); + free(member); + gn = Targ_FindNode(nameBuf, TARG_CREATE); + if (gn == NULL) { + 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, gn); + } + } + Lst_Destroy(members, NULL); + free(nameBuf); + } else { + size_t sz = strlen(libName) + strlen(memName) + 3; + nameBuf = bmake_malloc(sz); + snprintf(nameBuf, sz, "%s(%s)", libName, memName); + gn = Targ_FindNode(nameBuf, TARG_CREATE); + free(nameBuf); + if (gn == NULL) { + 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, 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(const void *ar, const void *archName) +{ + return (strcmp(archName, ((const 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, archive, ArchFindArchive); + if (ln != NULL) { + 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]; + size_t 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 = bmake_malloc(sizeof(Arch)); + ar->name = bmake_strdup(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/<namelen>, with name as the + * first <namelen> 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)) { + fprintf(debug_file, "ArchStat: Extended format entry for %s\n", memName); + } + } +#endif + + he = Hash_CreateEntry(&ar->members, memName, NULL); + Hash_SetValue(he, bmake_malloc(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, 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 "/<offset>", 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)) { + fprintf(debug_file, "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 = bmake_malloc(size); + ar->fnamesize = size; + + if (fread(ar->fnametab, size, 1, arch) != 1) { + if (DEBUG(ARCH)) { + fprintf(debug_file, "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)) { + fprintf(debug_file, "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)) { + fprintf(debug_file, "Could not parse SVR4 name %s\n", name); + } + return 2; + } + if (entry >= ar->fnamesize) { + if (DEBUG(ARCH)) { + fprintf(debug_file, "SVR4 entry offset %s is greater than %lu\n", + name, (u_long)ar->fnamesize); + } + return 2; + } + + if (DEBUG(ARCH)) { + fprintf(debug_file, "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]; + size_t 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/<namelen>, with name as the + * first <namelen> 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 + 1]; + + 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)) { + fprintf(debug_file, "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 MAKE_ATTR_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)) != NULL) { + 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 = bmake_malloc(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->cmgn->mtime). + * 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->cmgn == NULL) || + (gn->mtime > now) || + (gn->cmgn != NULL && gn->mtime < gn->cmgn->mtime)) { + 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)) { + fprintf(debug_file, "%s modified %s...", RANLIBMAG, Targ_FmtTime(modTimeTOC)); + } + oodate = (gn->cmgn == NULL || gn->cmgn->mtime > modTimeTOC); + } else { + /* + * A library w/o a table of contents is out-of-date + */ + if (DEBUG(ARCH) || DEBUG(MAKE)) { + fprintf(debug_file, "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[] = "!<arch>\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/20120704/bmake.1 b/20120704/bmake.1 new file mode 100644 index 0000000..0bb377a --- /dev/null +++ b/20120704/bmake.1 @@ -0,0 +1,2043 @@ +.\" $NetBSD: make.1,v 1.204 2012/04/24 20:12:16 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 April 24, 2012 +.Dt MAKE 1 +.Os +.Sh NAME +.Nm bmake +.Nd maintain program dependencies +.Sh SYNOPSIS +.Nm +.Op Fl BeikNnqrstWX +.Op Fl C Ar directory +.Op Fl D Ar variable +.Op Fl d Ar flags +.Op Fl f Ar makefile +.Op Fl I Ar directory +.Op Fl J Ar private +.Op Fl j Ar max_jobs +.Op Fl m Ar directory +.Op Fl T Ar file +.Op Fl V Ar variable +.Op Ar variable=value +.Op Ar target ... +.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 no +.Fl f Ar makefile +makefile option is given, +.Nm +will try to open +.Ql Pa makefile +then +.Ql Pa Makefile +in order to find the specifications. +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 "PMake \- A Tutorial" . +.Pp +.Nm +will prepend the contents of the +.Va MAKEFLAGS +environment variable to the command line arguments before parsing them. +.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 C Ar directory +Change to +.Ar directory +before reading the makefiles or doing anything else. +If multiple +.Fl C +options are specified, each is interpreted relative to the previous one: +.Fl C Pa / Fl C Pa etc +is equivalent to +.Fl C Pa /etc . +.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. +Unless the flags are preceded by +.Ql \- +they are added to the +.Va MAKEFLAGS +environment variable and will be processed by any child make processes. +By default, debugging information is printed to standard error, +but this can be changed using the +.Ar F +debugging flag. +The debugging output is always unbuffered; in addition, if debugging +is enabled but debugging output is not directed to standard output, +then the standard output is line buffered. +.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 current working directory. +.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 Ns Oo Sy \&+ Oc Ns Ar filename +Specify where debugging output is written. +This must be the last flag, because it consumes the remainder of +the argument. +If the character immediately after the +.Ql F +flag is +.Ql \&+ , +then the file will be opened in append mode; +otherwise the file will be overwritten. +If the file name is +.Ql stdout +or +.Ql stderr +then debugging output will be written to the +standard output or standard error output file descriptors respectively +(and the +.Ql \&+ +option has no effect). +Otherwise, the output will be written to the named file. +If the file name ends +.Ql .%d +then the +.Ql %d +is replaced by the pid. +.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 l +Print commands in Makefiles regardless of whether or not they are prefixed by +.Ql @ +or other "quiet" flags. +Also known as "loud" behavior. +.It Ar M +Print debugging information about "meta" mode decisions about targets. +.It Ar m +Print debugging information about making targets, including modification +dates. +.It Ar n +Don't delete the temporary command scripts created when running commands. +These temporary scripts are created in the directory +referred to by the +.Ev TMPDIR +environment variable, or in +.Pa /tmp +if +.Ev TMPDIR +is unset or set to the empty string. +The temporary scripts are created by +.Xr mkstemp 3 , +and have names of the form +.Pa makeXXXXXX . +.Em NOTE : +This can create many files in +.Ev TMPDIR +or +.Pa /tmp , +so use with care. +.It Ar p +Print debugging information about makefile parsing. +.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. +The value is saved in +.Va .MAKE.JOBS . +Turns compatibility mode off, unless the +.Ar B +flag is also specified. +When compatibility mode is off, all commands associated with a +target are executed in a single shell invocation as opposed to the +traditional one shell invocation per line. +This can break traditional scripts which change directories on each +command invocation and then expect to start with a fresh environment +on the next line. +It is more efficient to correct the scripts rather than turn backwards +compatibility on. +.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 +.Dq 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. +.Em NOTE : +References to undefined variables are +.Em not +expanded. +This can cause problems when variable modifiers are used. +.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 +If the variable name contains a dollar, then the name itself is expanded first. +This allows almost arbitrary variable names, however names containing dollar, +braces, parenthesis, or whitespace are really best avoided! +.Pp +If the result of expanding a variable contains a dollar sign +.Pq Ql \&$ +the string is expanded again. +.Pp +Variable substitution occurs at three distinct times, depending on where +the variable is being used. +.Bl -enum +.It +Variables in dependency lines are expanded as the line is read. +.It +Variables in shell commands are expanded when the shell command is +executed. +.It +.Dq .for +loop index variables are expanded on each loop iteration. +Note that other variables are not expanded inside loops so +the following example code: +.Bd -literal -offset indent + +.Dv .for i in 1 2 3 +a+= ${i} +j= ${i} +b+= ${j} +.Dv .endfor + +all: + @echo ${a} + @echo ${b} + +.Ed +will print: +.Bd -literal -offset indent +1 2 3 +3 3 3 + +.Ed +Because while ${a} contains +.Dq 1 2 3 +after the loop is executed, ${b} +contains +.Dq ${j} ${j} ${j} +which expands to +.Dq 3 3 3 +since after the loop completes ${j} contains +.Dq 3 . +.El +.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 +In suffix-transformation rules, the name/path of the source from which the +target is to be transformed (the +.Dq implied +source); also known as +.Ql Va \&\*[Lt] . +It is not defined in explicit rules. +.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 target, 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 built-in 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 compatibility +.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 Va .MAKE.DEPENDFILE +Names the makefile (default +.Ql Pa .depend ) +from which generated dependencies are read. +.It Va .MAKE.EXPORTED +The list of variables exported by +.Nm . +.It Va .MAKE.JOBS +The argument to the +.Fl j +option. +.It Va .MAKE.JOB.PREFIX +If +.Nm +is run with +.Ar j +then output for each target is prefixed with a token +.Ql --- target --- +the first part of which can be controlled via +.Va .MAKE.JOB.PREFIX . +.br +For example: +.Li .MAKE.JOB.PREFIX=${.newline}---${.MAKE:T}[${.MAKE.PID}] +would produce tokens like +.Ql ---make[1234] target --- +making it easier to track the degree of parallelism being achieved. +.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 .MAKE.LEVEL +The recursion depth of +.Nm . +The initial instance of +.Nm +will be 0, and an incremented value is put into the environment +to be seen by the next generation. +This allows tests like: +.Li .if ${.MAKE.LEVEL} == 0 +to protect things which should only be evaluated in the initial instance of +.Nm . +.It Va .MAKE.MAKEFILE_PREFERENCE +The ordered list of makefile names +(default +.Ql Pa makefile , +.Ql Pa Makefile ) +that +.Nm +will look for. +.It Va .MAKE.MAKEFILES +The list of makefiles read by +.Nm , +which is useful for tracking dependencies. +Each makefile is recorded only once, regardless of the number of times read. +.It Va .MAKE.MODE +Processed after reading all makefiles. +Can affect the mode that +.Nm +runs in. +It can contain a number of keywords: +.Bl -hang -width ignore-cmd +.It Pa compat +Like +.Fl B , +puts +.Nm +into "compat" mode. +.It Pa meta +Puts +.Nm +into "meta" mode, where meta files are created for each target +to capture the command run, the output generated and if +.Xr filemon 4 +is available, the system calls which are of interest to +.Nm . +The captured output can be very useful when diagnosing errors. +.It Pa curdirOk= Ar bf +Normally +.Nm +will not create .meta files in +.Ql Va .CURDIR . +This can be overridden by setting +.Va bf +to a value which represents True. +.It Pa env +For debugging, it can be useful to inlcude the environment +in the .meta file. +.It Pa verbose +If in "meta" mode, print a clue about the target being built. +This is useful if the build is otherwise running silently. +The message printed the value of: +.Va .MAKE.META.PREFIX . +.It Pa ignore-cmd +Some makefiles have commands which are simply not stable. +This keyword causes them to be ignored for +determining whether a target is out of date in "meta" mode. +See also +.Ic .NOMETA_CMP . +.It Pa silent= Ar bf +If +.Va bf +is True, when a .meta file is created, mark the target +.Ic .SILENT . +.El +.It Va .MAKE.META.BAILIWICK +In "meta" mode, provides a list of prefixes which +match the directories controlled by +.Nm . +If a file that was generated outside of +.Va .OBJDIR +but within said bailiwick is missing, +the current target is considered out-of-date. +.It Va .MAKE.META.CREATED +In "meta" mode, this variable contains a list of all the meta files +updated. +If not empty, it can be used to trigger processing of +.Va .MAKE.META.FILES . +.It Va .MAKE.META.FILES +In "meta" mode, this variable contains a list of all the meta files +used (updated or not). +This list can be used to process the meta files to extract dependency +information. +.It Va .MAKE.META.PREFIX +Defines the message printed for each meta file updated in "meta verbose" mode. +The default value is: +.Dl Building ${.TARGET:H:tA}/${.TARGET:T} +.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.PID +The process-id of +.Nm . +.It Va .MAKE.PPID +The parent process-id of +.Nm . +.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:S,^/usr/src,/var/obj,} +may be used. +This is especially useful with +.Ql Ev MAKEOBJDIR . +.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. +If you want to retain their current values, assign them to a variable +using assignment with expansion: +.Pq Ql Cm \&:= . +.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. +.It Ev .TARGETS +The list of targets explicitly specified on the command line, if any. +.It Ev VPATH +Colon-separated +.Pq Dq \&: +lists of directories that +.Nm +will search for files. +The variable is supported for compatibility with old make programs only, +use +.Ql Va .PATH +instead. +.El +.Ss Variable modifiers +Variable expansion may be modified to select or modify each word of the +variable (where a +.Dq 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 . +.Pp +A set of modifiers can be specified via a variable, as follows: +.Pp +.Dl modifier_variable=modifier[:...] +.Dl ${variable:${modifier_variable}[:...]} +.Pp +In this case the first modifier in the modifier_variable does not +start with a colon, since that must appear in the referencing +variable. +If any of the modifiers in the modifier_variable contain a dollar sign +.Pq Ql $ , +these must be doubled to avoid early expansion. +.Pp +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 Oo Oc ) +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 \&:gmtime +The value is a format string for +.Xr strftime 3 , +using the current +.Xr gmtime 3 . +.It Cm \&:hash +Compute a 32bit hash of the value and encode it as hex digits. +.It Cm \&:localtime +The value is a format string for +.Xr strftime 3 , +using the current +.Xr localtime 3 . +.It Cm \&:tA +Attempt to convert variable to an absolute path using +.Xr realpath 3 , +if that fails, the value is unchanged. +.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. +The common escapes (including octal numeric codes), work as expected. +.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 No \&/ Ar new_string No \&/ Op Cm 1gW +.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 No \&/ Ar replacement No \&/ Op Cm 1gW +.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 name (not its value), when parsed as a .if conditional +expression, evaluates to true, return as its value the +.Ar true_string , +otherwise return the +.Ar false_string . +Since the variable name is used as the expression, \&:\&? must be the +first modifier after the variable name itself - which will, of course, +usually contain variable expansions. +A common error is trying to use expressions like +.Dl ${NUMBERS:M42:?match:no} +which actually tests defined(NUMBERS), +to determine is any words match "42" you need to use something like: +.Dl ${"${NUMBERS:M42}" != \&"\&":?match:no} . +.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 @ Ar string Cm @ +.Sm on +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.}@} +.Pp +However a single character varaiable is often more readable: +.Dl ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@} +.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. +In order for this modifier to work, the name (node) must at least have +appeared on the rhs of a dependency. +.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 set a variable when shell commands +are being parsed. +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. +.Pp +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 .error Ar message +The message is printed along with the name of the makefile and line number, +then +.Nm +will exit. +.It Ic .export Ar variable ... +Export the specified global variable. +If no variable list is provided, all globals are exported +except for internal variables (those that start with +.Ql \&. ) . +This is not affected by the +.Fl X +flag, so should be used with caution. +For compatibility with other +.Nm +programs +.Ql export variable=value +is also accepted. +.Pp +Appending a variable name to +.Va .MAKE.EXPORTED +is equivalent to exporting a variable. +.It Ic .export-env Ar variable ... +The same as +.Ql .export , +except that the variable is not appended to +.Va .MAKE.EXPORTED . +This allows exporting a value to the environment which is different from that +used by +.Nm +internally. +.It Ic .info Ar message +The message is printed along with the name of the makefile and line number. +.It Ic .undef Ar variable +Un-define the specified global variable. +Only global variables may be un-defined. +.It Ic .unexport Ar variable ... +The opposite of +.Ql .export . +The specified global +.Va variable +will be removed from +.Va .MAKE.EXPORTED . +If no variable list is provided, all globals are unexported, +and +.Va .MAKE.EXPORTED +deleted. +.It Ic .unexport-env +Unexport all globals previously exported and +clear the environment inherited from the parent. +This operation will cause a memory leak of the original environment, +so should be used sparingly. +Testing for +.Va .MAKE.LEVEL +being 0, would make sense. +Also note that any variables which originated in the parent environment +should be explicitly preserved if desired. +For example: +.Bd -literal -offset indent +.Li .if ${.MAKE.LEVEL} == 0 +PATH := ${PATH} +.Li .unexport-env +.Li .export PATH +.Li .endif +.Pp +.Ed +Would result in an environment containing only +.Ql Ev PATH , +which is the minimal useful environment. +Actually +.Ql Ev .MAKE.LEVEL +will also be pushed into the new environment. +.It Ic .warning Ar message +The message prefixed by +.Ql Pa warning: +is printed along with the name of the makefile and line number. +.It Ic \&.if Oo \&! Oc Ns Ar expression Op Ar operator expression ... +Test the value of an expression. +.It Ic .ifdef Oo \&! Oc Ns Ar variable Op Ar operator variable ... +Test the value of a variable. +.It Ic .ifndef Oo \&! Oc Ns Ar variable Op Ar operator variable ... +Test the value of a variable. +.It Ic .ifmake Oo \&! Oc Ns Ar target Op Ar operator target ... +Test the target being built. +.It Ic .ifnmake Oo \&! Ns Oc Ar target Op Ar operator target ... +Test the target being built. +.It Ic .else +Reverse the sense of the last conditional. +.It Ic .elif Oo \&! Ns Oc Ar expression Op Ar operator expression ... +A combination of +.Ql Ic .else +followed by +.Ql Ic .if . +.It Ic .elifdef Oo \&! Oc Ns Ar variable Op Ar operator variable ... +A combination of +.Ql Ic .else +followed by +.Ql Ic .ifdef . +.It Ic .elifndef Oo \&! Oc Ns Ar variable Op Ar operator variable ... +A combination of +.Ql Ic .else +followed by +.Ql Ic .ifndef . +.It Ic .elifmake Oo \&! Oc Ns Ar target Op Ar operator target ... +A combination of +.Ql Ic .else +followed by +.Ql Ic .ifmake . +.It Ic .elifnmake Oo \&! Oc Ns Ar target Op Ar operator target ... +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 expressions, and it encounters +a (white-space separated) word it doesn't recognize, either the +.Dq make +or +.Dq defined +expression is applied to it, depending on the form of the conditional. +If the form is +.Ql Ic .ifdef , +.Ql Ic .ifndef , +or +.Ql Ic .if +the +.Dq defined +expression is applied. +Similarly, if the form is +.Ql Ic .ifmake +or +.Ql Ic .ifnmake , the +.Dq 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 Ic \&.for Ar variable Oo Ar variable ... Oc Ic in Ar expression +.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 .META +Create a meta file for the target, even if it is flagged as +.Ic .PHONY , +.Ic .MAKE , +or +.Ic .SPECIAL . +Usage in conjunction with +.Ic .MAKE +is the most likely case. +In "meta" mode, the target is out-of-date if the meta file is missing. +.It Ic .NOMETA +Do not create a meta file for the target. +Meta files are also not created for +.Ic .PHONY , +.Ic .MAKE , +or +.Ic .SPECIAL +targets. +.It Ic .NOMETA_CMP +Ignore differences in commands when deciding if target is out of date. +This is useful if the command contains a value which always changes. +If the number of commands change, though, the target will still be out of date. +.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. +Suffix-transformation rules are not applied to +.Ic .PHONY +targets. +.It Ic .PRECIOUS +When +.Nm +is interrupted, it normally 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. +Since the dependents of files are not made until the file itself +could be made, this also stops the dependents being built unless they +are needed for another branch of the dependency tree. +So given: +.Bd -literal +x: a .WAIT b + echo x +a: + echo a +b: b1 + echo b +b1: + echo b1 + +.Ed +the output is always +.Ql a , +.Ql b1 , +.Ql b , +.Ql x . +.br +The ordering imposed by +.Ic .WAIT +is only relevant for parallel makes. +.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 .ERROR +Any command lines attached to this target are executed when another target fails. +The +.Ic .ERROR_TARGET +variable is set to the target that failed. +See also +.Ic MAKE_PRINT_VAR_ON_ERROR . +.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. +This ordering does not add targets to the list of targets to be made. +Since the dependents of a target do not get built until the target itself +could be built, unless +.Ql a +is built by another part of the dependency graph, +the following is a dependency loop: +.Bd -literal +\&.ORDER: b a +b: a +.Ed +.Pp +The ordering imposed by +.Ic .ORDER +is only relevant for parallel makes. +.\" 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. +.It Ar newline +The string literal to pass the shell that results in a single newline +character when used outside of any quoting characters. +.El +Example: +.Bd -literal +\&.SHELL: name=ksh path=/bin/ksh hasErrCtl=true \e + check="set \-e" ignore="set +e" \e + echo="set \-v" quiet="set +v" filter="set +v" \e + echoFlag=v errFlag=e newline="'\en'" +.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. +It allows the creation of suffix-transformation rules. +.Pp +Example: +.Bd -literal +\&.SUFFIXES: .o +\&.c.o: + cc \-o ${.TARGET} \-c ${.IMPSRC} +.Ed +.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 , +.Ev PWD , +and +.Ev TMPDIR . +.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 COMPATIBILITY +The basic make syntax is compatible between different versions of make, +however the special variables, variable modifiers and conditionals are not. +.Pp +The way that parallel makes are scheduled changed in +NetBSD 4.0 +so that .ORDER and .WAIT apply recursively to the dependent nodes. +The algorithms used may change again in the future. +.Pp +The way that .for loop variables are substituted changed after +NetBSD 5.0 +so that they still appear to be variable expansions. +In particular this stops them being treated as syntax, and removes some +obscure problems using them in .if statements. +.Pp +Unlike other +.Nm +programs, this implementation by default executes all commands for a given +target using a single shell invocation. +This is done for both efficiency and to simplify error handling in remote +command invocations. +Typically this is transparent to the user, unless the target commands change +the current working directory using +.Dq cd +or +.Dq chdir . +To be compatible with Makefiles that do this, one can use +.Fl B +to disable this behavior. +.Sh SEE ALSO +.Xr mkdep 1 +.Sh HISTORY +.Nm +is derived from NetBSD +.Xr make 1 . +It uses autoconf to facilitate portability to other platforms. diff --git a/20120704/bmake.cat1 b/20120704/bmake.cat1 new file mode 100644 index 0000000..7624c43 --- /dev/null +++ b/20120704/bmake.cat1 @@ -0,0 +1,1305 @@ +MAKE(1) NetBSD General Commands Manual MAKE(1) + +NNAAMMEE + bbmmaakkee -- maintain program dependencies + +SSYYNNOOPPSSIISS + bbmmaakkee [--BBeeiikkNNnnqqrrssttWWXX] [--CC _d_i_r_e_c_t_o_r_y] [--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 no --ff _m_a_k_e_f_i_l_e makefile option is + given, bbmmaakkee will try to open `_m_a_k_e_f_i_l_e' then `_M_a_k_e_f_i_l_e' in order to find + the specifications. 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 _P_M_a_k_e _- _A + _T_u_t_o_r_i_a_l. + + bbmmaakkee will prepend the contents of the _M_A_K_E_F_L_A_G_S environment variable to + the command line arguments before parsing them. + + 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. + + --CC _d_i_r_e_c_t_o_r_y + Change to _d_i_r_e_c_t_o_r_y before reading the makefiles or doing any- + thing else. If multiple --CC options are specified, each is inter- + preted relative to the previous one: --CC _/ --CC _e_t_c is equivalent to + --CC _/_e_t_c. + + --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. Unless the flags are preceded by + `-' they are added to the _M_A_K_E_F_L_A_G_S environment variable and will + be processed by any child make processes. By default, debugging + information is printed to standard error, but this can be changed + using the _F debugging flag. The debugging output is always + unbuffered; in addition, if debugging is enabled but debugging + output is not directed to standard output, then the standard out- + put is line buffered. _F_l_a_g_s is one or more of the following: + + _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 current working direc- + tory. + + _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[++]_f_i_l_e_n_a_m_e + Specify where debugging output is written. This must be + the last flag, because it consumes the remainder of the + argument. If the character immediately after the `F' + flag is `+', then the file will be opened in append mode; + otherwise the file will be overwritten. If the file name + is `stdout' or `stderr' then debugging output will be + written to the standard output or standard error output + file descriptors respectively (and the `+' option has no + effect). Otherwise, the output will be written to the + named file. If the file name ends `.%d' then the `%d' is + replaced by the pid. + + _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. + + _l Print commands in Makefiles regardless of whether or not + they are prefixed by `@' or other "quiet" flags. Also + known as "loud" behavior. + + _M Print debugging information about "meta" mode decisions + about targets. + + _m Print debugging information about making targets, includ- + ing modification dates. + + _n Don't delete the temporary command scripts created when + running commands. These temporary scripts are created in + the directory referred to by the TMPDIR environment vari- + able, or in _/_t_m_p if TMPDIR is unset or set to the empty + string. The temporary scripts are created by mkstemp(3), + and have names of the form _m_a_k_e_X_X_X_X_X_X. _N_O_T_E: This can + create many files in TMPDIR or _/_t_m_p, so use with care. + + _p Print debugging information about makefile parsing. + + _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. The value is saved in _._M_A_K_E_._J_O_B_S. Turns compati- + bility mode off, unless the _B flag is also specified. When com- + patibility mode is off, all commands associated with a target are + executed in a single shell invocation as opposed to the tradi- + tional one shell invocation per line. This can break traditional + scripts which change directories on each command invocation and + then expect to start with a fresh environment on the next line. + It is more efficient to correct the scripts rather than turn + backwards compatibility on. + + --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. _N_O_T_E: References to undefined variables are + _n_o_t expanded. This can cause problems when variable modifiers + are used. + + !!== 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. + + If the variable name contains a dollar, then the name itself is expanded + first. This allows almost arbitrary variable names, however names con- + taining dollar, braces, parenthesis, or whitespace are really best + avoided! + + If the result of expanding a variable contains a dollar sign (`$') the + string is expanded again. + + Variable substitution occurs at three distinct times, depending on where + the variable is being used. + + 1. Variables in dependency lines are expanded as the line is read. + + 2. Variables in shell commands are expanded when the shell command is + executed. + + 3. ``.for'' loop index variables are expanded on each loop iteration. + Note that other variables are not expanded inside loops so the fol- + lowing example code: + + + .for i in 1 2 3 + a+= ${i} + j= ${i} + b+= ${j} + .endfor + + all: + @echo ${a} + @echo ${b} + + will print: + + 1 2 3 + 3 3 3 + + Because while ${a} contains ``1 2 3'' after the loop is executed, + ${b} contains ``${j} ${j} ${j}'' which expands to ``3 3 3'' since + after the loop completes ${j} contains ``3''. + + 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 In suffix-transformation rules, the name/path of the + source from which the target is to be transformed (the + ``implied'' source); also known as `_<'. It is not + defined in explicit rules. + + _._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 target, 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 bbuuiilltt--iinn 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. Refer + to the description of `PWD' for more details. + + MAKE The name that bbmmaakkee was executed with (_a_r_g_v_[_0_]). For + compatibility 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. + + _._M_A_K_E_._D_E_P_E_N_D_F_I_L_E + Names the makefile (default `_._d_e_p_e_n_d') from which gener- + ated dependencies are read. + + _._M_A_K_E_._E_X_P_O_R_T_E_D The list of variables exported by bbmmaakkee. + + _._M_A_K_E_._J_O_B_S The argument to the --jj option. + + _._M_A_K_E_._J_O_B_._P_R_E_F_I_X + If bbmmaakkee is run with _j then output for each target is + prefixed with a token `--- target ---' the first part of + which can be controlled via _._M_A_K_E_._J_O_B_._P_R_E_F_I_X. + For example: + .MAKE.JOB.PREFIX=${.newline}---${.MAKE:T}[${.MAKE.PID}] + would produce tokens like `---make[1234] target ---' mak- + ing it easier to track the degree of parallelism being + achieved. + + 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_._L_E_V_E_L The recursion depth of bbmmaakkee. The initial instance of + bbmmaakkee will be 0, and an incremented value is put into the + environment to be seen by the next generation. This + allows tests like: .if ${.MAKE.LEVEL} == 0 to protect + things which should only be evaluated in the initial + instance of bbmmaakkee. + + _._M_A_K_E_._M_A_K_E_F_I_L_E___P_R_E_F_E_R_E_N_C_E + The ordered list of makefile names (default `_m_a_k_e_f_i_l_e', + `_M_a_k_e_f_i_l_e') that bbmmaakkee will look for. + + _._M_A_K_E_._M_A_K_E_F_I_L_E_S + The list of makefiles read by bbmmaakkee, which is useful for + tracking dependencies. Each makefile is recorded only + once, regardless of the number of times read. + + _._M_A_K_E_._M_O_D_E Processed after reading all makefiles. Can affect the + mode that bbmmaakkee runs in. It can contain a number of key- + words: + + _c_o_m_p_a_t Like --BB, puts bbmmaakkee into "compat" mode. + + _m_e_t_a Puts bbmmaakkee into "meta" mode, where meta files + are created for each target to capture the + command run, the output generated and if + filemon(4) is available, the system calls + which are of interest to bbmmaakkee. The captured + output can be very useful when diagnosing + errors. + + _c_u_r_d_i_r_O_k_= _b_f Normally bbmmaakkee will not create .meta files + in `_._C_U_R_D_I_R'. This can be overridden by set- + ting _b_f to a value which represents True. + + _e_n_v For debugging, it can be useful to inlcude + the environment in the .meta file. + + _v_e_r_b_o_s_e If in "meta" mode, print a clue about the + target being built. This is useful if the + build is otherwise running silently. The + message printed the value of: + _._M_A_K_E_._M_E_T_A_._P_R_E_F_I_X. + + _i_g_n_o_r_e_-_c_m_d Some makefiles have commands which are simply + not stable. This keyword causes them to be + ignored for determining whether a target is + out of date in "meta" mode. See also + ..NNOOMMEETTAA__CCMMPP. + + _s_i_l_e_n_t_= _b_f If _b_f is True, when a .meta file is created, + mark the target ..SSIILLEENNTT. + + _._M_A_K_E_._M_E_T_A_._B_A_I_L_I_W_I_C_K + In "meta" mode, provides a list of prefixes which match + the directories controlled by bbmmaakkee. If a file that was + generated outside of _._O_B_J_D_I_R but within said bailiwick is + missing, the current target is considered out-of-date. + + _._M_A_K_E_._M_E_T_A_._C_R_E_A_T_E_D + In "meta" mode, this variable contains a list of all the + meta files updated. If not empty, it can be used to + trigger processing of _._M_A_K_E_._M_E_T_A_._F_I_L_E_S. + + _._M_A_K_E_._M_E_T_A_._F_I_L_E_S + In "meta" mode, this variable contains a list of all the + meta files used (updated or not). This list can be used + to process the meta files to extract dependency informa- + tion. + + _._M_A_K_E_._M_E_T_A_._P_R_E_F_I_X + Defines the message printed for each meta file updated in + "meta verbose" mode. The default value is: + Building ${.TARGET:H:tA}/${.TARGET:T} + + _._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_I_D The process-id of bbmmaakkee. + + _._M_A_K_E_._P_P_I_D The parent process-id of bbmmaakkee. + + _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. Its + value is determined by trying to chdir(2) to the follow- + ing directories in order and using the first match: + + 1. ${MAKEOBJDIRPREFIX}${.CURDIR} + + (Only if `MAKEOBJDIRPREFIX' is set in the environ- + ment or on the command line.) + + 2. ${MAKEOBJDIR} + + (Only if `MAKEOBJDIR' is set in the environment or + on the command line.) + + 3. ${.CURDIR}_/_o_b_j_.${MACHINE} + + 4. ${.CURDIR}_/_o_b_j + + 5. _/_u_s_r_/_o_b_j_/${.CURDIR} + + 6. ${.CURDIR} + + Variable expansion is performed on the value before it's + used, so expressions such as + ${.CURDIR:S,^/usr/src,/var/obj,} + may be used. This is especially useful with + `MAKEOBJDIR'. + + `_._O_B_J_D_I_R' may be modified in the makefile as a global + variable. In all cases, bbmmaakkee will chdir(2) to `_._O_B_J_D_I_R' + and set `PWD' to that directory before executing any tar- + gets. + + _._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. If you want to retain + their current values, assign them to a variable using + assignment with expansion: (`::=='). + + _._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 or `MAKEOBJDIR' + contains a variable transform. `PWD' is set to the value + of `_._O_B_J_D_I_R' for all programs which bbmmaakkee executes. + + .TARGETS The list of targets explicitly specified on the command + line, if any. + + VPATH Colon-separated (``:'') lists of directories that bbmmaakkee + will search for files. The variable is supported for + compatibility with old make programs only, use `_._P_A_T_H' + instead. + + 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 + (`\'). + + A set of modifiers can be specified via a variable, as follows: + + modifier_variable=modifier[:...] + ${variable:${modifier_variable}[:...]} + + In this case the first modifier in the modifier_variable does not start + with a colon, since that must appear in the referencing variable. If any + of the modifiers in the modifier_variable contain a dollar sign (`$'), + these must be doubled to avoid early expansion. + + 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. + + ::ggmmttiimmee + The value is a format string for strftime(3), using the current + gmtime(3). + + ::hhaasshh + Compute a 32bit hash of the value and encode it as hex digits. + + ::llooccaallttiimmee + The value is a format string for strftime(3), using the current + localtime(3). + + ::ttAA Attempt to convert variable to an absolute path using realpath(3), + if that fails, the value is unchanged. + + ::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. The common escapes (including + octal numeric codes), work as expected. + + ::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 name (not its value), when parsed as a .if condi- + tional expression, 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. Since the variable + name is used as the expression, :? must be the first modifier after + the variable name itself - which will, of course, usually contain + variable expansions. A common error is trying to use expressions + like + ${NUMBERS:M42:?match:no} + which actually tests defined(NUMBERS), to determine is any words + match "42" you need to use something like: + ${"${NUMBERS:M42}" != "":?match:no}. + + _:_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.}@} + + However a single character varaiable is often more readable: + ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@} + + ::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. In order for this modifier to work, the name + (node) must at least have appeared on the rhs of a dependency. + + ::!!_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 set a variable when shell commands are being parsed. + These assignment modifiers always expand to nothing, so if appearing + in a rule line by themselves should be preceded with something to + keep bbmmaakkee happy. + + 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: + + ..eerrrroorr _m_e_s_s_a_g_e + The message is printed along with the name of the makefile and + line number, then bbmmaakkee will exit. + + ..eexxppoorrtt _v_a_r_i_a_b_l_e _._._. + Export the specified global variable. If no variable list is + provided, all globals are exported except for internal variables + (those that start with `.'). This is not affected by the --XX + flag, so should be used with caution. For compatibility with + other bbmmaakkee programs `export variable=value' is also accepted. + + Appending a variable name to _._M_A_K_E_._E_X_P_O_R_T_E_D is equivalent to + exporting a variable. + + ..eexxppoorrtt--eennvv _v_a_r_i_a_b_l_e _._._. + The same as `.export', except that the variable is not appended + to _._M_A_K_E_._E_X_P_O_R_T_E_D. This allows exporting a value to the environ- + ment which is different from that used by bbmmaakkee internally. + + ..iinnffoo _m_e_s_s_a_g_e + The message is printed along with the name of the makefile and + line number. + + ..uunnddeeff _v_a_r_i_a_b_l_e + Un-define the specified global variable. Only global variables + may be un-defined. + + ..uunneexxppoorrtt _v_a_r_i_a_b_l_e _._._. + The opposite of `.export'. The specified global _v_a_r_i_a_b_l_e will be + removed from _._M_A_K_E_._E_X_P_O_R_T_E_D. If no variable list is provided, + all globals are unexported, and _._M_A_K_E_._E_X_P_O_R_T_E_D deleted. + + ..uunneexxppoorrtt--eennvv + Unexport all globals previously exported and clear the environ- + ment inherited from the parent. This operation will cause a mem- + ory leak of the original environment, so should be used spar- + ingly. Testing for _._M_A_K_E_._L_E_V_E_L being 0, would make sense. Also + note that any variables which originated in the parent environ- + ment should be explicitly preserved if desired. For example: + + .if ${.MAKE.LEVEL} == 0 + PATH := ${PATH} + .unexport-env + .export PATH + .endif + + Would result in an environment containing only `PATH', which is + the minimal useful environment. Actually `.MAKE.LEVEL' will also + be pushed into the new environment. + + ..wwaarrnniinngg _m_e_s_s_a_g_e + The message prefixed by `_w_a_r_n_i_n_g_:' is printed along with the name + of the makefile and line number. + + ..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 expressions, and it + encounters a (white-space separated) 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', `..iiffnnddeeff', or `..iiff' + the ``defined'' expression is applied. Similarly, if the form is + `..iiffmmaakkee' or `..iiffnnmmaakkee, tthhee' ``make'' 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 + <make-rules> + ..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 an unescaped new 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. + + ..MMEETTAA Create a meta file for the target, even if it is flagged as + ..PPHHOONNYY, ..MMAAKKEE, or ..SSPPEECCIIAALL. Usage in conjunction with ..MMAAKKEE is + the most likely case. In "meta" mode, the target is out-of- + date if the meta file is missing. + + ..NNOOMMEETTAA Do not create a meta file for the target. Meta files are also + not created for ..PPHHOONNYY, ..MMAAKKEE, or ..SSPPEECCIIAALL targets. + + ..NNOOMMEETTAA__CCMMPP + Ignore differences in commands when deciding if target is out + of date. This is useful if the command contains a value which + always changes. If the number of commands change, though, the + target will still be out of date. + + ..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. Suffix-transformation rules are not applied to + ..PPHHOONNYY targets. + + ..PPRREECCIIOOUUSS + When bbmmaakkee is interrupted, it normally removes any partially + made targets. 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. + Since the dependents of files are not made until the file + itself could be made, this also stops the dependents being + built unless they are needed for another branch of the depen- + dency tree. So given: + + x: a .WAIT b + echo x + a: + echo a + b: b1 + echo b + b1: + echo b1 + + the output is always `a', `b1', `b', `x'. + The ordering imposed by ..WWAAIITT is only relevant for parallel + makes. + +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. + + ..EERRRROORR Any command lines attached to this target are executed when + another target fails. The ..EERRRROORR__TTAARRGGEETT variable is set to the + target that failed. See also MMAAKKEE__PPRRIINNTT__VVAARR__OONN__EERRRROORR. + + ..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. This ordering does not + add targets to the list of targets to be made. Since the depen- + dents of a target do not get built until the target itself could + be built, unless `a' is built by another part of the dependency + graph, the following is a dependency loop: + + .ORDER: b a + b: a + + The ordering imposed by ..OORRDDEERR is only relevant for parallel + makes. + + ..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. + + _n_e_w_l_i_n_e The string literal to pass the shell that results in + a single newline character when used outside of any + quoting characters. + 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 newline="'\n'" + + ..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. It + allows the creation of suffix-transformation rules. + + Example: + + .SUFFIXES: .o + .c.o: + cc -o ${.TARGET} -c ${.IMPSRC} + +EENNVVIIRROONNMMEENNTT + bbmmaakkee uses the following environment variables, if they exist: MACHINE, + MACHINE_ARCH, MAKE, MAKEFLAGS, MAKEOBJDIR, MAKEOBJDIRPREFIX, MAKESYSPATH, + PWD, and TMPDIR. + + MAKEOBJDIRPREFIX and MAKEOBJDIR may only be set in the environment or on + the command line to bbmmaakkee and not as makefile variables; see the descrip- + tion of `_._O_B_J_D_I_R' for more details. + +FFIILLEESS + .depend list of dependencies + Makefile list of dependencies + makefile list of dependencies + sys.mk system makefile + /usr/share/mk system makefile directory + +CCOOMMPPAATTIIBBIILLIITTYY + The basic make syntax is compatible between different versions of make, + however the special variables, variable modifiers and conditionals are + not. + + The way that parallel makes are scheduled changed in NetBSD 4.0 so that + .ORDER and .WAIT apply recursively to the dependent nodes. The algo- + rithms used may change again in the future. + + The way that .for loop variables are substituted changed after NetBSD 5.0 + so that they still appear to be variable expansions. In particular this + stops them being treated as syntax, and removes some obscure problems + using them in .if statements. + + Unlike other bbmmaakkee programs, this implementation by default executes all + commands for a given target using a single shell invocation. This is + done for both efficiency and to simplify error handling in remote command + invocations. Typically this is transparent to the user, unless the tar- + get commands change the current working directory using ``cd'' or + ``chdir''. To be compatible with Makefiles that do this, one can use --BB + to disable this behavior. + +SSEEEE AALLSSOO + mkdep(1) + +HHIISSTTOORRYY + bbmmaakkee is derived from NetBSD make(1). It uses autoconf to facilitate + portability to other platforms. + +NetBSD 5.1 April 24, 2012 NetBSD 5.1 diff --git a/20120704/boot-strap b/20120704/boot-strap new file mode 100755 index 0000000..660b766 --- /dev/null +++ b/20120704/boot-strap @@ -0,0 +1,388 @@ +: +# 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. +# +# Options: +# +# -c "rc" +# Pick up settings from "rc". +# We look for '.bmake-boot-strap.rc' before processing +# options. +# +# --share "share_dir" +# Where to put man pages and mk files. +# If $prefix ends in $HOST_TARGET, and $prefix/../share +# exits, the default will be that rather than $prefix/share. +# +# --mksrc "mksrc" +# Indicate where the mk files can be found. +# Default is ./mk or ../mk, set to 'none' to force +# building without "mksrc" but in that case a sys.mk +# needs to exist in the default syspath ($share_dir/mk) +# +# Possibly useful configure_args: +# +# --without-meta +# disable use of meta mode. +# +# --without-filemon +# disable use of filemon(9) which is currently only +# available for NetBSD and FreeBSD. +# +# --with-filemon="path/to/filemon.h" +# enables use of filemon(9) by meta mode. +# +# --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. +# +# AUTHOR: +# Simon J. Gerraty <sjg@crufty.net> + +# RCSid: +# $Id: boot-strap,v 1.39 2012/03/26 17:08:22 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" && 'pwd'`;; +esac + + +Usage() { + [ "$1" ] && echo "ERROR: $@" >&2 + echo "Usage:" >&2 + echo "$0 [--<configure_arg> ...][-s <srcdir>][-m <mksrc>][<prefix> [[<srcdir>] [<mksrc>]]]" >&2 + exit 1 +} + +Error() { + echo "ERROR: $@" >&2 + exit 1 +} + +source_rc() { + rc="$1"; shift + for d in ${*:-""} + do + r="${d:+$d/}$rc" + [ -f "$r" -a -s "$r" ] || continue + echo "NOTE: reading $r" + . "$r" + break + done +} + +CONFIGURE_ARGS= +MAKESYSPATH= +# pick a useful default prefix (for me at least ;-) +for prefix in /opt/$HOST_TARGET "$HOME/$HOST_TARGET" /usr/pkg /usr/local "" +do + [ -d "${prefix:-.}" ] && break +done +srcdir= +mksrc= +objdir= +quiet=: + +source_rc .bmake-boot-strap.rc . "$Mydir/.." "$HOME" + +get_optarg() { + expr "x$1" : "x[^=]*=\\(.*\\)" +} + +while : +do + case "$1" in + --) shift; break;; + --prefix) prefix="$2"; shift;; + --prefix=*) prefix=`get_optarg "$1"`;; + --src=*) srcdir=`get_optarg "$1"`;; + --with-mksrc=*|--mksrc=*) mksrc=`get_optarg "$1"`;; + --share=*) share_dir=`get_optarg "$1"`;; + --share) share_dir="$2"; shift;; + --with-default-sys-path=*) + CONFIGURE_ARGS="$1" + MAKESYSPATH=`get_optarg "$1"`;; + --with-default-sys-path) + CONFIGURE_ARGS="$1 $2" + MAKESYSPATH="$2"; shift;; + -s|--src) srcdir="$2"; shift;; + -m|--mksrc) mksrc="$2"; shift;; + -o|--objdir) objdir="$2"; shift;; + -q) quiet=;; + -c) source_rc "$2"; shift;; + --*) CONFIGURE_ARGS="$CONFIGURE_ARGS $1";; + *=*) eval "$1"; export `expr "x$1" : "x\\(.[^=]*\\)=.*"`;; + *) break;; + esac + shift +done + +AddConfigure() { + case " $CONFIGURE_ARGS " in + *" $1"*) ;; + *) CONFIGURE_ARGS="$CONFIGURE_ARGS $1$2";; + esac +} + +GetDir() { + match="$1" + shift + fmatch="$1" + shift + for dir in $* + do + [ -d "$dir" ] || continue + case "/$dir/" in + *$match*) ;; + *) continue;; + esac + case "$fmatch" in + .) ;; + *) [ -s $dir/$fmatch ] || continue;; + esac + case "$dir/" in + *./*) cd "$dir" && 'pwd';; + /*) echo $dir;; + *) cd "$dir" && 'pwd';; + esac + break + done +} + +FindHereOrAbove() { + ( + _t=-s + while : + do + case "$1" in + -C) cd "$2"; shift; shift;; + -?) _t=$1; shift;; + *) break;; + esac + done + case "$1" in + /*) # we shouldn't be here + [ $_t "$1" ] && echo "$1" + return + ;; + .../*) want=`echo "$1" | sed 's,^.../*,,'`;; + *) want="$1";; + esac + here=`'pwd'` + while : + do + if [ $_t "./$want" ]; then + echo "$here/$want" + return + fi + cd .. + here=`'pwd'` + case "$here" in + /) return;; + esac + done + ) +} + +# is $1 missing from $2 (or PATH) ? +no_path() { + eval "__p=\$${2:-PATH}" + case ":$__p:" in *:"$1":*) return 1;; *) return 0;; esac +} + +# if $1 exists and is not in path, append it +add_path () { + case "$1" in + -?) t=$1; shift;; + *) t=-d;; + esac + case "$2,$1" in + MAKESYSPATH,.../*) ;; + *) [ $t ${1:-.} ] || return;; + esac + no_path $* && eval ${2:-PATH}="$__p${__p:+:}$1" +} + + +srcdir=`GetDir /bmake make-bootstrap.sh.in "$srcdir" "$2" "$Mydir" ./bmake* "$Mydir"/../bmake*` +[ -d "${srcdir:-/dev/null}" ] || Usage +case "$mksrc" in +none|-) # we don't want it + mksrc= + ;; +.../*) # find here or above + mksrc=`FindHereOrAbove -C "$Mydir" -s "$mksrc/sys.mk"` + # that found a file + mksrc=`dirname $mksrc` + ;; +*) # guess we want mksrc... + mksrc=`GetDir /mk sys.mk "$mksrc" "$3" ./mk* "$srcdir"/mk* "$srcdir"/../mk*` + [ -d "${mksrc:-/dev/null}" ] || Usage "Use '-m none' to build without mksrc" + ;; +esac + +# Ok, get to work... +objdir="${objdir:-$OS}" +[ -d "$objdir" ] || mkdir -p "$objdir" +[ -d "$objdir" ] || mkdir "$objdir" +cd "$objdir" || exit 1 +# make it absolute +objdir=`'pwd'` + +ShareDir() { + case "/$1" in + /) [ -d /share ] || return;; + */$HOST_TARGET) + if [ -d "$1/../share" ]; then + echo `dirname "$1"`/share + return + fi + ;; + esac + echo $1/share +} + +# make it easy to force prefix to use $HOST_TARGET +: looking at "$prefix" +case "$prefix" in +*/host?target) prefix=`echo "$prefix" | sed "s,host.target,${HOST_TARGET},"`;; +esac + +share_dir="${share_dir:-`ShareDir $prefix`}" + +AddConfigure --prefix= "$prefix" +case "$CONFIGURE_ARGS" in +*--with-*-sys-path*) ;; # skip +*) [ "$share_dir" ] && AddConfigure --with-default-sys-path= "$share_dir/mk";; +esac +if [ "$mksrc" ]; then + AddConfigure --with-mksrc= "$mksrc" + # not all cc's support this + CFLAGS_MF= CFLAGS_MD= + export CFLAGS_MF CFLAGS_MD +fi + +$srcdir/configure $CONFIGURE_ARGS || exit 1 +chmod 755 make-bootstrap.sh || exit 1 +./make-bootstrap.sh || exit 1 +if [ -z "$MAKESYSPATH" ]; then + add_path "${share_dir:-...}/mk" MAKESYSPATH + case "$HOST_TARGET" in + netbsd*) add_path /usr/share/mk MAKESYSPATH;; + esac +fi +if [ -s "${mksrc:-/dev/null}/install-mk" ]; then + sh "${mksrc}/install-mk" "$objdir/mk" + case "$MAKESYSPATH" in + .../mk*) ;; + *) MAKESYSPATH=".../mk:${MAKESYSPATH}";; + esac +fi +# make sure test below uses the same diff that configure did +TOOL_DIFF=`type diff | sed 's,[()],,g;s,^[^/][^/]*,,;q'` +export MAKESYSPATH TOOL_DIFF +if [ "$mksrc" ]; then + $objdir/bmake test || exit 1 +else + # assume nothing + $objdir/bmake -r -m / test || exit 1 +fi +# If -q given, we don't want all the install instructions +$quiet exit 0 + +make_version=`./bmake -r -m / -f ./Makefile -V MAKE_VERSION | ( read one two; echo $one )` +bmake_version=bmake-$make_version + +if [ -s /usr/share/tmac/andoc.tmac ]; then + # this should be ok + man_subdir=man1 + man_src=$srcdir/bmake.1 +else + # guess not + man_subdir=cat1 + man_src=$srcdir/bmake.cat1 +fi + +install_prefix() { + ( + bin_dir= + share_dir= + man_dir= + mk_dir= + while : + do + case "$1" in + *=*) eval "$1"; shift;; + *) break;; + esac + done + bin_dir=${bin_dir:-$1/bin} + share_dir=${share_dir:-`ShareDir "$1"`} + man_dir=${man_dir:-$share_dir/man} + mk_dir=${mk_dir:-$share_dir/mk} + echo + echo Commands to install into $1/ + echo + echo mkdir -p $bin_dir + echo cp $objdir/bmake $bin_dir/$bmake_version + echo rm -f $bin_dir/bmake + echo ln -s $bmake_version $bin_dir/bmake + echo mkdir -p $man_dir/$man_subdir + echo cp $man_src $man_dir/$man_subdir/bmake.1 + if [ "$mksrc" ]; then + ev=`env | grep '_MK='` + echo $ev sh $mksrc/install-mk $mk_dir + fi + ) +} + +case "$prefix/" in +"$HOME"/*) ;; +*) CONFIGS=${CONFIGS:-/configs} + [ -d $CONFIGS ] && + install_prefix mksrc= "$CONFIGS/$OS/$OSMAJOR.X/$MACHINE_ARCH$prefix" + # I like to keep a copy here... + install_prefix share_dir="$HOME/share" "$HOME/$HOST_TARGET" + ;; +esac + +install_prefix "$prefix" diff --git a/20120704/bsd.after-import.mk b/20120704/bsd.after-import.mk new file mode 100644 index 0000000..e87026f --- /dev/null +++ b/20120704/bsd.after-import.mk @@ -0,0 +1,105 @@ +# $Id: bsd.after-import.mk,v 1.6 2012/06/27 18:23:32 sjg Exp $ + +# This makefile is for use when integrating bmake into a BSD build +# system. Use this makefile after importing bmake. +# It will bootstrap the new version, +# capture the generated files we need, and add an after-import +# target to allow the process to be easily repeated. + +# The goal is to allow the benefits of autoconf without +# the overhead of running configure. + +all: _makefile +all: after-import + +# we rely on bmake +.if !defined(.MAKE.LEVEL) +.error this makefile requires bmake +.endif + +_this := ${MAKEFILE:tA} +BMAKE_SRC := ${.PARSEDIR} + +# it helps to know where the top of the tree is. +.if !defined(SRCTOP) +srctop := ${.MAKE.MAKEFILES:M*src/share/mk/sys.mk:H:H:H} +.if empty(srctop) +# likely locations? +.for d in contrib/bmake external/bsd/bmake/dist +.if ${BMAKE_SRC:M*/$d} != "" +srctop := ${BMAKE_SRC:tA:S,/$d,,} +.endif +.endfor +.endif +.if !empty(srctop) +SRCTOP := ${srctop} +.endif +.endif + +# This lets us match what boot-strap does +.if !defined(HOST_OS) +HOST_OS!= uname +.endif + +# .../share/mk will find ${SRCTOP}/share/mk +# if we are within ${SRCTOP} +DEFAULT_SYS_PATH= .../share/mk:/usr/share/mk + +BOOTSTRAP_ARGS = \ + --with-default-sys-path='${DEFAULT_SYS_PATH}' \ + --prefix /usr \ + --share /usr/share \ + --mksrc none + +# run boot-strap with minimal influence +bootstrap: ${BMAKE_SRC}/boot-strap ${MAKEFILE} + HOME=/ ${BMAKE_SRC}/boot-strap ${BOOTSTRAP_ARGS} ${BOOTSTRAP_XTRAS} + touch ${.TARGET} + +# Makefiles need a little more tweaking than say config.h +MAKEFILE_SED = sed -e '/^MACHINE/d' \ + -e '/^PROG/s,bmake,${.CURDIR:T},' \ + -e 's,^.-include,.sinclude,' \ + -e 's,${SRCTOP},$${SRCTOP},g' + +# These are the simple files we want to capture +configured_files= config.h unit-tests/Makefile + +after-import: bootstrap ${MAKEFILE} +.for f in ${configured_files:N*Makefile} + @echo Capturing $f + @mkdir -p ${${.CURDIR}/$f:L:H} + @cmp -s ${.CURDIR}/$f ${HOST_OS}/$f || \ + cp ${HOST_OS}/$f ${.CURDIR}/$f +.endfor +.for f in ${configured_files:M*Makefile} + @echo Capturing $f + @mkdir -p ${${.CURDIR}/$f:L:H} + @${MAKEFILE_SED} ${HOST_OS}/$f > ${.CURDIR}/$f +.endfor + +# this needs the most work +_makefile: bootstrap ${MAKEFILE} + @echo Generating ${.CURDIR}/Makefile + @(echo '# This is a generated file, do NOT edit!'; \ + echo '# See ${_this:S,${SRCTOP}/,,}'; \ + echo '#'; echo '# $$${OS}$$'; echo; \ + echo 'SRCTOP?= $${.CURDIR:${.CURDIR:S,${SRCTOP}/,,:C,[^/]+,H,g:S,/,:,g}}'; echo; \ + echo; echo '# look here first for config.h'; \ + echo 'CFLAGS+= -I$${.CURDIR}'; echo; \ + ${MAKEFILE_SED} ${HOST_OS}/Makefile; \ + echo; echo '# override some simple things'; \ + echo 'BINDIR= /usr/bin'; \ + echo 'MANDIR= /usr/share/man'; \ + echo; echo '# make sure we get this'; \ + echo 'CFLAGS+= $${COPTS.$${.IMPSRC:T}}'; \ + echo 'CLEANFILES+= bootstrap'; \ + echo; echo 'after-import: ${_this:S,${SRCTOP},\${SRCTOP},}'; \ + echo ' cd $${.CURDIR} && $${.MAKE} -f ${_this:S,${SRCTOP},\${SRCTOP},}'; \ + echo; echo '.sinclude "Makefile.inc"'; \ + echo ) > ${.TARGET} + @cmp -s ${.TARGET} ${.CURDIR}/Makefile || \ + mv ${.TARGET} ${.CURDIR}/Makefile + +.include <bsd.obj.mk> + diff --git a/20120704/buf.c b/20120704/buf.c new file mode 100644 index 0000000..ac95c16 --- /dev/null +++ b/20120704/buf.c @@ -0,0 +1,291 @@ +/* $NetBSD: buf.c,v 1.25 2012/04/24 20:26:58 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.25 2012/04/24 20:26:58 sjg Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)buf.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: buf.c,v 1.25 2012/04/24 20:26:58 sjg Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * buf.c -- + * Functions for automatically-expanded buffers. + */ + +#include "make.h" +#include "buf.h" + +#ifndef max +#define max(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#define BUF_DEF_SIZE 256 /* Default buffer size */ + +/*- + *----------------------------------------------------------------------- + * Buf_Expand_1 -- + * Extend buffer for single byte add. + * + *----------------------------------------------------------------------- + */ +void +Buf_Expand_1(Buffer *bp) +{ + bp->size += max(bp->size, 16); + bp->buffer = bmake_realloc(bp->buffer, bp->size); +} + +/*- + *----------------------------------------------------------------------- + * 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) +{ + int count = bp->count; + Byte *ptr; + + if (__predict_false(count + numBytes >= bp->size)) { + bp->size += max(bp->size, numBytes + 16); + bp->buffer = bmake_realloc(bp->buffer, bp->size); + } + + ptr = bp->buffer + count; + bp->count = count + numBytes; + ptr[numBytes] = 0; + memcpy(ptr, bytesPtr, numBytes); +} + +/*- + *----------------------------------------------------------------------- + * 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->count; + + return (bp->buffer); +} + +/*- + *----------------------------------------------------------------------- + * Buf_Empty -- + * Throw away bytes in a buffer. + * + * Results: + * None. + * + * Side Effects: + * The bytes are discarded. + * + *----------------------------------------------------------------------- + */ +void +Buf_Empty(Buffer *bp) +{ + + bp->count = 0; + *bp->buffer = 0; +} + +/*- + *----------------------------------------------------------------------- + * 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. + * + *----------------------------------------------------------------------- + */ +void +Buf_Init(Buffer *bp, int size) +{ + if (size <= 0) { + size = BUF_DEF_SIZE; + } + bp->size = size; + bp->count = 0; + bp->buffer = bmake_malloc(size); + *bp->buffer = 0; +} + +/*- + *----------------------------------------------------------------------- + * Buf_Destroy -- + * Nuke a buffer and all its resources. + * + * Input: + * buf Buffer to destroy + * freeData TRUE if the data should be destroyed + * + * Results: + * Data buffer, NULL if freed + * + * Side Effects: + * The buffer is freed. + * + *----------------------------------------------------------------------- + */ +Byte * +Buf_Destroy(Buffer *buf, Boolean freeData) +{ + Byte *data; + + data = buf->buffer; + if (freeData) { + free(data); + data = NULL; + } + + buf->size = 0; + buf->count = 0; + buf->buffer = NULL; + + return data; +} + + +/*- + *----------------------------------------------------------------------- + * Buf_DestroyCompact -- + * Nuke a buffer and return its data. + * + * Input: + * buf Buffer to destroy + * + * Results: + * Data buffer + * + * Side Effects: + * If the buffer size is much greater than its content, + * a new buffer will be allocated and the old one freed. + * + *----------------------------------------------------------------------- + */ +#ifndef BUF_COMPACT_LIMIT +# define BUF_COMPACT_LIMIT 128 /* worthwhile saving */ +#endif + +Byte * +Buf_DestroyCompact(Buffer *buf) +{ +#if BUF_COMPACT_LIMIT > 0 + Byte *data; + + if (buf->size - buf->count >= BUF_COMPACT_LIMIT) { + /* We trust realloc to be smart */ + data = bmake_realloc(buf->buffer, buf->count + 1); + if (data) { + data[buf->count] = 0; + Buf_Destroy(buf, FALSE); + return data; + } + } +#endif + return Buf_Destroy(buf, FALSE); +} diff --git a/20120704/buf.h b/20120704/buf.h new file mode 100644 index 0000000..25be67d --- /dev/null +++ b/20120704/buf.h @@ -0,0 +1,119 @@ +/* $NetBSD: buf.h,v 1.17 2012/04/24 20:26:58 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 + +typedef char Byte; + +typedef struct Buffer { + int size; /* Current size of the buffer */ + int count; /* Number of bytes in buffer */ + Byte *buffer; /* The buffer itself (zero terminated) */ +} Buffer; + +/* If we aren't on netbsd, __predict_false() might not be defined. */ +#ifndef __predict_false +#define __predict_false(x) (x) +#endif + +/* Buf_AddByte adds a single byte to a buffer. */ +#define Buf_AddByte(bp, byte) do { \ + int _count = ++(bp)->count; \ + char *_ptr; \ + if (__predict_false(_count >= (bp)->size)) \ + Buf_Expand_1(bp); \ + _ptr = (bp)->buffer + _count; \ + _ptr[-1] = (byte); \ + _ptr[0] = 0; \ + } while (0) + +#define BUF_ERROR 256 + +#define Buf_Size(bp) ((bp)->count) + +void Buf_Expand_1(Buffer *); +void Buf_AddBytes(Buffer *, int, const Byte *); +Byte *Buf_GetAll(Buffer *, int *); +void Buf_Empty(Buffer *); +void Buf_Init(Buffer *, int); +Byte *Buf_Destroy(Buffer *, Boolean); +Byte *Buf_DestroyCompact(Buffer *); + +#endif /* _BUF_H */ diff --git a/20120704/compat.c b/20120704/compat.c new file mode 100644 index 0000000..7f715cc --- /dev/null +++ b/20120704/compat.c @@ -0,0 +1,764 @@ +/* $NetBSD: compat.c,v 1.89 2012/06/10 21:44:01 wiz 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.89 2012/06/10 21:44:01 wiz Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)compat.c 8.2 (Berkeley) 3/19/94"; +#else +__RCSID("$NetBSD: compat.c,v 1.89 2012/06/10 21:44:01 wiz 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 <sys/types.h> +#include <sys/stat.h> +#include "wait.h" + +#include <ctype.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> + +#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 = NULL; +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 != NULL) && !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 != NULL) { + Compat_Make(gn, gn); + } + } + + } + if (signo == SIGQUIT) + _exit(signo); + bmake_signal(signo, SIG_DFL); + kill(myPid, 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(void *cmdp, void *gnp) +{ + char *cmdStart; /* Start of expanded command */ + char *cp, *bp; + Boolean silent, /* Don't print command */ + doIt; /* Execute even if -n */ + volatile Boolean errCheck; /* Check errors */ + WAIT_T reason; /* Reason for child's death */ + int status; /* Description of child's death */ + pid_t cpid; /* Child actually found */ + pid_t retstat; /* Result of wait */ + LstNode cmdNode; /* Node where current command is located */ + const char ** volatile av; /* Argument vector for thing to exec */ + char ** volatile mav;/* Copy of the argument vector for freeing */ + int argc; /* Number of arguments in av or 0 if not + * dynamically allocated */ + Boolean local; /* TRUE if command should be executed + * locally */ + Boolean useShell; /* TRUE if command should be executed + * using a shell */ + char * volatile cmd = (char *)cmdp; + GNode *gn = (GNode *)gnp; + + silent = gn->type & OP_SILENT; + errCheck = !(gn->type & OP_IGNORE); + doIt = FALSE; + + cmdNode = Lst_Member(gn->commands, 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); + } + cmd = cmdStart; + Lst_Replace(cmdNode, cmdStart); + + if ((gn->type & OP_SAVE_CMDS) && (gn != ENDNode)) { + (void)Lst_AtEnd(ENDNode->commands, cmdStart); + return(0); + } + if (strcmp(cmdStart, "...") == 0) { + gn->type |= OP_SAVE_CMDS; + return(0); + } + + while ((*cmd == '@') || (*cmd == '-') || (*cmd == '+')) { + switch (*cmd) { + case '@': + silent = DEBUG(LOUD) ? FALSE : 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++; + + /* + * If we did not end up with a command, just skip it. + */ + if (!*cmd) + return (0); + +#if !defined(MAKE_NATIVE) + /* + * In a non-native build, the host environment might be weird enough + * that it's necessary to go through a shell to get the correct + * behaviour. Or perhaps the shell has been replaced with something + * that does extra logging, and that should not be bypassed. + */ + useShell = TRUE; +#else + /* + * 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; + } + useShell = (*cp != '\0'); +#endif + + /* + * 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 (DEBUG(JOB)) + fprintf(debug_file, "Execute: '%s'\n", cmd); + +again: + if (useShell) { + /* + * We need to pass the command off to the shell, typically + * because the command contains a "meta" character. + */ + 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; + mav = 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. + */ + mav = brk_string(cmd, &argc, TRUE, &bp); + if (mav == NULL) { + useShell = 1; + goto again; + } + av = (void *)mav; + } + + local = TRUE; + +#ifdef USE_META + if (useMeta) { + meta_compat_start(); + } +#endif + + /* + * 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); + Var_ExportVars(); +#ifdef USE_META + if (useMeta) { + meta_compat_child(); + } +#endif + 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 (mav) + free(mav); + if (bp) + free(bp); + Lst_Replace(cmdNode, NULL); + +#ifdef USE_META + if (useMeta) { + meta_compat_parent(); + } +#endif + + /* + * The child is off and running. Now all we can do is wait... + */ + while (1) { + + while ((retstat = wait(&reason)) != cpid) { + if (retstat > 0) + JobReapChild(retstat, reason, FALSE); /* not ours? */ + 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 defined(USE_META) && defined(USE_FILEMON_ONCE) + if (useMeta) { + meta_cmd_finish(NULL); + } +#endif + if (status != 0) { + if (DEBUG(ERROR)) { + fprintf(debug_file, "\n*** Failed target: %s\n*** Failed command: ", + gn->name); + for (cp = cmd; *cp; ) { + if (isspace((unsigned char)*cp)) { + fprintf(debug_file, " "); + while (isspace((unsigned char)*cp)) + cp++; + } else { + fprintf(debug_file, "%c", *cp); + cp++; + } + } + fprintf(debug_file, "\n"); + } + printf("*** Error code %d", status); + } + } else { + status = WTERMSIG(reason); /* signaled */ + printf("*** Signal %d", status); + } + + + if (!WIFEXITED(reason) || (status != 0)) { + if (errCheck) { +#ifdef USE_META + if (useMeta) { + meta_job_error(NULL, gn, 0, status); + } +#endif + 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(void *gnp, void *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, gn); + if ((gn->flags & REMAKE) == 0) { + gn->made = ABORTED; + pgn->flags &= ~REMAKE; + goto cohorts; + } + + if (Lst_Member(gn->iParents, pgn) != NULL) { + char *p1; + Var_Set(IMPSRC, Var_Value(TARGET, gn, &p1), pgn, 0); + if (p1) + free(p1); + } + + /* + * All the children were made ok. Now cmgn->mtime 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)) { + fprintf(debug_file, "Examining %s...", gn->name); + } + if (! Make_OODate(gn)) { + gn->made = UPTODATE; + if (DEBUG(MAKE)) { + fprintf(debug_file, "up-to-date.\n"); + } + goto cohorts; + } else if (DEBUG(MAKE)) { + fprintf(debug_file, "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; +#ifdef USE_META + if (useMeta && !NoExecute(gn)) { + meta_job_start(NULL, gn); + } +#endif + Lst_ForEach(gn->commands, CompatRunCommand, gn); + curTarg = NULL; + } else { + Job_Touch(gn, gn->type & OP_SILENT); + } + } else { + gn->made = ERROR; + } +#ifdef USE_META + if (useMeta && !NoExecute(gn)) { + meta_job_finish(NULL); + } +#endif + + 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(gn, "\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) != NULL) { + 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 (bmake_signal(SIGINT, SIG_IGN) != SIG_IGN) { + bmake_signal(SIGINT, CompatInterrupt); + } + if (bmake_signal(SIGTERM, SIG_IGN) != SIG_IGN) { + bmake_signal(SIGTERM, CompatInterrupt); + } + if (bmake_signal(SIGHUP, SIG_IGN) != SIG_IGN) { + bmake_signal(SIGHUP, CompatInterrupt); + } + if (bmake_signal(SIGQUIT, SIG_IGN) != SIG_IGN) { + bmake_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 != NULL) { + Compat_Make(gn, gn); + if (gn->made == ERROR) { + PrintOnError(gn, "\n\nStop."); + exit(1); + } + } + } + + /* + * Expand .USE nodes right now, because they can modify the structure + * of the tree. + */ + Make_ExpandUse(targs); + + /* + * 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(gn, "\n\nStop."); + exit(1); + } + } +} diff --git a/20120704/cond.c b/20120704/cond.c new file mode 100644 index 0000000..6d0b965 --- /dev/null +++ b/20120704/cond.c @@ -0,0 +1,1410 @@ +/* $NetBSD: cond.c,v 1.64 2012/06/12 19:21:50 joerg 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.64 2012/06/12 19:21:50 joerg Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)cond.c 8.2 (Berkeley) 1/2/94"; +#else +__RCSID("$NetBSD: cond.c,v 1.64 2012/06/12 19:21:50 joerg 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 <ctype.h> +#include <errno.h> /* For strtoul() error checking */ + +#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 TOK_AND for '&' and '&&', TOK_OR for '|' and '||', + * TOK_NOT for '!', TOK_LPAREN for '(', TOK_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 TOK_TRUE + * or TOK_FALSE. + * + * TOK_FALSE is 0 and TOK_TRUE 1 so we can directly assign C comparisons. + * + * All Non-Terminal functions (CondE, CondF and CondT) return TOK_ERROR on + * error. + */ +typedef enum { + TOK_FALSE = 0, TOK_TRUE = 1, TOK_AND, TOK_OR, TOK_NOT, + TOK_LPAREN, TOK_RPAREN, TOK_EOF, TOK_NONE, TOK_ERROR +} 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 *); +static Boolean CondDoDefined(int, const char *); +static int CondStrMatch(const void *, const void *); +static Boolean CondDoMake(int, const char *); +static Boolean CondDoExists(int, const char *); +static Boolean CondDoTarget(int, const char *); +static Boolean CondDoCommands(int, const char *); +static Boolean CondCvtArg(char *, double *); +static Token CondToken(Boolean); +static Token CondT(Boolean); +static Token CondF(Boolean); +static Token CondE(Boolean); +static int do_Cond_EvalExpression(Boolean *); + +static const 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, const char *); /* Default function to apply */ +} ifs[] = { + { "def", 3, FALSE, CondDoDefined }, + { "ndef", 4, TRUE, CondDoDefined }, + { "make", 4, FALSE, CondDoMake }, + { "nmake", 5, TRUE, CondDoMake }, + { "", 0, FALSE, CondDoDefined }, + { NULL, 0, FALSE, NULL } +}; + +static const struct If *if_info; /* Info for current statement */ +static char *condExpr; /* The expression to parse */ +static Token condPushBack=TOK_NONE; /* Single push-back token used in + * parsing */ + +static unsigned int cond_depth = 0; /* current .if nesting level */ +static unsigned int cond_min_depth = 0; /* depth at makefile open */ + +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) +{ + char *cp; + int argLen; + Buffer buf; + int paren_depth; + char ch; + + cp = *linePtr; + if (func != NULL) + /* Skip opening '(' - verfied by caller */ + 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 = NULL; + 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_Init(&buf, 16); + + paren_depth = 0; + for (;;) { + ch = *cp; + if (ch == 0 || ch == ' ' || ch == '\t') + break; + if ((ch == '&' || ch == '|') && paren_depth == 0) + break; + 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; + void *freeIt; + + cp2 = Var_Parse(cp, VAR_CMD, TRUE, &len, &freeIt); + Buf_AddBytes(&buf, strlen(cp2), cp2); + if (freeIt) + free(freeIt); + cp += len; + continue; + } + if (ch == '(') + paren_depth++; + else + if (ch == ')' && --paren_depth < 0) + break; + Buf_AddByte(&buf, *cp); + cp++; + } + + *argPtr = Buf_GetAll(&buf, &argLen); + Buf_Destroy(&buf, FALSE); + + while (*cp == ' ' || *cp == '\t') { + cp++; + } + + if (func != NULL && *cp++ != ')') { + Parse_Error(PARSE_WARNING, "Missing closing parenthesis for %s()", + func); + return (0); + } + + *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 MAKE_ATTR_UNUSED, const char *arg) +{ + char *p1; + Boolean result; + + if (Var_Value(arg, VAR_CMD, &p1) != NULL) { + result = TRUE; + } else { + result = FALSE; + } + if (p1) + free(p1); + 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(const void *string, const void *pattern) +{ + return(!Str_Match(string, 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 MAKE_ATTR_UNUSED, const char *arg) +{ + return Lst_Find(create, arg, CondStrMatch) != NULL; +} + +/*- + *----------------------------------------------------------------------- + * 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 MAKE_ATTR_UNUSED, const char *arg) +{ + Boolean result; + char *path; + + path = Dir_FindFile(arg, dirSearchPath); + if (DEBUG(COND)) { + fprintf(debug_file, "exists(%s) result is \"%s\"\n", + arg, path ? path : ""); + } + if (path != NULL) { + result = TRUE; + free(path); + } else { + result = FALSE; + } + 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 MAKE_ATTR_UNUSED, const char *arg) +{ + GNode *gn; + + gn = Targ_FindNode(arg, TARG_NOCREATE); + return (gn != NULL) && !OP_NOP(gn->type); +} + +/*- + *----------------------------------------------------------------------- + * 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 MAKE_ATTR_UNUSED, const char *arg) +{ + GNode *gn; + + gn = Targ_FindNode(arg, TARG_NOCREATE); + return (gn != NULL) && !OP_NOP(gn->type) && !Lst_IsEmpty(gn->commands); +} + +/*- + *----------------------------------------------------------------------- + * CondCvtArg -- + * Convert the given number into a double. + * We try a base 10 or 16 integer conversion first, if that fails + * then we try a floating point conversion instead. + * + * Results: + * Sets 'value' to double value of string. + * Returns 'true' if the convertion suceeded + * + *----------------------------------------------------------------------- + */ +static Boolean +CondCvtArg(char *str, double *value) +{ + char *eptr, ech; + unsigned long l_val; + double d_val; + + errno = 0; + l_val = strtoul(str, &eptr, str[1] == 'x' ? 16 : 10); + ech = *eptr; + if (ech == 0 && errno != ERANGE) { + d_val = str[0] == '-' ? -(double)-l_val : (double)l_val; + } else { + if (ech != 0 && ech != '.' && ech != 'e' && ech != 'E') + return FALSE; + d_val = strtod(str, &eptr); + if (*eptr) + return FALSE; + } + + *value = d_val; + return TRUE; +} + +/*- + *----------------------------------------------------------------------- + * 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 freeIt 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. + * + * + *----------------------------------------------------------------------- + */ +/* coverity:[+alloc : arg-*2] */ +static char * +CondGetString(Boolean doEval, Boolean *quoted, void **freeIt) +{ + Buffer buf; + char *cp; + char *str; + int len; + int qt; + char *start; + + Buf_Init(&buf, 0); + str = NULL; + *freeIt = 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, *condExpr); + } + break; + case '"': + if (qt) { + condExpr++; /* we don't want the quotes */ + goto got_str; + } else + Buf_AddByte(&buf, *condExpr); /* likely? */ + break; + case ')': + case '!': + case '=': + case '>': + case '<': + case ' ': + case '\t': + if (!qt) + goto got_str; + else + Buf_AddByte(&buf, *condExpr); + break; + case '$': + /* if we are in quotes, then an undefined variable is ok */ + str = Var_Parse(condExpr, VAR_CMD, (qt ? 0 : doEval), + &len, freeIt); + if (str == var_Error) { + if (*freeIt) { + free(*freeIt); + *freeIt = NULL; + } + /* + * 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, *cp); + } + if (*freeIt) { + free(*freeIt); + *freeIt = NULL; + } + str = NULL; /* not finished yet */ + condExpr--; /* don't skip over next char */ + break; + default: + Buf_AddByte(&buf, *condExpr); + break; + } + } + got_str: + str = Buf_GetAll(&buf, NULL); + *freeIt = str; + 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 TOK_NONE if it is used. + * + *----------------------------------------------------------------------- + */ +static Token +compare_expression(Boolean doEval) +{ + Token t; + char *lhs; + char *rhs; + char *op; + void *lhsFree; + void *rhsFree; + Boolean lhsQuoted; + Boolean rhsQuoted; + double left, right; + + t = TOK_ERROR; + rhs = NULL; + lhsFree = rhsFree = FALSE; + lhsQuoted = rhsQuoted = FALSE; + + /* + * Parse the variable spec and skip over it, saving its + * value in lhs. + */ + lhs = CondGetString(doEval, &lhsQuoted, &lhsFree); + if (!lhs) + goto done; + + /* + * 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: + if (!doEval) { + t = TOK_FALSE; + goto done; + } + /* For .ifxxx "..." check for non-empty string. */ + if (lhsQuoted) { + t = lhs[0] != 0; + goto done; + } + /* For .ifxxx <number> compare against zero */ + if (CondCvtArg(lhs, &left)) { + t = left != 0.0; + goto done; + } + /* For .if ${...} check for non-empty string (defProc is ifdef). */ + if (if_info->form[0] == 0) { + t = lhs[0] != 0; + goto done; + } + /* Otherwise action default test ... */ + t = if_info->defProc(strlen(lhs), lhs) != if_info->doNot; + goto done; + } + + while (isspace((unsigned char)*condExpr)) + condExpr++; + + if (*condExpr == '\0') { + Parse_Error(PARSE_WARNING, + "Missing right-hand-side of operator"); + goto done; + } + + rhs = CondGetString(doEval, &rhsQuoted, &rhsFree); + if (!rhs) + goto done; + + if (rhsQuoted || lhsQuoted) { +do_string_compare: + if (((*op != '!') && (*op != '=')) || (op[1] != '=')) { + Parse_Error(PARSE_WARNING, + "String comparison operator should be either == or !="); + goto done; + } + + if (DEBUG(COND)) { + fprintf(debug_file, "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) == 0; + } else { + t = strcmp(lhs, rhs) != 0; + } + } else { + /* + * rhs is either a float or an integer. Convert both the + * lhs and the rhs to a double and compare the two. + */ + + if (!CondCvtArg(lhs, &left) || !CondCvtArg(rhs, &right)) + goto do_string_compare; + + if (DEBUG(COND)) { + fprintf(debug_file, "left = %f, right = %f, op = %.2s\n", left, + right, op); + } + switch(op[0]) { + case '!': + if (op[1] != '=') { + Parse_Error(PARSE_WARNING, + "Unknown operator"); + goto done; + } + t = (left != right); + break; + case '=': + if (op[1] != '=') { + Parse_Error(PARSE_WARNING, + "Unknown operator"); + goto done; + } + t = (left == right); + break; + case '<': + if (op[1] == '=') { + t = (left <= right); + } else { + t = (left < right); + } + break; + case '>': + if (op[1] == '=') { + t = (left >= right); + } else { + t = (left > right); + } + break; + } + } + +done: + if (lhsFree) + free(lhsFree); + if (rhsFree) + free(rhsFree); + return t; +} + +static int +get_mpt_arg(char **linePtr, char **argPtr, const char *func MAKE_ATTR_UNUSED) +{ + /* + * Use Var_Parse to parse the spec in parens and return + * TOK_TRUE if the resulting string is empty. + */ + int length; + void *freeIt; + char *val; + char *cp = *linePtr; + + /* We do all the work here and return the result as the length */ + *argPtr = NULL; + + val = Var_Parse(cp - 1, VAR_CMD, FALSE, &length, &freeIt); + /* + * Advance *linePtr to beyond the closing ). Note that + * we subtract one because 'length' is calculated from 'cp - 1'. + */ + *linePtr = cp - 1 + length; + + if (val == var_Error) { + free(freeIt); + return -1; + } + + /* A variable is empty when it just contains spaces... 4/15/92, christos */ + while (isspace(*(unsigned char *)val)) + val++; + + /* + * For consistency with the other functions we can't generate the + * true/false here. + */ + length = *val ? 2 : 1; + if (freeIt) + free(freeIt); + return length; +} + +static Boolean +CondDoEmpty(int arglen, const char *arg MAKE_ATTR_UNUSED) +{ + return arglen == 1; +} + +static Token +compare_function(Boolean doEval) +{ + static const struct fn_def { + const char *fn_name; + int fn_name_len; + int (*fn_getarg)(char **, char **, const char *); + Boolean (*fn_proc)(int, const char *); + } fn_defs[] = { + { "defined", 7, CondGetArg, CondDoDefined }, + { "make", 4, CondGetArg, CondDoMake }, + { "exists", 6, CondGetArg, CondDoExists }, + { "empty", 5, get_mpt_arg, CondDoEmpty }, + { "target", 6, CondGetArg, CondDoTarget }, + { "commands", 8, CondGetArg, CondDoCommands }, + { NULL, 0, NULL, NULL }, + }; + const struct fn_def *fn_def; + Token t; + char *arg = NULL; + int arglen; + char *cp = condExpr; + char *cp1; + + for (fn_def = fn_defs; fn_def->fn_name != NULL; fn_def++) { + if (!istoken(cp, fn_def->fn_name, fn_def->fn_name_len)) + continue; + cp += fn_def->fn_name_len; + /* There can only be whitespace before the '(' */ + while (isspace(*(unsigned char *)cp)) + cp++; + if (*cp != '(') + break; + + arglen = fn_def->fn_getarg(&cp, &arg, fn_def->fn_name); + if (arglen <= 0) { + condExpr = cp; + return arglen < 0 ? TOK_ERROR : TOK_FALSE; + } + /* Evaluate the argument using the required function. */ + t = !doEval || fn_def->fn_proc(arglen, arg); + if (arg) + free(arg); + condExpr = cp; + return t; + } + + /* Push anything numeric through the compare expression */ + cp = condExpr; + if (isdigit((unsigned char)cp[0]) || strchr("+-", cp[0])) + return compare_expression(doEval); + + /* + * Most likely we have a naked token to apply the default function to. + * However ".if a == b" gets here when the "a" is unquoted and doesn't + * start with a '$'. This surprises people. + * If what follows the function argument is a '=' or '!' then the syntax + * would be invalid if we did "defined(a)" - so instead treat as an + * expression. + */ + arglen = CondGetArg(&cp, &arg, NULL); + for (cp1 = cp; isspace(*(unsigned char *)cp1); cp1++) + continue; + if (*cp1 == '=' || *cp1 == '!') + return compare_expression(doEval); + condExpr = cp; + + /* + * Evaluate the argument using the default function. + * This path always treats .if as .ifdef. To get here the character + * after .if must have been taken literally, so the argument cannot + * be empty - even if it contained a variable expansion. + */ + t = !doEval || if_info->defProc(arglen, arg) != if_info->doNot; + if (arg) + free(arg); + return t; +} + +static Token +CondToken(Boolean doEval) +{ + Token t; + + t = condPushBack; + if (t != TOK_NONE) { + condPushBack = TOK_NONE; + return t; + } + + while (*condExpr == ' ' || *condExpr == '\t') { + condExpr++; + } + + switch (*condExpr) { + + case '(': + condExpr++; + return TOK_LPAREN; + + case ')': + condExpr++; + return TOK_RPAREN; + + case '|': + if (condExpr[1] == '|') { + condExpr++; + } + condExpr++; + return TOK_OR; + + case '&': + if (condExpr[1] == '&') { + condExpr++; + } + condExpr++; + return TOK_AND; + + case '!': + condExpr++; + return TOK_NOT; + + case '#': + case '\n': + case '\0': + return TOK_EOF; + + case '"': + case '$': + return compare_expression(doEval); + + default: + return compare_function(doEval); + } +} + +/*- + *----------------------------------------------------------------------- + * CondT -- + * Parse a single term in the expression. This consists of a terminal + * symbol or TOK_NOT and a terminal symbol (not including the binary + * operators): + * T -> defined(variable) | make(target) | exists(file) | symbol + * T -> ! T | ( E ) + * + * Results: + * TOK_TRUE, TOK_FALSE or TOK_ERROR. + * + * Side Effects: + * Tokens are consumed. + * + *----------------------------------------------------------------------- + */ +static Token +CondT(Boolean doEval) +{ + Token t; + + t = CondToken(doEval); + + if (t == TOK_EOF) { + /* + * If we reached the end of the expression, the expression + * is malformed... + */ + t = TOK_ERROR; + } else if (t == TOK_LPAREN) { + /* + * T -> ( E ) + */ + t = CondE(doEval); + if (t != TOK_ERROR) { + if (CondToken(doEval) != TOK_RPAREN) { + t = TOK_ERROR; + } + } + } else if (t == TOK_NOT) { + t = CondT(doEval); + if (t == TOK_TRUE) { + t = TOK_FALSE; + } else if (t == TOK_FALSE) { + t = TOK_TRUE; + } + } + return (t); +} + +/*- + *----------------------------------------------------------------------- + * CondF -- + * Parse a conjunctive factor (nice name, wot?) + * F -> T && F | T + * + * Results: + * TOK_TRUE, TOK_FALSE or TOK_ERROR + * + * Side Effects: + * Tokens are consumed. + * + *----------------------------------------------------------------------- + */ +static Token +CondF(Boolean doEval) +{ + Token l, o; + + l = CondT(doEval); + if (l != TOK_ERROR) { + o = CondToken(doEval); + + if (o == TOK_AND) { + /* + * F -> T && F + * + * If T is TOK_FALSE, the whole thing will be TOK_FALSE, but we have to + * parse the r.h.s. anyway (to throw it away). + * If T is TOK_TRUE, the result is the r.h.s., be it an TOK_ERROR or no. + */ + if (l == TOK_TRUE) { + l = CondF(doEval); + } else { + (void)CondF(FALSE); + } + } else { + /* + * F -> T + */ + CondPushBack(o); + } + } + return (l); +} + +/*- + *----------------------------------------------------------------------- + * CondE -- + * Main expression production. + * E -> F || E | F + * + * Results: + * TOK_TRUE, TOK_FALSE or TOK_ERROR. + * + * Side Effects: + * Tokens are, of course, consumed. + * + *----------------------------------------------------------------------- + */ +static Token +CondE(Boolean doEval) +{ + Token l, o; + + l = CondF(doEval); + if (l != TOK_ERROR) { + o = CondToken(doEval); + + if (o == TOK_OR) { + /* + * E -> F || E + * + * A similar thing occurs for ||, except that here we make sure + * the l.h.s. is TOK_FALSE before we bother to evaluate the r.h.s. + * Once again, if l is TOK_FALSE, the result is the r.h.s. and once + * again if l is TOK_TRUE, we parse the r.h.s. to throw it away. + */ + if (l == TOK_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(const struct If *info, char *line, Boolean *value, int eprint) +{ + static const struct If *dflt_info; + const struct If *sv_if_info = if_info; + char *sv_condExpr = condExpr; + Token sv_condPushBack = condPushBack; + int rval; + + while (*line == ' ' || *line == '\t') + line++; + + if (info == NULL && (info = dflt_info) == NULL) { + /* Scan for the entry for .if - it can't be first */ + for (info = ifs; ; info++) + if (info->form[0] == 0) + break; + dflt_info = info; + } + + if_info = info != NULL ? info : ifs + 4; + condExpr = line; + condPushBack = TOK_NONE; + + rval = do_Cond_EvalExpression(value); + + if (rval == COND_INVALID && eprint) + Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", line); + + if_info = sv_if_info; + condExpr = sv_condExpr; + condPushBack = sv_condPushBack; + + return rval; +} + +static int +do_Cond_EvalExpression(Boolean *value) +{ + + switch (CondE(TRUE)) { + case TOK_TRUE: + if (CondToken(TRUE) == TOK_EOF) { + *value = TRUE; + return COND_PARSE; + } + break; + case TOK_FALSE: + if (CondToken(TRUE) == TOK_EOF) { + *value = FALSE; + return COND_PARSE; + } + break; + default: + case TOK_ERROR: + break; + } + + return COND_INVALID; +} + + +/*- + *----------------------------------------------------------------------- + * Cond_Eval -- + * Evaluate the conditional in the passed line. The line + * looks like this: + * .<cond-type> <expr> + * where <cond-type> is any of if, ifmake, ifnmake, ifdef, + * ifndef, elif, elifmake, elifnmake, elifdef, elifndef + * and <expr> 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. + * + * Note that the states IF_ACTIVE and ELSE_ACTIVE are only different in order + * to detect splurious .else lines (as are SKIP_TO_ELSE and SKIP_TO_ENDIF) + * otherwise .else could be treated as '.elif 1'. + * + *----------------------------------------------------------------------- + */ +int +Cond_Eval(char *line) +{ + #define MAXIF 128 /* maximum depth of .if'ing */ + enum if_states { + IF_ACTIVE, /* .if or .elif part active */ + ELSE_ACTIVE, /* .else part active */ + SEARCH_FOR_ELIF, /* searching for .elif/else to execute */ + SKIP_TO_ELSE, /* has been true, but not seen '.else' */ + SKIP_TO_ENDIF /* nothing else to execute */ + }; + static enum if_states cond_state[MAXIF + 1] = { IF_ACTIVE }; + + const struct If *ifp; + Boolean isElif; + Boolean value; + int level; /* Level at which to report errors. */ + enum if_states state; + + level = PARSE_FATAL; + + /* skip leading character (the '.') and any whitespace */ + for (line++; *line == ' ' || *line == '\t'; line++) + continue; + + /* Find what type of if we're dealing with. */ + if (line[0] == 'e') { + if (line[1] != 'l') { + if (!istoken(line + 1, "ndif", 4)) + return COND_INVALID; + /* End of conditional section */ + if (cond_depth == cond_min_depth) { + Parse_Error(level, "if-less endif"); + return COND_PARSE; + } + /* Return state for previous conditional */ + cond_depth--; + if (cond_depth > MAXIF) + return COND_SKIP; + return cond_state[cond_depth] <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP; + } + + /* Quite likely this is 'else' or 'elif' */ + line += 2; + if (istoken(line, "se", 2)) { + /* It is else... */ + if (cond_depth == cond_min_depth) { + Parse_Error(level, "if-less else"); + return COND_PARSE; + } + + if (cond_depth > MAXIF) + return COND_SKIP; + state = cond_state[cond_depth]; + switch (state) { + case SEARCH_FOR_ELIF: + state = ELSE_ACTIVE; + break; + case ELSE_ACTIVE: + case SKIP_TO_ENDIF: + Parse_Error(PARSE_WARNING, "extra else"); + /* FALLTHROUGH */ + default: + case IF_ACTIVE: + case SKIP_TO_ELSE: + state = SKIP_TO_ENDIF; + break; + } + cond_state[cond_depth] = state; + return state <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP; + } + /* Assume for now it is an elif */ + isElif = TRUE; + } else + isElif = FALSE; + + if (line[0] != 'i' || line[1] != 'f') + /* Not an ifxxx or elifxxx line */ + return COND_INVALID; + + /* + * Figure out what sort of conditional it is -- what its default + * function is, etc. -- by looking in the table of valid "ifs" + */ + line += 2; + for (ifp = ifs; ; ifp++) { + if (ifp->form == NULL) + return COND_INVALID; + if (istoken(ifp->form, line, ifp->formlen)) { + line += ifp->formlen; + break; + } + } + + /* Now we know what sort of 'if' it is... */ + + if (isElif) { + if (cond_depth == cond_min_depth) { + Parse_Error(level, "if-less elif"); + return COND_PARSE; + } + if (cond_depth > MAXIF) + /* Error reported when we saw the .if ... */ + return COND_SKIP; + state = cond_state[cond_depth]; + if (state == SKIP_TO_ENDIF || state == ELSE_ACTIVE) { + Parse_Error(PARSE_WARNING, "extra elif"); + cond_state[cond_depth] = SKIP_TO_ENDIF; + return COND_SKIP; + } + if (state != SEARCH_FOR_ELIF) { + /* Either just finished the 'true' block, or already SKIP_TO_ELSE */ + cond_state[cond_depth] = SKIP_TO_ELSE; + return COND_SKIP; + } + } else { + /* Normal .if */ + if (cond_depth >= MAXIF) { + cond_depth++; + Parse_Error(PARSE_FATAL, "Too many nested if's. %d max.", MAXIF); + return COND_SKIP; + } + state = cond_state[cond_depth]; + cond_depth++; + if (state > ELSE_ACTIVE) { + /* If we aren't parsing the data, treat as always false */ + cond_state[cond_depth] = SKIP_TO_ELSE; + return COND_SKIP; + } + } + + /* And evaluate the conditional expresssion */ + if (Cond_EvalExpression(ifp, line, &value, 1) == COND_INVALID) { + /* Syntax error in conditional, error message already output. */ + /* Skip everything to matching .endif */ + cond_state[cond_depth] = SKIP_TO_ELSE; + return COND_SKIP; + } + + if (!value) { + cond_state[cond_depth] = SEARCH_FOR_ELIF; + return COND_SKIP; + } + cond_state[cond_depth] = IF_ACTIVE; + return COND_PARSE; +} + + + +/*- + *----------------------------------------------------------------------- + * 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_restore_depth(unsigned int saved_depth) +{ + int open_conds = cond_depth - cond_min_depth; + + if (open_conds != 0 || saved_depth > cond_depth) { + Parse_Error(PARSE_FATAL, "%d open conditional%s", open_conds, + open_conds == 1 ? "" : "s"); + cond_depth = cond_min_depth; + } + + cond_min_depth = saved_depth; +} + +unsigned int +Cond_save_depth(void) +{ + int depth = cond_min_depth; + + cond_min_depth = cond_depth; + return depth; +} diff --git a/20120704/config.h.in b/20120704/config.h.in new file mode 100644 index 0000000..7108dcf --- /dev/null +++ b/20120704/config.h.in @@ -0,0 +1,314 @@ +/* config.h.in. Generated from configure.in by autoheader. */ + +/* Define if building universal (internal helper macro) */ +#undef AC_APPLE_UNIVERSAL_BUILD + +/* Path of default shell */ +#undef DEFSHELL_CUSTOM + +/* Shell spec to use by default */ +#undef DEFSHELL_INDEX + +/* Define to 1 if you have the <ar.h> header file. */ +#undef HAVE_AR_H + +/* 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 <dirent.h> header file, and it defines `DIR'. + */ +#undef HAVE_DIRENT_H + +/* Define to 1 if you have the `dirname' function. */ +#undef HAVE_DIRNAME + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +#undef HAVE_DOPRNT + +/* Define to 1 if you have the `err' function. */ +#undef HAVE_ERR + +/* Define to 1 if you have the `errx' function. */ +#undef HAVE_ERRX + +/* Define to 1 if you have the <err.h> header file. */ +#undef HAVE_ERR_H + +/* Define to 1 if you have the <fcntl.h> 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 <inttypes.h> 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 <memory.h> header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `mmap' function. */ +#undef HAVE_MMAP + +/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */ +#undef HAVE_NDIR_H + +/* Define to 1 if you have the <paths.h> header file. */ +#undef HAVE_PATHS_H + +/* Define to 1 if you have the <poll.h> 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 <ranlib.h> header file. */ +#undef HAVE_RANLIB_H + +/* Define to 1 if you have the `realpath' function. */ +#undef HAVE_REALPATH + +/* 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 `setpgid' function. */ +#undef HAVE_SETPGID + +/* Define to 1 if you have the `setsid' function. */ +#undef HAVE_SETSID + +/* 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 <stdint.h> header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the <stdlib.h> header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `strerror' function. */ +#undef HAVE_STRERROR + +/* Define to 1 if you have the `stresep' function. */ +#undef HAVE_STRESEP + +/* Define to 1 if you have the `strftime' function. */ +#undef HAVE_STRFTIME + +/* Define to 1 if you have the <strings.h> header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the <string.h> header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `strlcpy' function. */ +#undef HAVE_STRLCPY + +/* Define to 1 if you have the `strsep' function. */ +#undef HAVE_STRSEP + +/* 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 `struct stat' is a member of `st_rdev'. */ +#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 <sys/dir.h> header file, and it defines `DIR'. + */ +#undef HAVE_SYS_DIR_H + +/* Define to 1 if you have the <sys/mman.h> header file. */ +#undef HAVE_SYS_MMAN_H + +/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'. + */ +#undef HAVE_SYS_NDIR_H + +/* Define to 1 if you have the <sys/select.h> header file. */ +#undef HAVE_SYS_SELECT_H + +/* Define to 1 if you have the <sys/socket.h> header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the <sys/time.h> header file. */ +#undef HAVE_SYS_TIME_H + +/* Define to 1 if you have the <sys/types.h> header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the <sys/uio.h> header file. */ +#undef HAVE_SYS_UIO_H + +/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */ +#undef HAVE_SYS_WAIT_H + +/* Define to 1 if you have the <unistd.h> header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `unsetenv' function. */ +#undef HAVE_UNSETENV + +/* Define to 1 if you have the <utime.h> header file. */ +#undef HAVE_UTIME_H + +/* Define to 1 if you have the `vfork' function. */ +#undef HAVE_VFORK + +/* Define to 1 if you have the <vfork.h> 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 you have the `warn' function. */ +#undef HAVE_WARN + +/* Define to 1 if you have the `warnx' function. */ +#undef HAVE_WARNX + +/* 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 home page for this package. */ +#undef PACKAGE_URL + +/* 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 <sys/stat.h> 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 <sys/time.h> and <time.h>. */ +#undef TIME_WITH_SYS_TIME + +/* Define to 1 if your <sys/time.h> declares `struct tm'. */ +#undef TM_IN_SYS_TIME + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# undef _ALL_SOURCE +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# undef _GNU_SOURCE +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# undef _POSIX_PTHREAD_SEMANTICS +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# undef _TANDEM_SOURCE +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# undef __EXTENSIONS__ +#endif + + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +# undef WORDS_BIGENDIAN +# endif +#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 int' if <sys/types.h> does not define. */ +#undef off_t + +/* Define to `int' if <sys/types.h> does not define. */ +#undef pid_t + +/* Define to `unsigned int' if <sys/types.h> does not define. */ +#undef size_t + +/* Define as `fork' if `vfork' does not work. */ +#undef vfork diff --git a/20120704/configure b/20120704/configure new file mode 100755 index 0000000..ee479f1 --- /dev/null +++ b/20120704/configure @@ -0,0 +1,7134 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.64 for bmake 20120620. +# +# Report bugs to <sjg@NetBSD.org>. +# +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 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 more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +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 +IFS=$as_save_IFS + + ;; +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 + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + # We cannot yet assume a decent shell, so we have to provide a + # neutralization value for shells without unset; and this also + # works around shells that cannot unset nonexistent variables. + BASH_ENV=/dev/null + ENV=/dev/null + (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"} +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." + else + $as_echo "$0: Please tell bug-autoconf@gnu.org and sjg@NetBSD.org +$0: about your system, including any error possibly output +$0: before this message. Then install a modern shell, or +$0: manually run the script under such a shell if you do +$0: have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error ERROR [LINENO LOG_FD] +# --------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with status $?, using 1 if that was 0. +as_fn_error () +{ + as_status=$?; test $as_status -eq 0 && as_status=1 + if test "$3"; then + as_lineno=${as_lineno-"$2"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $1" >&$3 + fi + $as_echo "$as_me: error: $1" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; 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 + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# 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 + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_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 sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in #( + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval 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="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 7<&0 </dev/null 6>&1 + +# 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` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME='bmake' +PACKAGE_TARNAME='bmake' +PACKAGE_VERSION='20120620' +PACKAGE_STRING='bmake 20120620' +PACKAGE_BUGREPORT='sjg@NetBSD.org' +PACKAGE_URL='' + +# Factoring default headers for most tests. +ac_includes_default="\ +#include <stdio.h> +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H +# include <sys/stat.h> +#endif +#ifdef STDC_HEADERS +# include <stdlib.h> +# include <stddef.h> +#else +# ifdef HAVE_STDLIB_H +# include <stdlib.h> +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include <memory.h> +# endif +# include <string.h> +#endif +#ifdef HAVE_STRINGS_H +# include <strings.h> +#endif +#ifdef HAVE_INTTYPES_H +# include <inttypes.h> +#endif +#ifdef HAVE_STDINT_H +# include <stdint.h> +#endif +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif" + +ac_subst_vars='LTLIBOBJS +filemon_h +use_meta +diff_u +GCC +INSTALL +default_sys_path +mksrc +machine_arch +force_machine +machine +LIBOBJS +ac_exe_suffix +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +EGREP +GREP +CPP +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +with_defshell +with_meta +with_filemon +with_machine +with_force_machine +with_force_machine_arch +with_machine_arch +with_default_sys_path +with_path_objdirprefix +enable_pwd_override +enable_check_make_chdir +with_mksrc +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CPP' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# 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. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -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) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$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 ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$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 ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + 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 ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$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_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=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 ;; + + -*) as_fn_error "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information." + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_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'` + as_fn_error "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error "expected an absolute directory name for --$ac_var: $ac_val" +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 + $as_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 + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error "pwd does not report name of working directory" + + +# 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 the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + 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 + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# 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 bmake 20120620 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 \`..'] + +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] + --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] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/bmake] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of bmake 20120620:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options + --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 use SHELL by default - must be sh compatible, use sh or ksh to pick the internal definitions + --without-meta dissable use of meta-mode + --with-filemon=path/filemon.h indicate path to filemon.h for meta-mode + --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<lib dir> if you have libraries in a + nonstandard directory <lib dir> + LIBS libraries to pass to the linker, e.g. -l<library> + CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I<include dir> if + you have headers in a nonstandard directory <include dir> + 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. + +Report bugs to <sjg@NetBSD.org>. +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested 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 + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +bmake configure 20120620 +generated by GNU Autoconf 2.64 + +Copyright (C) 2009 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 +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + return $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + return $ac_retval + +} # ac_fn_c_try_cpp + +# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists, giving a warning if it cannot be compiled using +# the include files in INCLUDES and setting the cache variable VAR +# accordingly. +ac_fn_c_check_header_mongrel () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : + $as_echo_n "(cached) " >&6 +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 +$as_echo_n "checking $2 usability... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_header_compiler=yes +else + ac_header_compiler=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 +$as_echo_n "checking $2 presence... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <$2> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + ac_header_preproc=yes +else + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( + yes:no: ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; + no:yes:* ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} +( cat <<\_ASBOX +## ----------------------------- ## +## Report this to sjg@NetBSD.org ## +## ----------------------------- ## +_ASBOX + ) | sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=\$ac_header_compiler" +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +fi + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + +} # ac_fn_c_check_header_mongrel + +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : + ac_retval=0 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + return $ac_retval + +} # ac_fn_c_try_run + +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + +} # ac_fn_c_check_header_compile + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + return $ac_retval + +} # ac_fn_c_try_link + +# ac_fn_c_check_type LINENO TYPE VAR INCLUDES +# ------------------------------------------- +# Tests whether TYPE exists after having included INCLUDES, setting cache +# variable VAR accordingly. +ac_fn_c_check_type () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof ($2)) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof (($2))) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + eval "$3=yes" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + +} # ac_fn_c_check_type + +# ac_fn_c_check_decl LINENO SYMBOL VAR +# ------------------------------------ +# Tests whether SYMBOL is declared, setting cache variable VAR accordingly. +ac_fn_c_check_decl () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $2 is declared" >&5 +$as_echo_n "checking whether $2 is declared... " >&6; } +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +#ifndef $2 + (void) $2; +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + +} # ac_fn_c_check_decl + +# ac_fn_c_check_func LINENO FUNC VAR +# ---------------------------------- +# Tests whether FUNC exists, setting the cache variable VAR accordingly +ac_fn_c_check_func () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Define $2 to an innocuous variant, in case <limits.h> declares $2. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef $2 + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $2 (); +/* 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_$2 || defined __stub___$2 +choke me +#endif + +int +main () +{ +return $2 (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + +} # ac_fn_c_check_func + +# ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES +# ---------------------------------------------------- +# Tries to find if the field MEMBER exists in type AGGR, after including +# INCLUDES, setting cache variable VAR accordingly. +ac_fn_c_check_member () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5 +$as_echo_n "checking for $2.$3... " >&6; } +if { as_var=$4; eval "test \"\${$as_var+set}\" = set"; }; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$5 +int +main () +{ +static $2 ac_aggr; +if (ac_aggr.$3) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$4=yes" +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$5 +int +main () +{ +static $2 ac_aggr; +if (sizeof ac_aggr.$3) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$4=yes" +else + eval "$4=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$4 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + +} # ac_fn_c_check_member +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by bmake $as_me 20120620, which was +generated by GNU Autoconf 2.64. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +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` +/usr/bin/hostinfo = `(/usr/bin/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=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&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_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=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append 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 + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset 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: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +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, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------------- ## +## File substitutions. ## +## ------------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +$as_echo "/* confdefs.h */" > 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 + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + ac_site_file1=$CONFIG_SITE +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_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 + { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +$as_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 $ac_precious_vars; 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,) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_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 + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_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. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +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 was given. +if test "${with_defshell+set}" = set; then : + withval=$with_defshell; case "${withval}" in +yes) as_fn_error "bad value ${withval} given for bmake DEFSHELL" "$LINENO" 5 ;; +no) ;; +*) case "$with_defshell" in + sh) DEFSHELL_INDEX=DEFSHELL_INDEX_SH;; # it's the default anyway + ksh) DEFSHELL_INDEX=DEFSHELL_INDEX_KSH;; + csh) DEFSHELL_INDEX=DEFSHELL_INDEX_CSH;; # kidding right? + *) defshell_path=$with_defshell;; # better be sh compatible! + esac + ;; + esac +fi + +use_meta=yes + +# Check whether --with-meta was given. +if test "${with_meta+set}" = set; then : + withval=$with_meta; case "${withval}" in +yes|no) use_meta=${withval};; +*) as_fn_error "bad value ${withval} given for meta" "$LINENO" 5 ;; +esac +fi + + +# Check whether --with-filemon was given. +if test "${with_filemon+set}" = set; then : + withval=$with_filemon; case "/${withval}" in +/no|*/filemon.h) filemon_h="${withval}";; +*/filemon*) filemon_h="${withval}/filemon.h";; +*) as_fn_error "bad value ${withval} given for filemon" "$LINENO" 5 ;; +esac +else + +OS=`uname -s` +for d in "/usr/include/dev/filemon" "$prefix/include/dev/filemon" "$srcdir/filemon" "$srcdir/../filemon" "$srcdir/../../sys/dev/filemon" +do + for x in "/$OS" "" + do + filemon_h="$d$x/filemon.h" + test -s "$filemon_h" && break + done + test -s "$filemon_h" && break +done +test -s "${filemon_h:-/dev/null}" || filemon_h=no + +fi + +case "$use_meta" in +yes) + case "$filemon_h" in + *.h) echo "Using: filemon=$filemon_h" >&6;; + esac + ;; +esac +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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then : + $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then : + $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then : + $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + 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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then : + $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 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 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + 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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then : + $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then : + $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error "no acceptable C compiler found in \$PATH +See \`config.log' for more details." "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + rm -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdio.h> +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out conftest.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. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +if test -z "$ac_file"; then : + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ as_fn_set_status 77 +as_fn_error "C compiler cannot create executables +See \`config.log' for more details." "$LINENO" 5; }; } +fi +ac_exeext=$ac_cv_exeext + +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out conftest.out +ac_clean_files=$ac_clean_files_save +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; 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 | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." "$LINENO" 5; } +fi +rm -f conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if test "${ac_cv_objext+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error "cannot compute suffix of object files: cannot compile +See \`config.log' for more details." "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if test "${ac_cv_c_compiler_gnu+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if test "${ac_cv_prog_cc_g+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if test "${ac_cv_prog_cc_c89+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdarg.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +/* 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; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +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 +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +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_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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&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 : + $as_echo_n "(cached) " >&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 <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> 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 confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # 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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$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 <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> 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 confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # 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 + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." "$LINENO" 5; } +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 + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if test "${ac_cv_path_GREP+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if test "${ac_cv_path_EGREP+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if test "${ac_cv_header_stdc+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <float.h> + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err 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 confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <string.h> + +_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 confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdlib.h> + +_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 confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <ctype.h> +#include <stdlib.h> +#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)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +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=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +eval as_val=\$$as_ac_Header + if test "x$as_val" = x""yes; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + ac_fn_c_check_header_mongrel "$LINENO" "minix/config.h" "ac_cv_header_minix_config_h" "$ac_includes_default" +if test "x$ac_cv_header_minix_config_h" = x""yes; then : + MINIX=yes +else + MINIX= +fi + + + if test "$MINIX" = yes; then + +$as_echo "#define _POSIX_SOURCE 1" >>confdefs.h + + +$as_echo "#define _POSIX_1_SOURCE 2" >>confdefs.h + + +$as_echo "#define _MINIX 1" >>confdefs.h + + fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5 +$as_echo_n "checking whether it is safe to define __EXTENSIONS__... " >&6; } +if test "${ac_cv_safe_to_define___extensions__+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +# define __EXTENSIONS__ 1 + $ac_includes_default +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_safe_to_define___extensions__=yes +else + ac_cv_safe_to_define___extensions__=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5 +$as_echo "$ac_cv_safe_to_define___extensions__" >&6; } + test $ac_cv_safe_to_define___extensions__ = yes && + $as_echo "#define __EXTENSIONS__ 1" >>confdefs.h + + $as_echo "#define _ALL_SOURCE 1" >>confdefs.h + + $as_echo "#define _GNU_SOURCE 1" >>confdefs.h + + $as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h + + $as_echo "#define _TANDEM_SOURCE 1" >>confdefs.h + + +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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then : + $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then : + $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then : + $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + 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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then : + $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 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 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + 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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then : + $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then : + $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error "no acceptable C compiler found in \$PATH +See \`config.log' for more details." "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + rm -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if test "${ac_cv_c_compiler_gnu+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if test "${ac_cv_prog_cc_g+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if test "${ac_cv_prog_cc_c89+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdarg.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +/* 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; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +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 +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +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 $ac_cv_c_compiler_gnu = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC needs -traditional" >&5 +$as_echo_n "checking whether $CC needs -traditional... " >&6; } +if test "${ac_cv_prog_gcc_traditional+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_pattern="Autoconf.*'x'" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <sgtty.h> +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 confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <termio.h> +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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_gcc_traditional" >&5 +$as_echo "$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 + for ac_t in install-sh install.sh shtool; do + if test -f "$ac_dir/$ac_t"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/$ac_t -c" + break 2 + fi + done +done +if test -z "$ac_aux_dir"; then + as_fn_error "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + +# 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" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then : + $as_echo_n "(cached) " >&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/* | \ + ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ + /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 { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$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 + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + + done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +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. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 +$as_echo "$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 $ECHO_N "checking if sh will pass .MAKE. variables... $ECHO_C" >&6 +ok=`env .MAKE.LEVEL=1 /bin/sh -c env | grep LEVEL=` +case "$ok" in +"") echo no >&6; CPPFLAGS="${CPPFLAGS} -DNEED_MAKE_LEVEL_SAFE";; +*) echo yes >&6;; +esac + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if test "${ac_cv_header_stdc+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <float.h> + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err 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 confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <string.h> + +_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 confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdlib.h> + +_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 confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <ctype.h> +#include <stdlib.h> +#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)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sys/wait.h that is POSIX.1 compatible" >&5 +$as_echo_n "checking for sys/wait.h that is POSIX.1 compatible... " >&6; } +if test "${ac_cv_header_sys_wait_h+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <sys/types.h> +#include <sys/wait.h> +#ifndef WEXITSTATUS +# define WEXITSTATUS(stat_val) ((unsigned int) (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 +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_sys_wait_h=yes +else + ac_cv_header_sys_wait_h=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_sys_wait_h" >&5 +$as_echo "$ac_cv_header_sys_wait_h" >&6; } +if test $ac_cv_header_sys_wait_h = yes; then + +$as_echo "#define HAVE_SYS_WAIT_H 1" >>confdefs.h + +fi + +ac_header_dirent=no +for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do + as_ac_Header=`$as_echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_hdr that defines DIR" >&5 +$as_echo_n "checking for $ac_hdr that defines DIR... " >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <sys/types.h> +#include <$ac_hdr> + +int +main () +{ +if ((DIR *) 0) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$as_ac_Header=yes" +else + eval "$as_ac_Header=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$as_ac_Header + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +eval as_val=\$$as_ac_Header + if test "x$as_val" = x""yes; then : + cat >>confdefs.h <<_ACEOF +#define `$as_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 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5 +$as_echo_n "checking for library containing opendir... " >&6; } +if test "${ac_cv_search_opendir+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char opendir (); +int +main () +{ +return opendir (); + ; + return 0; +} +_ACEOF +for ac_lib in '' dir; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_opendir=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if test "${ac_cv_search_opendir+set}" = set; then : + break +fi +done +if test "${ac_cv_search_opendir+set}" = set; then : + +else + ac_cv_search_opendir=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5 +$as_echo "$ac_cv_search_opendir" >&6; } +ac_res=$ac_cv_search_opendir +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5 +$as_echo_n "checking for library containing opendir... " >&6; } +if test "${ac_cv_search_opendir+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char opendir (); +int +main () +{ +return opendir (); + ; + return 0; +} +_ACEOF +for ac_lib in '' x; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_opendir=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if test "${ac_cv_search_opendir+set}" = set; then : + break +fi +done +if test "${ac_cv_search_opendir+set}" = set; then : + +else + ac_cv_search_opendir=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5 +$as_echo "$ac_cv_search_opendir" >&6; } +ac_res=$ac_cv_search_opendir +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + +fi + +for ac_header in \ + ar.h \ + err.h \ + fcntl.h \ + paths.h \ + poll.h \ + ranlib.h \ + string.h \ + sys/mman.h \ + sys/select.h \ + sys/socket.h \ + sys/time.h \ + sys/uio.h \ + unistd.h \ + utime.h \ + +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +eval as_val=\$$as_ac_Header + if test "x$as_val" = x""yes; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +ac_fn_c_check_header_mongrel "$LINENO" "sys/cdefs.h" "ac_cv_header_sys_cdefs_h" "$ac_includes_default" +if test "x$ac_cv_header_sys_cdefs_h" = x""yes; then : + echo $ECHO_N "checking whether sys/cdefs.h is compatible... $ECHO_C" >&6 +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <sys/cdefs.h> +#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 + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for __attribute__" >&5 +$as_echo_n "checking for __attribute__... " >&6; } +if test "${ac_cv___attribute__+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include <stdlib.h> + +int +main () +{ + +static void foo(void) __attribute__ ((noreturn)); + +static void +foo(void) +{ + exit(1); +} + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv___attribute__=yes +else + ac_cv___attribute__=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +if test "$ac_cv___attribute__" = "yes"; then + +$as_echo "#define HAVE___ATTRIBUTE__ 1" >>confdefs.h + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv___attribute__" >&5 +$as_echo "$ac_cv___attribute__" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 +$as_echo_n "checking whether byte ordering is bigendian... " >&6; } +if test "${ac_cv_c_bigendian+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_c_bigendian=unknown + # See if we're dealing with a universal compiler. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifndef __APPLE_CC__ + not a universal capable compiler + #endif + typedef int dummy; + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + # Check for potential -arch flags. It is not universal unless + # there are at least two -arch flags with different values. + ac_arch= + ac_prev= + for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do + if test -n "$ac_prev"; then + case $ac_word in + i?86 | x86_64 | ppc | ppc64) + if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then + ac_arch=$ac_word + else + ac_cv_c_bigendian=universal + break + fi + ;; + esac + ac_prev= + elif test "x$ac_word" = "x-arch"; then + ac_prev=arch + fi + done +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if test $ac_cv_c_bigendian = unknown; then + # See if sys/param.h defines the BYTE_ORDER macro. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <sys/types.h> + #include <sys/param.h> + +int +main () +{ +#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ + && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ + && LITTLE_ENDIAN) + bogus endian macros + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + # It does; now see whether it defined to BIG_ENDIAN or not. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <sys/types.h> + #include <sys/param.h> + +int +main () +{ +#if BYTE_ORDER != BIG_ENDIAN + not big endian + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_bigendian=yes +else + ac_cv_c_bigendian=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi + if test $ac_cv_c_bigendian = unknown; then + # See if <limits.h> defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <limits.h> + +int +main () +{ +#if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) + bogus endian macros + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + # It does; now see whether it defined to _BIG_ENDIAN or not. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <limits.h> + +int +main () +{ +#ifndef _BIG_ENDIAN + not big endian + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_bigendian=yes +else + ac_cv_c_bigendian=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi + if test $ac_cv_c_bigendian = unknown; then + # Compile a test program. + if test "$cross_compiling" = yes; then : + # Try to guess by grepping values from an object file. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +short int ascii_mm[] = + { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; + short int ascii_ii[] = + { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; + int use_ascii (int i) { + return ascii_mm[i] + ascii_ii[i]; + } + short int ebcdic_ii[] = + { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; + short int ebcdic_mm[] = + { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; + int use_ebcdic (int i) { + return ebcdic_mm[i] + ebcdic_ii[i]; + } + extern int foo; + +int +main () +{ +return use_ascii (foo) == use_ebcdic (foo); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; 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 +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + /* Are we little or big endian? From Harbison&Steele. */ + union + { + long int l; + char c[sizeof (long int)]; + } u; + u.l = 1; + return u.c[sizeof (long int) - 1] == 1; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_c_bigendian=no +else + ac_cv_c_bigendian=yes +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 +$as_echo "$ac_cv_c_bigendian" >&6; } + case $ac_cv_c_bigendian in #( + yes) + $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h +;; #( + no) + ;; #( + universal) + +$as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h + + ;; #( + *) + as_fn_error "unknown endianness + presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; + esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5 +$as_echo_n "checking for an ANSI C-conforming const... " >&6; } +if test "${ac_cv_c_const+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* 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 cs; + /* SunOS 4.1.1 cc rejects this. */ + char const *const *pcpcc; + char **ppc; + /* 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"; + pcpcc = &g + (g ? g-g : 0); + /* HPUX 7.0 cc rejects these. */ + ++pcpcc; + ppc = (char**) pcpcc; + pcpcc = (char const *const *) ppc; + { /* SCO 3.2v4 cc rejects this. */ + char *t; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; + if (s) return 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; + if (!foo) return 0; + } + return !cs[0] && !zero.x; +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_const=yes +else + ac_cv_c_const=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const" >&5 +$as_echo "$ac_cv_c_const" >&6; } +if test $ac_cv_c_const = no; then + +$as_echo "#define const /**/" >>confdefs.h + +fi + +ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "$ac_includes_default" +if test "x$ac_cv_type_off_t" = x""yes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define off_t long int +_ACEOF + +fi + +ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default" +if test "x$ac_cv_type_pid_t" = x""yes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define pid_t int +_ACEOF + +fi + +ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" +if test "x$ac_cv_type_size_t" = x""yes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned int +_ACEOF + +fi + +ac_fn_c_check_decl "$LINENO" "sys_siglist" "ac_cv_have_decl_sys_siglist" "#include <signal.h> +/* NetBSD declares sys_siglist in unistd.h. */ +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +" +if test "x$ac_cv_have_decl_sys_siglist" = x""yes; then : + ac_have_decl=1 +else + ac_have_decl=0 +fi + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_SYS_SIGLIST $ac_have_decl +_ACEOF + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether time.h and sys/time.h may both be included" >&5 +$as_echo_n "checking whether time.h and sys/time.h may both be included... " >&6; } +if test "${ac_cv_header_time+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <sys/types.h> +#include <sys/time.h> +#include <time.h> + +int +main () +{ +if ((struct tm *) 0) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_time=yes +else + ac_cv_header_time=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_time" >&5 +$as_echo "$ac_cv_header_time" >&6; } +if test $ac_cv_header_time = yes; then + +$as_echo "#define TIME_WITH_SYS_TIME 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether struct tm is in sys/time.h or time.h" >&5 +$as_echo_n "checking whether struct tm is in sys/time.h or time.h... " >&6; } +if test "${ac_cv_struct_tm+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <sys/types.h> +#include <time.h> + +int +main () +{ +struct tm tm; + int *p = &tm.tm_sec; + return !p; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_struct_tm=time.h +else + ac_cv_struct_tm=sys/time.h +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_struct_tm" >&5 +$as_echo "$ac_cv_struct_tm" >&6; } +if test $ac_cv_struct_tm = sys/time.h; then + +$as_echo "#define TM_IN_SYS_TIME 1" >>confdefs.h + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking return type of signal handlers" >&5 +$as_echo_n "checking return type of signal handlers... " >&6; } +if test "${ac_cv_type_signal+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <sys/types.h> +#include <signal.h> + +int +main () +{ +return *(signal (0, 0)) (0) == 1; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_type_signal=int +else + ac_cv_type_signal=void +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_signal" >&5 +$as_echo "$ac_cv_type_signal" >&6; } + +cat >>confdefs.h <<_ACEOF +#define RETSIGTYPE $ac_cv_type_signal +_ACEOF + + +for ac_header in vfork.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "vfork.h" "ac_cv_header_vfork_h" "$ac_includes_default" +if test "x$ac_cv_header_vfork_h" = x""yes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_VFORK_H 1 +_ACEOF + +fi + +done + +for ac_func in fork vfork +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +eval as_val=\$$as_ac_var + if test "x$as_val" = x""yes; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +if test "x$ac_cv_func_fork" = xyes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working fork" >&5 +$as_echo_n "checking for working fork... " >&6; } +if test "${ac_cv_func_fork_works+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ac_cv_func_fork_works=cross +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + /* By Ruediger Kuhlmann. */ + return fork () < 0; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_func_fork_works=yes +else + ac_cv_func_fork_works=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_fork_works" >&5 +$as_echo "$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 + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5 +$as_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 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working vfork" >&5 +$as_echo_n "checking for working vfork... " >&6; } +if test "${ac_cv_func_vfork_works+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ac_cv_func_vfork_works=cross +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Thanks to Paul Eggert for this test. */ +$ac_includes_default +#include <sys/wait.h> +#ifdef HAVE_VFORK_H +# include <vfork.h> +#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 <vfork.h>, but some compilers + (e.g. gcc -O) don't grok <vfork.h>. 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) + ; + return ( + /* 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 +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_func_vfork_works=yes +else + ac_cv_func_vfork_works=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_vfork_works" >&5 +$as_echo "$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 + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5 +$as_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 + +$as_echo "#define HAVE_WORKING_VFORK 1" >>confdefs.h + +else + +$as_echo "#define vfork fork" >>confdefs.h + +fi +if test "x$ac_cv_func_fork_works" = xyes; then + +$as_echo "#define HAVE_WORKING_FORK 1" >>confdefs.h + +fi + +for ac_func in vprintf +do : + ac_fn_c_check_func "$LINENO" "vprintf" "ac_cv_func_vprintf" +if test "x$ac_cv_func_vprintf" = x""yes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_VPRINTF 1 +_ACEOF + +ac_fn_c_check_func "$LINENO" "_doprnt" "ac_cv_func__doprnt" +if test "x$ac_cv_func__doprnt" = x""yes; then : + +$as_echo "#define HAVE_DOPRNT 1" >>confdefs.h + +fi + +fi +done + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for wait3 that fills in rusage" >&5 +$as_echo_n "checking for wait3 that fills in rusage... " >&6; } +if test "${ac_cv_func_wait3_rusage+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ac_cv_func_wait3_rusage=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/wait.h> +/* 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); + return (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 +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_func_wait3_rusage=yes +else + ac_cv_func_wait3_rusage=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_wait3_rusage" >&5 +$as_echo "$ac_cv_func_wait3_rusage" >&6; } +if test $ac_cv_func_wait3_rusage = yes; then + +$as_echo "#define HAVE_WAIT3 1" >>confdefs.h + +fi + +for ac_func in \ + err \ + errx \ + getcwd \ + getenv \ + getopt \ + getwd \ + killpg \ + mmap \ + putenv \ + select \ + setenv \ + setpgid \ + setsid \ + sigaction \ + sigvec \ + snprintf \ + strerror \ + strftime \ + strsep \ + strtod \ + strtol \ + unsetenv \ + vsnprintf \ + wait3 \ + wait4 \ + waitpid \ + warn \ + warnx \ + +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +eval as_val=\$$as_ac_var + if test "x$as_val" = x""yes; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +for ac_func in \ + realpath \ + dirname \ + stresep \ + strlcpy \ + +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +eval as_val=\$$as_ac_var + if test "x$as_val" = x""yes; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + case " $LIBOBJS " in + *" $ac_func.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS $ac_func.$ac_objext" + ;; +esac + +fi +done + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for emalloc in -lutil" >&5 +$as_echo_n "checking for emalloc in -lutil... " >&6; } +if test "${ac_cv_lib_util_emalloc+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lutil $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char emalloc (); +int +main () +{ +return emalloc (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_util_emalloc=yes +else + ac_cv_lib_util_emalloc=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_util_emalloc" >&5 +$as_echo "$ac_cv_lib_util_emalloc" >&6; } +if test "x$ac_cv_lib_util_emalloc" = x""yes; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for erealloc in -lutil" >&5 +$as_echo_n "checking for erealloc in -lutil... " >&6; } +if test "${ac_cv_lib_util_erealloc+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lutil $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char erealloc (); +int +main () +{ +return erealloc (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_util_erealloc=yes +else + ac_cv_lib_util_erealloc=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_util_erealloc" >&5 +$as_echo "$ac_cv_lib_util_erealloc" >&6; } +if test "x$ac_cv_lib_util_erealloc" = x""yes; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for estrdup in -lutil" >&5 +$as_echo_n "checking for estrdup in -lutil... " >&6; } +if test "${ac_cv_lib_util_estrdup+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lutil $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char estrdup (); +int +main () +{ +return estrdup (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_util_estrdup=yes +else + ac_cv_lib_util_estrdup=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_util_estrdup" >&5 +$as_echo "$ac_cv_lib_util_estrdup" >&6; } +if test "x$ac_cv_lib_util_estrdup" = x""yes; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for estrndup in -lutil" >&5 +$as_echo_n "checking for estrndup in -lutil... " >&6; } +if test "${ac_cv_lib_util_estrndup+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lutil $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char estrndup (); +int +main () +{ +return estrndup (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_util_estrndup=yes +else + ac_cv_lib_util_estrndup=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_util_estrndup" >&5 +$as_echo "$ac_cv_lib_util_estrndup" >&6; } +if test "x$ac_cv_lib_util_estrndup" = x""yes; then : + LIBS="$LIBS -lutil" + CPPFLAGS="$CPPFLAGS -DUSE_EMALLOC" +fi + +fi + +fi + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stat file-mode macros are broken" >&5 +$as_echo_n "checking whether stat file-mode macros are broken... " >&6; } +if test "${ac_cv_header_stat_broken+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <sys/types.h> +#include <sys/stat.h> + +#if defined S_ISBLK && defined S_IFDIR +extern char c1[S_ISBLK (S_IFDIR) ? -1 : 1]; +#endif + +#if defined S_ISBLK && defined S_IFCHR +extern char c2[S_ISBLK (S_IFCHR) ? -1 : 1]; +#endif + +#if defined S_ISLNK && defined S_IFREG +extern char c3[S_ISLNK (S_IFREG) ? -1 : 1]; +#endif + +#if defined S_ISSOCK && defined S_IFREG +extern char c4[S_ISSOCK (S_IFREG) ? -1 : 1]; +#endif + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stat_broken=no +else + ac_cv_header_stat_broken=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stat_broken" >&5 +$as_echo "$ac_cv_header_stat_broken" >&6; } +if test $ac_cv_header_stat_broken = yes; then + +$as_echo "#define STAT_MACROS_BROKEN 1" >>confdefs.h + +fi + +ac_fn_c_check_member "$LINENO" "struct stat" "st_rdev" "ac_cv_member_struct_stat_st_rdev" "$ac_includes_default" +if test "x$ac_cv_member_struct_stat_st_rdev" = x""yes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_STAT_ST_RDEV 1 +_ACEOF + + +$as_echo "#define HAVE_ST_RDEV 1" >>confdefs.h + +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 +echo "checking for MACHINE & MACHINE_ARCH..." >&6 +cat > conftest.$ac_ext <<EOF +#include "confdefs.h" +#include <sys/param.h> +#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 was given. +if test "${with_machine+set}" = set; then : + withval=$with_machine; case "${withval}" in +yes) as_fn_error "bad value ${withval} given for bmake MACHINE" "$LINENO" 5 ;; +no) ;; +generic) machine=`$srcdir/machine.sh`;; +*) machine=$with_machine;; +esac +fi + +force_machine= + +# Check whether --with-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 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 was given. +if test "${with_machine_arch+set}" = set; then : + withval=$with_machine_arch; case "${withval}" in +yes) as_fn_error "bad value ${withval} given for bmake MACHINE_ARCH" "$LINENO" 5 ;; +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 was given. +if test "${with_default_sys_path+set}" = set; then : + withval=$with_default_sys_path; case "${withval}" in +yes) as_fn_error "bad value ${withval} given for bmake _PATH_DEFSYSPATH" "$LINENO" 5 ;; +no) ;; +*) default_sys_path="$with_default_sys_path" + ;; +esac +fi + + +# Check whether --with-path-objdirprefix was given. +if test "${with_path_objdirprefix+set}" = set; then : + withval=$with_path_objdirprefix; case "${withval}" in +yes) as_fn_error "bad value ${withval} given for bmake _PATH_OBJDIRPREFIX" "$LINENO" 5 ;; +no) CPPFLAGS="$CPPFLAGS -DNO_PATH_OBJDIRPREFIX" ;; +*) CPPFLAGS="$CPPFLAGS \"-D_PATH_OBJDIRPREFIX=\\\"$with_path-objdir\\\"\"" ;; +esac +fi + +# Check whether --enable-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" ;; +*) as_fn_error "bad value ${enableval} given for pwd-override option" "$LINENO" 5 ;; +esac +fi + +# Check whether --enable-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" ;; +*) as_fn_error "bad value ${enableval} given for check-make-chdir option" "$LINENO" 5 ;; +esac +fi + + +# Check whether --with-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 || +as_fn_error "bad value ${withval} given for mksrc cannot find install-mk" "$LINENO" 5 +;; +esac + +fi + +srcdir=`cd $srcdir && pwd` +for mksrc in $mksrc $srcdir/mk $srcdir/../mk mk +do + test -s $mksrc/install-mk || continue + mksrc=`cd $mksrc && pwd` + break +done +mksrc=`echo $mksrc | sed "s,$srcdir,\\\${srcdir},"` +echo "Using: MKSRC=$mksrc" 1>&6 +if test -x /usr/xpg4/bin/sh; then + defshell_path=${defshell_path:-/usr/xpg4/bin/sh} +fi +if test -n "$defshell_path"; then + echo "Using: SHELL=$defshell_path" >&6 + +cat >>confdefs.h <<_ACEOF +#define DEFSHELL_CUSTOM "$defshell_path" +_ACEOF + +fi +if test -n "$DEFSHELL_INDEX"; then + +cat >>confdefs.h <<_ACEOF +#define DEFSHELL_INDEX $DEFSHELL_INDEX +_ACEOF + +fi + + + + + + + + + + +ac_config_files="$ac_config_files Makefile make-bootstrap.sh 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, we kill variables containing newlines. +# 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. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}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 "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + 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" && + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + cat confcache >$cache_file + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + 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}' + +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_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + + +: ${CONFIG_STATUS=./config.status} +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $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} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +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 +IFS=$as_save_IFS + + ;; +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 + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error ERROR [LINENO LOG_FD] +# --------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with status $?, using 1 if that was 0. +as_fn_error () +{ + as_status=$?; test $as_status -eq 0 && as_status=1 + if test "$3"; then + as_lineno=${as_lineno-"$2"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $1" >&$3 + fi + $as_echo "$as_me: error: $1" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; 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 + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# 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 + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in #( + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval 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="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by bmake $as_me 20120620, which was +generated by GNU Autoconf 2.64. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + -q, --quiet, --silent + 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 <sjg@NetBSD.org>." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_version="\\ +bmake config.status 20120620 +configured by $0, generated by GNU Autoconf 2.64, + with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" + +Copyright (C) 2009 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +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 + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + 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 || ac_write_fail=1 +if \$ac_cs_recheck; then + set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "make-bootstrap.sh") CONFIG_FILES="$CONFIG_FILES make-bootstrap.sh" ;; + "unit-tests/Makefile") CONFIG_FILES="$CONFIG_FILES unit-tests/Makefile" ;; + + *) as_fn_error "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + 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 against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= + trap 'exit_status=$? + { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error "cannot create a temporary directory in ." "$LINENO" 5 + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '$'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\).*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\).*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' <conf$$subs.awk | sed ' +/^[^""]/{ + N + s/\n// +} +' >>$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \ + || as_fn_error "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# 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 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_t=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_t"; then + break + elif $ac_last_try; then + as_fn_error "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' <confdefs.h | sed ' +s/'"$ac_delim"'/"\\\ +"/g' >>$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #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. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # 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. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$tmp/stdin" \ + || as_fn_error "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;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&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \ + || as_fn_error "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&2;} + + rm -f "$tmp/stdin" + case $ac_file in + -) cat "$tmp/out" && rm -f "$tmp/out";; + *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";; + esac \ + || as_fn_error "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" + } >"$tmp/config.h" \ + || as_fn_error "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$tmp/config.h" "$ac_file" \ + || as_fn_error "could not create $ac_file" "$LINENO" 5 + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error "could not create -" "$LINENO" 5 + fi + ;; + + + esac + +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# 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 || as_fn_exit $? +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + + +cat <<EOF + +You can now run + + sh ./make-bootstrap.sh + +to produce a fully functional bmake. + +EOF diff --git a/20120704/configure.in b/20120704/configure.in new file mode 100644 index 0000000..156034b9 --- /dev/null +++ b/20120704/configure.in @@ -0,0 +1,370 @@ +dnl +dnl RCSid: +dnl $Id: configure.in,v 1.45 2012/06/20 22:43:41 sjg Exp $ +dnl +dnl Process this file with autoconf to produce a configure script +dnl +AC_INIT([bmake], [20120620], [sjg@NetBSD.org]) +AC_CONFIG_HEADER(config.h) + +dnl +AC_ARG_WITH(defshell, +[ --with-defshell=SHELL use SHELL by default - must be sh compatible, use sh or ksh to pick the internal definitions], +[case "${withval}" in +yes) AC_MSG_ERROR(bad value ${withval} given for bmake DEFSHELL) ;; +no) ;; +*) case "$with_defshell" in + sh) DEFSHELL_INDEX=DEFSHELL_INDEX_SH;; # it's the default anyway + ksh) DEFSHELL_INDEX=DEFSHELL_INDEX_KSH;; + csh) DEFSHELL_INDEX=DEFSHELL_INDEX_CSH;; # kidding right? + *) defshell_path=$with_defshell;; # better be sh compatible! + esac + ;; + esac]) +dnl +use_meta=yes +AC_ARG_WITH(meta, +[ --without-meta dissable use of meta-mode], +[case "${withval}" in +yes|no) use_meta=${withval};; +*) AC_MSG_ERROR(bad value ${withval} given for meta) ;; +esac]) +dnl +AC_ARG_WITH(filemon, +[ --with-filemon=path/filemon.h indicate path to filemon.h for meta-mode], +[ case "/${withval}" in +/no|*/filemon.h) filemon_h="${withval}";; +*/filemon*) filemon_h="${withval}/filemon.h";; +*) AC_MSG_ERROR(bad value ${withval} given for filemon) ;; +esac], +[ +OS=`uname -s` +for d in "/usr/include/dev/filemon" "$prefix/include/dev/filemon" "$srcdir/filemon" "$srcdir/../filemon" "$srcdir/../../sys/dev/filemon" +do + for x in "/$OS" "" + do + filemon_h="$d$x/filemon.h" + test -s "$filemon_h" && break + done + test -s "$filemon_h" && break +done +test -s "${filemon_h:-/dev/null}" || filemon_h=no +]) +dnl echo "Note: use_meta=$use_meta filemon_h=$filemon_h" >&6 +case "$use_meta" in +yes) + case "$filemon_h" in + *.h) echo "Using: filemon=$filemon_h" >&6;; + esac + ;; +esac +dnl +dnl Check for OS problems +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. +AC_USE_SYSTEM_EXTENSIONS +dnl Checks for programs. +AC_PROG_CC +AC_PROG_GCC_TRADITIONAL +AC_PROG_INSTALL +dnl Executable suffix - normally empty; .exe on os2. +AC_SUBST(ac_exe_suffix)dnl + +dnl +dnl Check if /bin/sh will pass .MAKE.LEVEL +echo $ECHO_N "checking if sh will pass .MAKE. variables... $ECHO_C" >&6 +ok=`env .MAKE.LEVEL=1 /bin/sh -c env | grep LEVEL=` +case "$ok" in +"") echo no >&6; CPPFLAGS="${CPPFLAGS} -DNEED_MAKE_LEVEL_SAFE";; +*) echo yes >&6;; +esac + +dnl +dnl AC_C_CROSS +dnl + +dnl Checks for header files. +AC_HEADER_STDC +AC_HEADER_SYS_WAIT +AC_HEADER_DIRENT +dnl Keep this list sorted +AC_CHECK_HEADERS( \ + ar.h \ + err.h \ + fcntl.h \ + paths.h \ + poll.h \ + ranlib.h \ + string.h \ + sys/mman.h \ + sys/select.h \ + sys/socket.h \ + sys/time.h \ + sys/uio.h \ + unistd.h \ + utime.h \ + ) + +dnl Both *BSD and Linux have sys/cdefs.h, most do not. +dnl If it is missing, we add -I${srcdir}/missing to CFLAGS +dnl also if sys/cdefs.h does not have __RCSID we need to use ours +dnl but we need to include the host's one too *sigh* +AC_CHECK_HEADER(sys/cdefs.h, +echo $ECHO_N "checking whether sys/cdefs.h is compatible... $ECHO_C" >&6 +AC_EGREP_CPP(yes, +[#include <sys/cdefs.h> +#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( \ + err \ + errx \ + getcwd \ + getenv \ + getopt \ + getwd \ + killpg \ + mmap \ + putenv \ + select \ + setenv \ + setpgid \ + setsid \ + sigaction \ + sigvec \ + snprintf \ + strerror \ + strftime \ + strsep \ + strtod \ + strtol \ + unsetenv \ + vsnprintf \ + wait3 \ + wait4 \ + waitpid \ + warn \ + warnx \ + ) + +dnl functions which we may need to provide +AC_REPLACE_FUNCS( \ + realpath \ + dirname \ + stresep \ + strlcpy \ + ) + +AC_CHECK_LIB([util], [emalloc], + [ AC_CHECK_LIB([util], [erealloc], + [ AC_CHECK_LIB([util], [estrdup], + [ AC_CHECK_LIB([util], [estrndup], + [ LIBS="$LIBS -lutil" + CPPFLAGS="$CPPFLAGS -DUSE_EMALLOC" ])])])]) + +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 AC_* don't quite cut it. +dnl +echo "checking for MACHINE & MACHINE_ARCH..." >&6 +cat > conftest.$ac_ext <<EOF +#include "confdefs.h" +#include <sys/param.h> +#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 +srcdir=`cd $srcdir && pwd` +for mksrc in $mksrc $srcdir/mk $srcdir/../mk mk +do + test -s $mksrc/install-mk || continue + mksrc=`cd $mksrc && pwd` + break +done +mksrc=`echo $mksrc | sed "s,$srcdir,\\\${srcdir},"` +echo "Using: MKSRC=$mksrc" 1>&6 +dnl On some systems we want a different default shell by default +if test -x /usr/xpg4/bin/sh; then + defshell_path=${defshell_path:-/usr/xpg4/bin/sh} +fi +if test -n "$defshell_path"; then + echo "Using: SHELL=$defshell_path" >&6 + AC_DEFINE_UNQUOTED(DEFSHELL_CUSTOM, "$defshell_path", Path of default shell) +fi +if test -n "$DEFSHELL_INDEX"; then + AC_DEFINE_UNQUOTED(DEFSHELL_INDEX, $DEFSHELL_INDEX, Shell spec to use by default) +fi +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_SUBST(use_meta) +AC_SUBST(filemon_h) +AC_OUTPUT(Makefile make-bootstrap.sh unit-tests/Makefile) + +cat <<EOF + +You can now run + + sh ./make-bootstrap.sh + +to produce a fully functional bmake. + +EOF diff --git a/20120704/dir.c b/20120704/dir.c new file mode 100644 index 0000000..1c56ea3 --- /dev/null +++ b/20120704/dir.c @@ -0,0 +1,1802 @@ +/* $NetBSD: dir.c,v 1.65 2012/06/12 19:21:50 joerg 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: dir.c,v 1.65 2012/06/12 19:21:50 joerg Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)dir.c 8.2 (Berkeley) 1/2/94"; +#else +__RCSID("$NetBSD: dir.c,v 1.65 2012/06/12 19:21:50 joerg 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 <sys/types.h> +#include <sys/stat.h> + +#include <dirent.h> +#include <errno.h> +#include <stdio.h> + +#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(const void *, const void *); +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(void *, void *); +static int DirPrintDir(void *, void *); +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 = bmake_malloc(sizeof(Path)); + dotLast->refCount = 1; + dotLast->hits = 0; + dotLast->name = bmake_strdup(".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(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, 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(cur); + } + dot->refCount -= 1; + dotLast->refCount -= 1; + Dir_Destroy(dotLast); + Dir_Destroy(dot); + Dir_ClearPath(dirSearchPath); + Lst_Destroy(dirSearchPath, NULL); + Dir_ClearPath(openDirectories); + Lst_Destroy(openDirectories, NULL); + 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)) != NULL) { + 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)) != NULL) { + 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(const void *p, const void *dname) +{ + return (strcmp(((const Path *)p)->name, 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 ? bmake_strdup(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 = bmake_malloc(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)) != NULL) { + 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(void *word, void *dummy) +{ + fprintf(debug_file, "%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)) { + fprintf(debug_file, "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, NULL); + } + } 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, NULL); + fprintf(debug_file, "\n"); + } +} + +/*- + *----------------------------------------------------------------------- + * 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 MAKE_ATTR_UNUSED, const char *cp, + Boolean hasSlash MAKE_ATTR_UNUSED) +{ + char *file; /* the current filename to check */ + + if (DEBUG(DIR)) { + fprintf(debug_file, " %s ...\n", p->name); + } + + if (Hash_FindEntry(&p->files, cp) == NULL) + return NULL; + + file = str_concat(p->name, cp, STR_ADDSLASH); + if (DEBUG(DIR)) { + fprintf(debug_file, " 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 = bmake_strdup(name); + } + + if (DEBUG(DIR)) { + fprintf(debug_file, "checking %s ...\n", file); + } + + if (stat(file, &stb) == 0) { + if (stb.st_mtime == 0) + stb.st_mtime = 1; + /* + * Save the modification time so if it's needed, we don't have + * to fetch it again. + */ + if (DEBUG(DIR)) { + fprintf(debug_file, " Caching %s for %s\n", Targ_FmtTime(stb.st_mtime), + file); + } + entry = Hash_CreateEntry(&mtimes, file, NULL); + Hash_SetTimeValue(entry, 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)) { + fprintf(debug_file, " %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)) { + fprintf(debug_file, " must be here but isn't -- returning\n"); + } + /* Return empty string: terminates search */ + return bmake_strdup(""); + } + + p->hits += 1; + hits += 1; + if (DEBUG(DIR)) { + fprintf(debug_file, " returning %s\n", name); + } + return (bmake_strdup(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 MAKE_ATTR_UNUSED, const char *name, const char *cp) +{ + + if (Hash_FindEntry(&dot->files, cp) != NULL) { + if (DEBUG(DIR)) { + fprintf(debug_file, " in '.'\n"); + } + hits += 1; + dot->hits += 1; + return (bmake_strdup(name)); + } + if (cur && + Hash_FindEntry(&cur->files, cp) != NULL) { + if (DEBUG(DIR)) { + fprintf(debug_file, " 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; /* Terminal name of file */ + 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 */ + const char *trailing_dot = "."; + + /* + * 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)) { + fprintf(debug_file, "Searching for %s ...", name); + } + + if (Lst_Open(path) == FAILURE) { + if (DEBUG(DIR)) { + fprintf(debug_file, "couldn't open path, file not found\n"); + } + misses += 1; + return NULL; + } + + if ((ln = Lst_First(path)) != NULL) { + p = (Path *)Lst_Datum(ln); + if (p == dotLast) { + hasLastDot = TRUE; + if (DEBUG(DIR)) + fprintf(debug_file, "[dot last]..."); + } + } + if (DEBUG(DIR)) { + fprintf(debug_file, "\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)) != NULL) { + 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)) { + fprintf(debug_file, " failed.\n"); + } + misses += 1; + return NULL; + } + + if (*cp == '\0') { + /* we were given a trailing "/" */ + cp = trailing_dot; + } + + if (name[0] != '/') { + Boolean checkedDot = FALSE; + + if (DEBUG(DIR)) { + fprintf(debug_file, " 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)) != NULL) { + 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)) { + fprintf(debug_file, " 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)) { + fprintf(debug_file, " 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)) != NULL) { + 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 + if (cp == traling_dot) { + cp = strrchr(name, '/'); + cp += 1; + } + cp[-1] = '\0'; + (void)Dir_AddDir(path, name); + cp[-1] = '/'; + + bigmisses += 1; + ln = Lst_Last(path); + if (ln == NULL) { + return NULL; + } else { + p = (Path *)Lst_Datum(ln); + } + + if (Hash_FindEntry(&p->files, cp) != NULL) { + return (bmake_strdup(name)); + } else { + return NULL; + } +#else /* !notdef */ + if (DEBUG(DIR)) { + fprintf(debug_file, " Looking for \"%s\" ...\n", name); + } + + bigmisses += 1; + entry = Hash_FindEntry(&mtimes, name); + if (entry != NULL) { + if (DEBUG(DIR)) { + fprintf(debug_file, " got it (in mtime cache)\n"); + } + return(bmake_strdup(name)); + } else if (stat(name, &stb) == 0) { + if (stb.st_mtime == 0) + stb.st_mtime = 1; + entry = Hash_CreateEntry(&mtimes, name, NULL); + if (DEBUG(DIR)) { + fprintf(debug_file, " Caching %s for %s\n", Targ_FmtTime(stb.st_mtime), + name); + } + Hash_SetTimeValue(entry, stb.st_mtime); + return (bmake_strdup(name)); + } else { + if (DEBUG(DIR)) { + fprintf(debug_file, " 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, Boolean recheck) +{ + 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)); + if (fullName == NULL && gn->flags & FROM_DEPEND && + !Lst_IsEmpty(gn->iParents)) { + char *cp; + + cp = strrchr(gn->name, '/'); + if (cp) { + /* + * This is an implied source, and it may have moved, + * see if we can find it via the current .PATH + */ + cp++; + + fullName = Dir_FindFile(cp, Suff_FindPath(gn)); + if (fullName) { + /* + * Put the found file in gn->path + * so that we give that to the compiler. + */ + gn->path = bmake_strdup(fullName); + fprintf(stdout, + "%s: ignoring stale %s for %s, found %s\n", + progname, makeDependfile, gn->name, fullName); + } + } + } + if (DEBUG(DIR)) + fprintf(debug_file, "Found '%s' as '%s'\n", + gn->name, fullName ? fullName : "(not found)" ); + } + } else { + fullName = gn->path; + } + + if (fullName == NULL) { + fullName = bmake_strdup(gn->name); + } + + if (!recheck) + entry = Hash_FindEntry(&mtimes, fullName); + else + entry = NULL; + if (entry != NULL) { + if (DEBUG(DIR)) { + fprintf(debug_file, "Using cached time %s for %s\n", + Targ_FmtTime(Hash_GetTimeValue(entry)), fullName); + } + stb.st_mtime = Hash_GetTimeValue(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; + } + } else { + if (stb.st_mtime == 0) { + /* + * 0 handled specially by the code, if the time is really 0, + * return something else instead + */ + stb.st_mtime = 1; + } + entry = Hash_CreateEntry(&mtimes, fullName, NULL); + Hash_SetTimeValue(entry, stb.st_mtime); + } + + 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 = NULL; /* 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, name, DirFindName); + if (ln != NULL) + return (Path *)Lst_Datum(ln); + else { + dotLast->refCount += 1; + (void)Lst_AtFront(path, dotLast); + } + } + + if (path) + ln = Lst_Find(openDirectories, name, DirFindName); + if (ln != NULL) { + p = (Path *)Lst_Datum(ln); + if (path && Lst_Member(path, p) == NULL) { + p->refCount += 1; + (void)Lst_AtEnd(path, p); + } + } else { + if (DEBUG(DIR)) { + fprintf(debug_file, "Caching %s ...", name); + } + + if ((d = opendir(name)) != NULL) { + p = bmake_malloc(sizeof(Path)); + p->name = bmake_strdup(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, p); + if (path != NULL) + (void)Lst_AtEnd(path, p); + } + if (DEBUG(DIR)) { + fprintf(debug_file, "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. + * + *----------------------------------------------------------------------- + */ +void * +Dir_CopyDir(void *p) +{ + ((Path *)p)->refCount += 1; + + return (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 = bmake_strdup(""); + + if (Lst_Open(path) == SUCCESS) { + while ((ln = Lst_Next(path)) != NULL) { + 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(void *pp) +{ + Path *p = (Path *)pp; + p->refCount -= 1; + + if (p->refCount == 0) { + LstNode ln; + + ln = Lst_Member(openDirectories, 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(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 != NULL; ln = Lst_Succ(ln)) { + p = (Path *)Lst_Datum(ln); + if (Lst_Member(path1, p) == NULL) { + p->refCount += 1; + (void)Lst_AtEnd(path1, p); + } + } +} + +/********** DEBUG INFO **********/ +void +Dir_PrintDirectories(void) +{ + LstNode ln; + Path *p; + + fprintf(debug_file, "#*** Directory Cache:\n"); + fprintf(debug_file, "# 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)); + fprintf(debug_file, "# %-20s referenced\thits\n", "directory"); + if (Lst_Open(openDirectories) == SUCCESS) { + while ((ln = Lst_Next(openDirectories)) != NULL) { + p = (Path *)Lst_Datum(ln); + fprintf(debug_file, "# %-20s %10d\t%4d\n", p->name, p->refCount, p->hits); + } + Lst_Close(openDirectories); + } +} + +static int +DirPrintDir(void *p, void *dummy) +{ + fprintf(debug_file, "%s ", ((Path *)p)->name); + return (dummy ? 0 : 0); +} + +void +Dir_PrintPath(Lst path) +{ + Lst_ForEach(path, DirPrintDir, NULL); +} diff --git a/20120704/dir.h b/20120704/dir.h new file mode 100644 index 0000000..aa00450 --- /dev/null +++ b/20120704/dir.h @@ -0,0 +1,108 @@ +/* $NetBSD: dir.h,v 1.15 2012/04/07 18:29:08 christos 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 *, Boolean); +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(void *); +void * Dir_CopyDir(void *); + +#endif /* _DIR */ diff --git a/20120704/dirname.c b/20120704/dirname.c new file mode 100644 index 0000000..8b6b6c3 --- /dev/null +++ b/20120704/dirname.c @@ -0,0 +1,95 @@ +/* $NetBSD: dirname.c,v 1.11 2009/11/24 13:34:20 tnozaki Exp $ */ + +/*- + * Copyright (c) 1997, 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Klaus Klein and Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#ifndef HAVE_DIRNAME + +#include <sys/cdefs.h> + +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifndef PATH_MAX +# define PATH_MAX 1024 +#endif + +char * +dirname(char *path) +{ + static char result[PATH_MAX]; + const char *lastp; + size_t len; + + /* + * If `path' is a null pointer or points to an empty string, + * return a pointer to the string ".". + */ + if ((path == NULL) || (*path == '\0')) + goto singledot; + + + /* Strip trailing slashes, if any. */ + lastp = path + strlen(path) - 1; + while (lastp != path && *lastp == '/') + lastp--; + + /* Terminate path at the last occurence of '/'. */ + do { + if (*lastp == '/') { + /* Strip trailing slashes, if any. */ + while (lastp != path && *lastp == '/') + lastp--; + + /* ...and copy the result into the result buffer. */ + len = (lastp - path) + 1 /* last char */; + if (len > (PATH_MAX - 1)) + len = PATH_MAX - 1; + + memcpy(result, path, len); + result[len] = '\0'; + + return (result); + } + } while (--lastp >= path); + + /* No /'s found, return a pointer to the string ".". */ +singledot: + result[0] = '.'; + result[1] = '\0'; + + return (result); +} +#endif diff --git a/20120704/find_lib.sh b/20120704/find_lib.sh new file mode 100755 index 0000000..3c2e4af --- /dev/null +++ b/20120704/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/20120704/for.c b/20120704/for.c new file mode 100644 index 0000000..33bcf13 --- /dev/null +++ b/20120704/for.c @@ -0,0 +1,496 @@ +/* $NetBSD: for.c,v 1.49 2012/06/03 04:29:40 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.49 2012/06/03 04:29:40 sjg Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)for.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: for.c,v 1.49 2012/06/03 04:29:40 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 <assert.h> +#include <ctype.h> + +#include "make.h" +#include "hash.h" +#include "dir.h" +#include "buf.h" +#include "strlist.h" + +#define FOR_SUB_ESCAPE_CHAR 1 +#define FOR_SUB_ESCAPE_BRACE 2 +#define FOR_SUB_ESCAPE_PAREN 4 + +/* + * For statements are of the form: + * + * .for <variable> in <varlist> + * ... + * .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 */ + strlist_t vars; /* Iteration variables */ + strlist_t items; /* Substitution items */ + char *parse_buf; + int short_var; + int sub_next; +} For; + +static For *accumFor; /* Loop being accumulated */ + + + +static char * +make_str(const char *ptr, int len) +{ + char *new_ptr; + + new_ptr = bmake_malloc(len + 1); + memcpy(new_ptr, ptr, len); + new_ptr[len] = 0; + return new_ptr; +} + +static void +For_Free(For *arg) +{ + Buf_Destroy(&arg->buf, TRUE); + strlist_clean(&arg->vars); + strlist_clean(&arg->items); + free(arg->parse_buf); + + free(arg); +} + +/*- + *----------------------------------------------------------------------- + * For_Eval -- + * Evaluate the for loop in the passed line. The line + * looks like this: + * .for <variable> in <varlist> + * + * Input: + * line Line to parse + * + * Results: + * 0: Not a .for statement, parse the line + * 1: We found a for loop + * -1: A .for statement with a bad syntax error, discard. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +int +For_Eval(char *line) +{ + For *new_for; + char *ptr = line, *sub; + int len; + int escapes; + unsigned char ch; + char **words, *word_buf; + int n, nwords; + + /* Skip the '.' and any following whitespace */ + 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])) { + if (ptr[0] == 'e' && strncmp(ptr+1, "ndfor", 5) == 0) { + Parse_Error(PARSE_FATAL, "for-less endfor"); + return -1; + } + return 0; + } + ptr += 3; + + /* + * we found a for loop, and now we are going to parse it. + */ + + new_for = bmake_malloc(sizeof *new_for); + memset(new_for, 0, sizeof *new_for); + + /* Grab the variables. Terminate on "in". */ + for (;; ptr += len) { + while (*ptr && isspace((unsigned char) *ptr)) + ptr++; + if (*ptr == '\0') { + Parse_Error(PARSE_FATAL, "missing `in' in for"); + For_Free(new_for); + return -1; + } + for (len = 1; ptr[len] && !isspace((unsigned char)ptr[len]); len++) + continue; + if (len == 2 && ptr[0] == 'i' && ptr[1] == 'n') { + ptr += 2; + break; + } + if (len == 1) + new_for->short_var = 1; + strlist_add_str(&new_for->vars, make_str(ptr, len), len); + } + + if (strlist_num(&new_for->vars) == 0) { + Parse_Error(PARSE_FATAL, "no iteration variables in for"); + For_Free(new_for); + return -1; + } + + while (*ptr && isspace((unsigned char) *ptr)) + ptr++; + + /* + * Make a list with the remaining words + * The values are substituted as ${:U<value>...} so we must \ escape + * characters that break that syntax. + * Variables are fully expanded - so it is safe for escape $. + * We can't do the escapes here - because we don't know whether + * we are substuting into ${...} or $(...). + */ + sub = Var_Subst(NULL, ptr, VAR_GLOBAL, FALSE); + + /* + * Split into words allowing for quoted strings. + */ + words = brk_string(sub, &nwords, FALSE, &word_buf); + + free(sub); + + if (words != NULL) { + for (n = 0; n < nwords; n++) { + ptr = words[n]; + if (!*ptr) + continue; + escapes = 0; + while ((ch = *ptr++)) { + switch(ch) { + case ':': + case '$': + case '\\': + escapes |= FOR_SUB_ESCAPE_CHAR; + break; + case ')': + escapes |= FOR_SUB_ESCAPE_PAREN; + break; + case /*{*/ '}': + escapes |= FOR_SUB_ESCAPE_BRACE; + break; + } + } + /* + * We have to dup words[n] to maintain the semantics of + * strlist. + */ + strlist_add_str(&new_for->items, bmake_strdup(words[n]), escapes); + } + + free(words); + free(word_buf); + + if ((len = strlist_num(&new_for->items)) > 0 && + len % (n = strlist_num(&new_for->vars))) { + Parse_Error(PARSE_FATAL, + "Wrong number of words (%d) in .for substitution list" + " with %d vars", len, n); + /* + * Return 'success' so that the body of the .for loop is + * accumulated. + * Remove all items so that the loop doesn't iterate. + */ + strlist_clean(&new_for->items); + } + } + + Buf_Init(&new_for->buf, 0); + accumFor = new_for; + forLevel = 1; + return 1; +} + +/* + * Add another line to a .for loop. + * Returns 0 when the matching .endfor is reached. + */ + +int +For_Accum(char *line) +{ + char *ptr = line; + + 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(debug_file, "For: end for %d\n", forLevel); + if (--forLevel <= 0) + return 0; + } else if (strncmp(ptr, "for", 3) == 0 && + isspace((unsigned char) ptr[3])) { + forLevel++; + if (DEBUG(FOR)) + (void)fprintf(debug_file, "For: new loop %d\n", forLevel); + } + } + + Buf_AddBytes(&accumFor->buf, strlen(line), line); + Buf_AddByte(&accumFor->buf, '\n'); + return 1; +} + + +/*- + *----------------------------------------------------------------------- + * For_Run -- + * Run the for loop, imitating the actions of an include file + * + * Results: + * None. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ + +static int +for_var_len(const char *var) +{ + char ch, var_start, var_end; + int depth; + int len; + + var_start = *var; + if (var_start == 0) + /* just escape the $ */ + return 0; + + if (var_start == '(') + var_end = ')'; + else if (var_start == '{') + var_end = '}'; + else + /* Single char variable */ + return 1; + + depth = 1; + for (len = 1; (ch = var[len++]) != 0;) { + if (ch == var_start) + depth++; + else if (ch == var_end && --depth == 0) + return len; + } + + /* Variable end not found, escape the $ */ + return 0; +} + +static void +for_substitute(Buffer *cmds, strlist_t *items, unsigned int item_no, char ech) +{ + const char *item = strlist_str(items, item_no); + int len; + char ch; + + /* If there were no escapes, or the only escape is the other variable + * terminator, then just substitute the full string */ + if (!(strlist_info(items, item_no) & + (ech == ')' ? ~FOR_SUB_ESCAPE_BRACE : ~FOR_SUB_ESCAPE_PAREN))) { + Buf_AddBytes(cmds, strlen(item), item); + return; + } + + /* Escape ':', '$', '\\' and 'ech' - removed by :U processing */ + while ((ch = *item++) != 0) { + if (ch == '$') { + len = for_var_len(item); + if (len != 0) { + Buf_AddBytes(cmds, len + 1, item - 1); + item += len; + continue; + } + Buf_AddByte(cmds, '\\'); + } else if (ch == ':' || ch == '\\' || ch == ech) + Buf_AddByte(cmds, '\\'); + Buf_AddByte(cmds, ch); + } +} + +static char * +For_Iterate(void *v_arg, size_t *ret_len) +{ + For *arg = v_arg; + int i, len; + char *var; + char *cp; + char *cmd_cp; + char *body_end; + char ch; + Buffer cmds; + + if (arg->sub_next + strlist_num(&arg->vars) > strlist_num(&arg->items)) { + /* No more iterations */ + For_Free(arg); + return NULL; + } + + free(arg->parse_buf); + arg->parse_buf = NULL; + + /* + * Scan the for loop body and replace references to the loop variables + * with variable references that expand to the required text. + * Using variable expansions ensures that the .for loop can't generate + * syntax, and that the later parsing will still see a variable. + * We assume that the null variable will never be defined. + * + * The detection of substitions of the loop control variable is naive. + * Many of the modifiers use \ to escape $ (not $) so it is possible + * to contrive a makefile where an unwanted substitution happens. + */ + + cmd_cp = Buf_GetAll(&arg->buf, &len); + body_end = cmd_cp + len; + Buf_Init(&cmds, len + 256); + for (cp = cmd_cp; (cp = strchr(cp, '$')) != NULL;) { + char ech; + ch = *++cp; + if ((ch == '(' && (ech = ')')) || (ch == '{' && (ech = '}'))) { + cp++; + /* Check variable name against the .for loop variables */ + STRLIST_FOREACH(var, &arg->vars, i) { + len = strlist_info(&arg->vars, i); + if (memcmp(cp, var, len) != 0) + continue; + if (cp[len] != ':' && cp[len] != ech && cp[len] != '\\') + continue; + /* Found a variable match. Replace with :U<value> */ + Buf_AddBytes(&cmds, cp - cmd_cp, cmd_cp); + Buf_AddBytes(&cmds, 2, ":U"); + cp += len; + cmd_cp = cp; + for_substitute(&cmds, &arg->items, arg->sub_next + i, ech); + break; + } + continue; + } + if (ch == 0) + break; + /* Probably a single character name, ignore $$ and stupid ones. {*/ + if (!arg->short_var || strchr("}):$", ch) != NULL) { + cp++; + continue; + } + STRLIST_FOREACH(var, &arg->vars, i) { + if (var[0] != ch || var[1] != 0) + continue; + /* Found a variable match. Replace with ${:U<value>} */ + Buf_AddBytes(&cmds, cp - cmd_cp, cmd_cp); + Buf_AddBytes(&cmds, 3, "{:U"); + cmd_cp = ++cp; + for_substitute(&cmds, &arg->items, arg->sub_next + i, /*{*/ '}'); + Buf_AddBytes(&cmds, 1, "}"); + break; + } + } + Buf_AddBytes(&cmds, body_end - cmd_cp, cmd_cp); + + cp = Buf_Destroy(&cmds, FALSE); + if (DEBUG(FOR)) + (void)fprintf(debug_file, "For: loop body:\n%s", cp); + + arg->sub_next += strlist_num(&arg->vars); + + arg->parse_buf = cp; + *ret_len = strlen(cp); + return cp; +} + +void +For_Run(int lineno) +{ + For *arg; + + arg = accumFor; + accumFor = NULL; + + if (strlist_num(&arg->items) == 0) { + /* Nothing to expand - possibly due to an earlier syntax error. */ + For_Free(arg); + return; + } + + Parse_SetInput(NULL, lineno, -1, For_Iterate, arg); +} diff --git a/20120704/getopt.c b/20120704/getopt.c new file mode 100644 index 0000000..c40bc13 --- /dev/null +++ b/20120704/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.3 1999/01/08 02:14:18 sjg Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + + +#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/20120704/hash.c b/20120704/hash.c new file mode 100644 index 0000000..a22e2f2 --- /dev/null +++ b/20120704/hash.c @@ -0,0 +1,463 @@ +/* $NetBSD: hash.c,v 1.19 2009/01/24 10:59:09 dsl 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.19 2009/01/24 10:59:09 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)hash.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: hash.c,v 1.19 2009/01/24 10:59:09 dsl 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 = bmake_malloc(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 = bmake_malloc(sizeof(*e) + keylen); + hp = &t->bucketPtr[h & t->mask]; + e->next = *hp; + *hp = e; + Hash_SetValue(e, 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 = bmake_malloc(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/20120704/hash.h b/20120704/hash.h new file mode 100644 index 0000000..31d2ff1 --- /dev/null +++ b/20120704/hash.h @@ -0,0 +1,154 @@ +/* $NetBSD: hash.h,v 1.10 2009/01/24 10:59:09 dsl 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. */ + union { + void *clientPtr; /* Arbitrary pointer */ + time_t clientTime; /* Arbitrary Time */ + } clientInfo; + 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. + */ + +/* + * void * Hash_GetValue(h) + * Hash_Entry *h; + */ + +#define Hash_GetValue(h) ((h)->clientInfo.clientPtr) +#define Hash_GetTimeValue(h) ((h)->clientInfo.clientTime) + +/* + * Hash_SetValue(h, val); + * Hash_Entry *h; + * char *val; + */ + +#define Hash_SetValue(h, val) ((h)->clientInfo.clientPtr = (val)) +#define Hash_SetTimeValue(h, val) ((h)->clientInfo.clientTime = (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/20120704/install-sh b/20120704/install-sh new file mode 100755 index 0000000..a247329 --- /dev/null +++ b/20120704/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 <sjg@quick.com.au> +# + +# RCSid: +# $Id: install-sh,v 1.18 2001/03/16 17:33:02 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/20120704/job.c b/20120704/job.c new file mode 100644 index 0000000..99e05d5 --- /dev/null +++ b/20120704/job.c @@ -0,0 +1,2994 @@ +/* $NetBSD: job.c,v 1.163 2012/07/03 21:03:40 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.163 2012/07/03 21:03:40 sjg Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)job.c 8.2 (Berkeley) 3/19/94"; +#else +__RCSID("$NetBSD: job.c,v 1.163 2012/07/03 21:03:40 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. + * + * 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_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 <sys/types.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <sys/time.h> +#include "wait.h" + +#include <errno.h> +#include <fcntl.h> +#if !defined(USE_SELECT) && defined(HAVE_POLL_H) +#include <poll.h> +#else +#ifndef USE_SELECT /* no poll.h */ +# define USE_SELECT +#endif +#if defined(HAVE_SYS_SELECT_H) +# include <sys/select.h> +#endif +#endif +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <utime.h> +#if defined(HAVE_SYS_SOCKET_H) +# include <sys/socket.h> +#endif + +#include "make.h" +#include "hash.h" +#include "dir.h" +#include "job.h" +#include "pathnames.h" +#include "trace.h" +# define STATIC static + +/* + * 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 */ +#define JOB_TOKENS "+EI+" /* Token to requeue for each abort state */ + +/* + * this tracks the number of tokens currently "out" to build jobs. + */ +int jobTokensRunning = 0; +int not_parallel = 0; /* set if .NOT_PARALLEL */ + +/* + * 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 = NULL; + /* 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 */ + +/* + * Descriptions for various shells. + * + * The build environment may set DEFSHELL_INDEX to one of + * DEFSHELL_INDEX_SH, DEFSHELL_INDEX_KSH, or DEFSHELL_INDEX_CSH, to + * select one of the prefedined shells as the default shell. + * + * Alternatively, the build environment may set DEFSHELL_CUSTOM to the + * name or the full path of a sh-compatible shell, which will be used as + * the default shell. + * + * ".SHELL" lines in Makefiles can choose the default shell from the + # set defined here, or add additional shells. + */ + +#ifdef DEFSHELL_CUSTOM +#define DEFSHELL_INDEX_CUSTOM 0 +#define DEFSHELL_INDEX_SH 1 +#define DEFSHELL_INDEX_KSH 2 +#define DEFSHELL_INDEX_CSH 3 +#else /* !DEFSHELL_CUSTOM */ +#define DEFSHELL_INDEX_SH 0 +#define DEFSHELL_INDEX_KSH 1 +#define DEFSHELL_INDEX_CSH 2 +#endif /* !DEFSHELL_CUSTOM */ + +#ifndef DEFSHELL_INDEX +#define DEFSHELL_INDEX 0 /* DEFSHELL_INDEX_CUSTOM or DEFSHELL_INDEX_SH */ +#endif /* !DEFSHELL_INDEX */ + +static Shell shells[] = { +#ifdef DEFSHELL_CUSTOM + /* + * An sh-compatible shell with a non-standard name. + * + * Keep this in sync with the "sh" description below, but avoid + * non-portable features that might not be supplied by all + * sh-compatible shells. + */ +{ + DEFSHELL_CUSTOM, + FALSE, "", "", "", 0, + FALSE, "echo \"%s\"\n", "%s\n", "{ %s \n} || exit $?\n", "'\n'", '#', + "", + "", +}, +#endif /* DEFSHELL_CUSTOM */ + /* + * 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", "'\n'", '#', +#if defined(MAKE_NATIVE) && defined(__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", "'\n'", '#', + "v", + "", +}, + /* + * 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", "", "'\\\n'", '#', + "v", "e", +}, + /* + * UNKNOWN. + */ +{ + NULL, + FALSE, NULL, NULL, NULL, 0, + FALSE, NULL, NULL, NULL, NULL, 0, + NULL, NULL, +} +}; +static Shell *commandShell = &shells[DEFSHELL_INDEX]; /* 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 Job *job_table; /* The structures that describe them */ +STATIC Job *job_table_end; /* job_table + maxJobs */ +static int wantToken; /* we want a token */ +static int lurking_children = 0; +static int make_suspended = 0; /* non-zero if we've seen a SIGTSTP (etc) */ + +/* + * Set of descriptors of pipes connected to + * the output channels of children + */ +static struct pollfd *fds = NULL; +static Job **jobfds = NULL; +static int nfds = 0; +static void watchfd(Job *); +static void clearfd(Job *); +static int readyfd(Job *); + +STATIC GNode *lastNode; /* The node for which output was most recently + * produced. */ +static char *targPrefix = NULL; /* What we print at the start of TARG_FMT */ +static Job tokenWaitJob; /* token wait pseudo-job */ + +static Job childExitJob; /* child exit pseudo-job */ +#define CHILD_EXIT "." +#define DO_JOB_RESUME "R" + +#define TARG_FMT "%s %s ---\n" /* Default format */ +#define MESSAGE(fp, gn) \ + if (maxJobs != 1) \ + (void)fprintf(fp, TARG_FMT, targPrefix, gn->name) + +static sigset_t caught_signals; /* Set of signals we handle */ +#if defined(SYSV) +#define KILLPG(pid, sig) kill(-(pid), (sig)) +#else +#define KILLPG(pid, sig) killpg((pid), (sig)) +#endif + +static void JobChildSig(int); +static void JobContinueSig(int); +static Job *JobFindPid(int, int, Boolean); +static int JobPrintCommand(void *, void *); +static int JobSaveCommand(void *, void *); +static void JobClose(Job *); +static void JobExec(Job *, char **); +static void JobMakeArgv(Job *, char **); +static int JobStart(GNode *, int); +static char *JobOutput(Job *, char *, char *, int); +static void JobDoOutput(Job *, Boolean); +static Shell *JobMatchShell(const char *); +static void JobInterrupt(int, int) MAKE_ATTR_DEAD; +static void JobRestartJobs(void); +static void JobTokenAdd(void); +static void JobSigLock(sigset_t *); +static void JobSigUnlock(sigset_t *); +static void JobSigReset(void); + +const char *malloc_options="A"; + +static void +job_table_dump(const char *where) +{ + Job *job; + + fprintf(debug_file, "job table @ %s\n", where); + for (job = job_table; job < job_table_end; job++) { + fprintf(debug_file, "job %d, status %d, flags %d, pid %d\n", + (int)(job - job_table), job->job_state, job->flags, job->pid); + } +} + +/* + * 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); +} + +static void +JobCreatePipe(Job *job, int minfd) +{ + int i, fd; + + if (pipe(job->jobPipe) == -1) + Punt("Cannot create pipe: %s", strerror(errno)); + + /* Set close-on-exec flag for both */ + (void)fcntl(job->jobPipe[0], F_SETFD, 1); + (void)fcntl(job->jobPipe[1], F_SETFD, 1); + + /* + * 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. + */ + fcntl(job->jobPipe[0], F_SETFL, + fcntl(job->jobPipe[0], F_GETFL, 0) | O_NONBLOCK); + + for (i = 0; i < 2; i++) { + /* Avoid using low numbered fds */ + fd = fcntl(job->jobPipe[i], F_DUPFD, minfd); + if (fd != -1) { + close(job->jobPipe[i]); + job->jobPipe[i] = fd; + } + } +} + +/*- + *----------------------------------------------------------------------- + * JobCondPassSig -- + * Pass a signal to a job + * + * Input: + * signop Signal to send it + * + * Side Effects: + * None, except the job may bite it. + * + *----------------------------------------------------------------------- + */ +static void +JobCondPassSig(int signo) +{ + Job *job; + + if (DEBUG(JOB)) { + (void)fprintf(debug_file, "JobCondPassSig(%d) called.\n", signo); + } + + for (job = job_table; job < job_table_end; job++) { + if (job->job_state != JOB_ST_RUNNING) + continue; + if (DEBUG(JOB)) { + (void)fprintf(debug_file, + "JobCondPassSig passing signal %d to child %d.\n", + signo, job->pid); + } + KILLPG(job->pid, signo); + } +} + +/*- + *----------------------------------------------------------------------- + * 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 MAKE_ATTR_UNUSED) +{ + write(childExitJob.outPipe, CHILD_EXIT, 1); +} + + +/*- + *----------------------------------------------------------------------- + * 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 MAKE_ATTR_UNUSED) +{ + /* + * Defer sending to SIGCONT to our stopped children until we return + * from the signal handler. + */ + write(childExitJob.outPipe, DO_JOB_RESUME, 1); +} + +/*- + *----------------------------------------------------------------------- + * JobPassSig -- + * Pass a signal on to all jobs, then resend to ourselves. + * + * Input: + * signo The signal number we've received + * + * Results: + * None. + * + * Side Effects: + * We die by the same signal. + * + *----------------------------------------------------------------------- + */ +MAKE_ATTR_DEAD static void +JobPassSig_int(int signo) +{ + /* Run .INTERRUPT target then exit */ + JobInterrupt(TRUE, signo); +} + +MAKE_ATTR_DEAD static void +JobPassSig_term(int signo) +{ + /* Dont run .INTERRUPT target then exit */ + JobInterrupt(FALSE, signo); +} + +static void +JobPassSig_suspend(int signo) +{ + sigset_t nmask, omask; + struct sigaction act; + + /* Suppress job started/continued messages */ + make_suspended = 1; + + /* Pass the signal onto every job */ + JobCondPassSig(signo); + + /* + * 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(debug_file, + "JobPassSig passing signal %d to self.\n", signo); + } + + (void)kill(getpid(), signo); + + /* + * We've been continued. + * + * A whole host of signals continue to happen! + * SIGCHLD for any processes that actually suspended themselves. + * SIGCHLD for any processes that exited while we were alseep. + * The SIGCONT that actually caused us to wakeup. + * + * Since we defer passing the SIGCONT on to our children until + * the main processing loop, we can be sure that all the SIGCHLD + * events will have happened by then - and that the waitpid() will + * collect the child 'suspended' events. + * For correct sequencing we just need to ensure we process the + * waitpid() before passign on the SIGCONT. + * + * In any case nothing else is needed here. + */ + + /* Restore handler and signal mask */ + act.sa_handler = JobPassSig_suspend; + (void)sigaction(signo, &act, NULL); + (void)sigprocmask(SIG_SETMASK, &omask, NULL); +} + +/*- + *----------------------------------------------------------------------- + * JobFindPid -- + * Compare the pid of the job with the given pid and return 0 if they + * are equal. This function is called from Job_CatchChildren + * to find the job descriptor of the finished job. + * + * Input: + * job job to examine + * pid process id desired + * + * Results: + * Job with matching pid + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +static Job * +JobFindPid(int pid, int status, Boolean isJobs) +{ + Job *job; + + for (job = job_table; job < job_table_end; job++) { + if ((job->job_state == status) && job->pid == pid) + return job; + } + if (DEBUG(JOB) && isJobs) + job_table_dump("no pid"); + return NULL; +} + +/*- + *----------------------------------------------------------------------- + * 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(void *cmdp, void *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, + cmd)); + return 1; + } + return 0; + } + +#define DBPRINTF(fmt, arg) if (DEBUG(JOB)) { \ + (void)fprintf(debug_file, fmt, arg); \ + } \ + (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 = DEBUG(LOUD) ? FALSE : TRUE; + break; + case '-': + job->flags |= JOB_IGNERR; + 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, 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 = bmake_malloc((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 (!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(void *cmd, void *gn) +{ + cmd = 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) +{ + clearfd(job); + (void)close(job->outPipe); + job->outPipe = -1; + + JobDoOutput(job, TRUE); + (void)close(job->inPipe); + job->inPipe = -1; +} + +/*- + *----------------------------------------------------------------------- + * 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, return_job_token; + + if (DEBUG(JOB)) { + fprintf(debug_file, "Jobfinish: %d [%s], status %d\n", + job->pid, job->node->name, status); + } + + 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... + */ + JobClose(job); + if (job->cmdFILE != NULL && job->cmdFILE != stdout) { + (void)fclose(job->cmdFILE); + job->cmdFILE = NULL; + } + done = TRUE; + } 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); + } else { + /* + * No need to close things down or anything. + */ + done = FALSE; + } + + if (done) { + if (WIFEXITED(status)) { + if (DEBUG(JOB)) { + (void)fprintf(debug_file, "Process %d [%s] exited.\n", + job->pid, job->node->name); + } + if (WEXITSTATUS(status) != 0) { + if (job->node != lastNode) { + MESSAGE(stdout, job->node); + lastNode = job->node; + } +#ifdef USE_META + if (useMeta) { + meta_job_error(job, job->node, job->flags, WEXITSTATUS(status)); + } +#endif + (void)printf("*** [%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 { + PrintOnError(job->node, NULL); + } + } else if (DEBUG(JOB)) { + if (job->node != lastNode) { + MESSAGE(stdout, job->node); + lastNode = job->node; + } + (void)printf("*** [%s] Completed successfully\n", + job->node->name); + } + } else { + if (job->node != lastNode) { + MESSAGE(stdout, job->node); + lastNode = job->node; + } + (void)printf("*** [%s] Signal %d\n", + job->node->name, WTERMSIG(status)); + } + (void)fflush(stdout); + } + +#ifdef USE_META + if (useMeta) { + meta_job_finish(job); + } +#endif + + return_job_token = FALSE; + + Trace_Log(JOBEND, job); + if (!(job->flags & JOB_SPECIAL)) { + if ((WAIT_STATUS(status) != 0) || + (aborting == ABORT_ERROR) || + (aborting == ABORT_INTERRUPT)) + return_job_token = TRUE; + } + + if ((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 != NULL) { + Lst_ForEachFrom(job->node->commands, job->tailCmds, + JobSaveCommand, + job->node); + } + job->node->made = MADE; + if (!(job->flags & JOB_SPECIAL)) + return_job_token = TRUE; + Make_Update(job->node); + job->job_state = JOB_ST_FREE; + } else if (WAIT_STATUS(status)) { + errors += 1; + job->job_state = JOB_ST_FREE; + } + + /* + * 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 (return_job_token) + Job_TokenReturn(); + + if (aborting == ABORT_ERROR && jobTokensRunning == 0) { + /* + * 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_SPECIAL|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 != NULL) && !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) == 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->flags & FROM_DEPEND) { + fprintf(stdout, "%s: ignoring stale %s for %s\n", + progname, makeDependfile, gn->name); + return TRUE; + } + + 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; +} + +/*- + *----------------------------------------------------------------------- + * JobExec -- + * Execute the shell for the given job. Called from JobStart + * + * 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(debug_file, "Running %s %sly\n", job->node->name, "local"); + (void)fprintf(debug_file, "\tCommand: "); + for (i = 0; argv[i] != NULL; i++) { + (void)fprintf(debug_file, "%s ", argv[i]); + } + (void)fprintf(debug_file, "\n"); + } + + /* + * 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_SILENT)) { + MESSAGE(stdout, job->node); + lastNode = job->node; + } + + /* No interruptions until this job is on the `jobs' list */ + JobSigLock(&mask); + + /* Pre-emptively mark job running, pid still zero though */ + job->job_state = JOB_ST_RUNNING; + + cpid = vFork(); + if (cpid == -1) + Punt("Cannot vfork: %s", strerror(errno)); + + if (cpid == 0) { + /* Child */ + sigset_t tmask; + +#ifdef USE_META + if (useMeta) { + meta_job_child(job); + } +#endif + /* + * Reset all signal handlers; this is necessary because we also + * need to unblock signals before we exec(2). + */ + JobSigReset(); + + /* Now unblock signals */ + sigemptyset(&tmask); + JobSigUnlock(&tmask); + + /* + * 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(tokenWaitJob.inPipe, F_SETFD, 0); + fcntl(tokenWaitJob.outPipe, F_SETFD, 0); + } + + /* + * 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); + } + /* + * 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); + } + + /* + * 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(HAVE_SETPGID) + (void)setpgid(0, getpid()); +#else +#if defined(HAVE_SETSID) + /* XXX: dsl - I'm sure this should be setpgrp()... */ + (void)setsid(); +#else + (void)setpgrp(0, getpid()); +#endif +#endif + + Var_ExportVars(); + + (void)execv(shellPath, argv); + execError("exec", shellPath); + _exit(1); + } + + /* Parent, continuing after the child exec */ + job->pid = cpid; + + Trace_Log(JOBSTART, job); + + /* + * Set the current position in the buffer to the beginning + * and mark another stream to watch in the outputs mask + */ + job->curPos = 0; + + watchfd(job); + + if (job->cmdFILE != NULL && job->cmdFILE != stdout) { + (void)fclose(job->cmdFILE); + job->cmdFILE = NULL; + } + + /* + * Now the job is actually running, add it to the table. + */ + if (DEBUG(JOB)) { + fprintf(debug_file, "JobExec(%s): pid %d added to jobs table\n", + job->node->name, job->pid); + job_table_dump("job started"); + } + 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; +} + +/*- + *----------------------------------------------------------------------- + * 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. + * + * NB: I'm fairly sure that this code is never called with JOB_SPECIAL set + * JOB_IGNDOTS is never set (dsl) + * Also the return value is ignored by everyone. + *----------------------------------------------------------------------- + */ +static int +JobStart(GNode *gn, int flags) +{ + Job *job; /* new job descriptor */ + char *argv[10]; /* Argument vector to shell */ + Boolean cmdsOK; /* true if the nodes commands were all right */ + Boolean noExec; /* Set true if we decide not to run the job */ + int tfd; /* File descriptor to the temp file */ + + for (job = job_table; job < job_table_end; job++) { + if (job->job_state == JOB_ST_FREE) + break; + } + if (job >= job_table_end) + Punt("JobStart no job slots vacant"); + + memset(job, 0, sizeof *job); + job->job_state = JOB_ST_SETUP; + if (gn->type & OP_SPECIAL) + flags |= JOB_SPECIAL; + + job->node = gn; + job->tailCmds = NULL; + + /* + * 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 + */ + cmdsOK = Job_CheckCommands(gn, Error); + + job->inPollfd = NULL; + /* + * 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 removed before the child shell is executed, unless + * DEBUG(SCRIPT) is set. + */ + char *tfile; + sigset_t mask; + /* + * We're serious here, but if the commands were bogus, we're + * also dead... + */ + if (!cmdsOK) { + PrintOnError(gn, NULL); /* provide some clue */ + DieHorribly(); + } + + JobSigLock(&mask); + tfd = mkTempFile(TMPPAT, &tfile); + 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; + +#ifdef USE_META + if (useMeta) { + meta_job_start(job, gn); + if (Targ_Silent(gn)) { /* might have changed */ + job->flags |= JOB_SILENT; + } + } +#endif + /* + * We can do all the commands at once. hooray for sanity + */ + numCommands = 0; + Lst_ForEach(gn->commands, JobPrintCommand, 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; + } + + free(tfile); + } 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, 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; + } + /* Just in case it isn't already... */ + (void)fflush(job->cmdFILE); + + /* + * If we're not supposed to execute a shell, don't. + */ + if (noExec) { + if (!(job->flags & JOB_SPECIAL)) + Job_TokenReturn(); + /* + * 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; + } + } + + /* + * 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 && aborting == 0) { + if (job->tailCmds != NULL) { + Lst_ForEachFrom(job->node->commands, job->tailCmds, + JobSaveCommand, + job->node); + } + job->node->made = MADE; + Make_Update(job->node); + } + job->job_state = JOB_ST_FREE; + return cmdsOK ? JOB_FINISHED : JOB_ERROR; + } + + /* + * Set up the control arguments to the shell. This is based on the flags + * set earlier for this job. + */ + JobMakeArgv(job, argv); + + /* Create the pipe by which we'll get the shell's output. */ + JobCreatePipe(job, 3); + + 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 (!beSilent && 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 */ + + /* + * 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 (errno == EAGAIN) + return; + 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 (!beSilent && job->node != lastNode) { + MESSAGE(stdout, job->node); + lastNode = job->node; + } +#ifdef USE_META + if (useMeta) { + meta_job_output(job, cp, gotNL ? "\n" : ""); + } +#endif + (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; + } +} + +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, NULL); + JobStart(targ, JOB_SPECIAL); + while (jobTokensRunning) { + Job_CatchOutput(); + } +#else + Compat_Make(targ, targ); + if (targ->made == ERROR) { + PrintOnError(targ, "\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. + * + *----------------------------------------------------------------------- + */ + +void +Job_CatchChildren(void) +{ + int pid; /* pid of dead child */ + WAIT_T status; /* Exit/termination status */ + + /* + * Don't even bother if we know there's no one around. + */ + if (jobTokensRunning == 0) + return; + + while ((pid = waitpid((pid_t) -1, &status, WNOHANG | WUNTRACED)) > 0) { + if (DEBUG(JOB)) { + (void)fprintf(debug_file, "Process %d exited/stopped status %x.\n", pid, + WAIT_STATUS(status)); + } + JobReapChild(pid, status, TRUE); + } +} + +/* + * It is possible that wait[pid]() was called from elsewhere, + * this lets us reap jobs regardless. + */ +void +JobReapChild(pid_t pid, WAIT_T status, Boolean isJobs) +{ + Job *job; /* job descriptor for dead child */ + + /* + * Don't even bother if we know there's no one around. + */ + if (jobTokensRunning == 0) + return; + + job = JobFindPid(pid, JOB_ST_RUNNING, isJobs); + if (job == NULL) { + if (isJobs) { + if (!lurking_children) + Error("Child (%d) status %x not in table?", pid, status); + } + return; /* not ours */ + } + if (WIFSTOPPED(status)) { + if (DEBUG(JOB)) { + (void)fprintf(debug_file, "Process %d (%s) stopped.\n", + job->pid, job->node->name); + } + if (!make_suspended) { + switch (WSTOPSIG(status)) { + case SIGTSTP: + (void)printf("*** [%s] Suspended\n", job->node->name); + break; + case SIGSTOP: + (void)printf("*** [%s] Stopped\n", job->node->name); + break; + default: + (void)printf("*** [%s] Stopped -- signal %d\n", + job->node->name, WSTOPSIG(status)); + } + job->job_suspended = 1; + } + (void)fflush(stdout); + return; + } + + job->job_state = JOB_ST_FINISHED; + job->exit_status = WAIT_STATUS(status); + + 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; + Job *job; + int i; + + (void)fflush(stdout); + + /* The first fd in the list is the job token pipe */ + nready = poll(fds + 1 - wantToken, nfds - 1 + wantToken, POLL_MSEC); + + if (nready < 0 || readyfd(&childExitJob)) { + char token = 0; + nready -= 1; + (void)read(childExitJob.inPipe, &token, 1); + if (token == DO_JOB_RESUME[0]) + /* Complete relay requested from our SIGCONT handler */ + JobRestartJobs(); + Job_CatchChildren(); + } + + if (nready <= 0) + return; + + if (wantToken && readyfd(&tokenWaitJob)) + nready--; + + for (i = 2; i < nfds; i++) { + if (!fds[i].revents) + continue; + job = jobfds[i]; + if (job->job_state != JOB_ST_RUNNING) + continue; + JobDoOutput(job, FALSE); + } +} + +/*- + *----------------------------------------------------------------------- + * 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); +} + +void +Shell_Init(void) +{ + if (shellPath == NULL) { + /* + * We are using the default shell, which may be an absolute + * path if DEFSHELL_CUSTOM is defined. + */ + shellName = commandShell->name; +#ifdef DEFSHELL_CUSTOM + if (*shellName == '/') { + shellPath = shellName; + shellName = strrchr(shellPath, '/'); + shellName++; + } else +#endif + shellPath = str_concat(_PATH_DEFSHELLDIR, shellName, STR_ADDSLASH); + } + if (commandShell->exit == NULL) { + commandShell->exit = ""; + } + if (commandShell->echo == NULL) { + commandShell->echo = ""; + } +} + +/*- + * Returns the string literal that is used in the current command shell + * to produce a newline character. + */ +const char * +Shell_GetNewline(void) +{ + + return commandShell->newline; +} + +void +Job_SetPrefix(void) +{ + + if (targPrefix) { + free(targPrefix); + } else if (!Var_Exists(MAKE_JOB_PREFIX, VAR_GLOBAL)) { + Var_Set(MAKE_JOB_PREFIX, "---", VAR_GLOBAL, 0); + } + + targPrefix = Var_Subst(NULL, "${" MAKE_JOB_PREFIX "}", VAR_GLOBAL, 0); +} + +/*- + *----------------------------------------------------------------------- + * Job_Init -- + * Initialize the process module + * + * Input: + * + * Results: + * none + * + * Side Effects: + * lists and counters are initialized + *----------------------------------------------------------------------- + */ +void +Job_Init(void) +{ + GNode *begin; /* node for commands to do at the very start */ + + /* Allocate space for all the job info */ + job_table = bmake_malloc(maxJobs * sizeof *job_table); + memset(job_table, 0, maxJobs * sizeof *job_table); + job_table_end = job_table + maxJobs; + wantToken = 0; + + aborting = 0; + errors = 0; + + lastNode = NULL; + + /* + * There is a non-zero chance that we already have children. + * eg after 'make -f- <<EOF' + * Since their termination causes a 'Child (pid) not in table' message, + * Collect the status of any that are already dead, and suppress the + * error message if there are any undead ones. + */ + for (;;) { + int rval, status; + rval = waitpid((pid_t) -1, &status, WNOHANG); + if (rval > 0) + continue; + if (rval == 0) + lurking_children = 1; + break; + } + + Shell_Init(); + + JobCreatePipe(&childExitJob, 3); + + /* We can only need to wait for tokens, children and output from each job */ + fds = bmake_malloc(sizeof (*fds) * (2 + maxJobs)); + jobfds = bmake_malloc(sizeof (*jobfds) * (2 + maxJobs)); + + /* These are permanent entries and take slots 0 and 1 */ + watchfd(&tokenWaitJob); + watchfd(&childExitJob); + + sigemptyset(&caught_signals); + /* + * Install a SIGCHLD handler. + */ + (void)bmake_signal(SIGCHLD, JobChildSig); + sigaddset(&caught_signals, SIGCHLD); + +#define ADDSIG(s,h) \ + if (bmake_signal(s, SIG_IGN) != SIG_IGN) { \ + sigaddset(&caught_signals, s); \ + (void)bmake_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_int) + ADDSIG(SIGHUP, JobPassSig_term) + ADDSIG(SIGTERM, JobPassSig_term) + ADDSIG(SIGQUIT, JobPassSig_term) + + /* + * 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) + */ + ADDSIG(SIGTSTP, JobPassSig_suspend) + ADDSIG(SIGTTOU, JobPassSig_suspend) + ADDSIG(SIGTTIN, JobPassSig_suspend) + ADDSIG(SIGWINCH, JobCondPassSig) + ADDSIG(SIGCONT, JobContinueSig) +#undef ADDSIG + + begin = Targ_FindNode(".BEGIN", TARG_NOCREATE); + + if (begin != NULL) { + JobRun(begin); + if (begin->made == ERROR) { + PrintOnError(begin, "\n\nStop."); + exit(1); + } + } + postCommands = Targ_FindNode(".END", TARG_CREATE); +} + +static void JobSigReset(void) +{ +#define DELSIG(s) \ + if (sigismember(&caught_signals, s)) { \ + (void)bmake_signal(s, SIG_DFL); \ + } + + DELSIG(SIGINT) + DELSIG(SIGHUP) + DELSIG(SIGQUIT) + DELSIG(SIGTERM) + DELSIG(SIGTSTP) + DELSIG(SIGTTOU) + DELSIG(SIGTTIN) + DELSIG(SIGWINCH) + DELSIG(SIGCONT) +#undef DELSIG + (void)bmake_signal(SIGCHLD, SIG_DFL); +} + +/*- + *----------------------------------------------------------------------- + * 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 + * newline String literal to represent a newline char + * 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); + if (words == NULL) { + Error("Unterminated quoted string [%s]", line); + return FAILURE; + } + 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, "newline=", 8) == 0) { + newShell.newline = &argv[0][8]; + } 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 + * Shell_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; + if (shellPath) { + /* Shell_Init has already been called! Do it again. */ + free(UNCONST(shellPath)); + shellPath = NULL; + Shell_Init(); + } + } + } 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 = bmake_malloc(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) +{ + Job *job; /* job descriptor in that element */ + GNode *interrupt; /* the node describing the .INTERRUPT target */ + sigset_t mask; + GNode *gn; + + aborting = ABORT_INTERRUPT; + + JobSigLock(&mask); + + for (job = job_table; job < job_table_end; job++) { + if (job->job_state != JOB_ST_RUNNING) + continue; + + 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); + } + } + if (job->pid) { + if (DEBUG(JOB)) { + (void)fprintf(debug_file, + "JobInterrupt passing signal %d to child %d.\n", + signo, job->pid); + } + KILLPG(job->pid, signo); + } + } + + JobSigUnlock(&mask); + + if (runINTERRUPT && !touchFlag) { + interrupt = Targ_FindNode(".INTERRUPT", TARG_NOCREATE); + if (interrupt != NULL) { + 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 != NULL && + (!Lst_IsEmpty(postCommands->commands) || + !Lst_IsEmpty(postCommands->children))) { + if (errors) { + Error("Errors reported so .END ignored"); + } else { + JobRun(postCommands); + } + } + 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 (jobTokensRunning != 0) { + Job_CatchOutput(); + } + 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) +{ + Job *job; /* the job descriptor in that element */ + WAIT_T foo; + + aborting = ABORT_ERROR; + + if (jobTokensRunning) { + for (job = job_table; job < job_table_end; job++) { + if (job->job_state != JOB_ST_RUNNING) + continue; + /* + * kill the child process with increasingly drastic signals to make + * darn sure it's dead. + */ + KILLPG(job->pid, SIGINT); + KILLPG(job->pid, SIGKILL); + } + } + + /* + * Catch as many children as want to report in at first, then give up + */ + while (waitpid((pid_t) -1, &foo, WNOHANG) > 0) + continue; +} + + +/*- + *----------------------------------------------------------------------- + * JobRestartJobs -- + * Tries to restart stopped jobs if there are slots available. + * Called in process context in response to a SIGCONT. + * + * Results: + * None. + * + * Side Effects: + * Resumes jobs. + * + *----------------------------------------------------------------------- + */ +static void +JobRestartJobs(void) +{ + Job *job; + + for (job = job_table; job < job_table_end; job++) { + if (job->job_state == JOB_ST_RUNNING && + (make_suspended || job->job_suspended)) { + if (DEBUG(JOB)) { + (void)fprintf(debug_file, "Restarting stopped job pid %d.\n", + job->pid); + } + if (job->job_suspended) { + (void)printf("*** [%s] Continued\n", job->node->name); + (void)fflush(stdout); + } + job->job_suspended = 0; + if (KILLPG(job->pid, SIGCONT) != 0 && DEBUG(JOB)) { + fprintf(debug_file, "Failed to send SIGCONT to %d\n", job->pid); + } + } + if (job->job_state == JOB_ST_FINISHED) + /* Job exit deferred after calling waitpid() in a signal handler */ + JobFinish(job, job->exit_status); + } + make_suspended = 0; +} + +static void +watchfd(Job *job) +{ + if (job->inPollfd != NULL) + Punt("Watching watched job"); + + 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; +} + +/*- + *----------------------------------------------------------------------- + * 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) +{ + char tok = JOB_TOKENS[aborting], tok1; + + /* If we are depositing an error token flush everything else */ + while (tok != '+' && read(tokenWaitJob.inPipe, &tok1, 1) == 1) + continue; + + if (DEBUG(JOB)) + fprintf(debug_file, "(%d) aborting %d, deposit token %c\n", + getpid(), aborting, JOB_TOKENS[aborting]); + write(tokenWaitJob.outPipe, &tok, 1); +} + +/*- + *----------------------------------------------------------------------- + * Job_ServerStartTokenAdd -- + * Prep the job token pipe in the root make process. + * + *----------------------------------------------------------------------- + */ + +void +Job_ServerStart(int max_tokens, int jp_0, int jp_1) +{ + int i; + char jobarg[64]; + + if (jp_0 >= 0 && jp_1 >= 0) { + /* Pipe passed in from parent */ + tokenWaitJob.inPipe = jp_0; + tokenWaitJob.outPipe = jp_1; + return; + } + + JobCreatePipe(&tokenWaitJob, 15); + + snprintf(jobarg, sizeof(jobarg), "%d,%d", + tokenWaitJob.inPipe, tokenWaitJob.outPipe); + + Var_Append(MAKEFLAGS, "-J", VAR_GLOBAL); + Var_Append(MAKEFLAGS, jobarg, VAR_GLOBAL); + + /* + * Preload the job pipe with one token per job, save the one + * "extra" token for the primary job. + * + * XXX should clip maxJobs against PIPE_BUF -- if max_tokens is + * larger than the write buffer size of the pipe, we will + * deadlock here. + */ + for (i = 1; i < max_tokens; i++) + JobTokenAdd(); +} + +/*- + *----------------------------------------------------------------------- + * Job_TokenReturn -- + * Return a withdrawn token to the pool. + * + *----------------------------------------------------------------------- + */ + +void +Job_TokenReturn(void) +{ + jobTokensRunning--; + if (jobTokensRunning < 0) + Punt("token botch"); + if (jobTokensRunning || JOB_TOKENS[aborting] != '+') + JobTokenAdd(); +} + +/*- + *----------------------------------------------------------------------- + * 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, tok1; + int count; + + wantToken = 0; + if (DEBUG(JOB)) + fprintf(debug_file, "Job_TokenWithdraw(%d): aborting %d, running %d\n", + getpid(), aborting, jobTokensRunning); + + if (aborting || (jobTokensRunning >= maxJobs)) + return FALSE; + + count = read(tokenWaitJob.inPipe, &tok, 1); + if (count == 0) + Fatal("eof on job pipe!"); + if (count < 0 && jobTokensRunning != 0) { + if (errno != EAGAIN) { + Fatal("job pipe read: %s", strerror(errno)); + } + if (DEBUG(JOB)) + fprintf(debug_file, "(%d) blocked for token\n", getpid()); + wantToken = 1; + return FALSE; + } + + if (count == 1 && tok != '+') { + /* make being abvorted - remove any other job tokens */ + if (DEBUG(JOB)) + fprintf(debug_file, "(%d) aborted by token %c\n", getpid(), tok); + while (read(tokenWaitJob.inPipe, &tok1, 1) == 1) + continue; + /* And put the stopper back */ + write(tokenWaitJob.outPipe, &tok, 1); + Fatal("A failure has been detected in another branch of the parallel make"); + } + + if (count == 1 && jobTokensRunning == 0) + /* We didn't want the token really */ + write(tokenWaitJob.outPipe, &tok, 1); + + jobTokensRunning++; + if (DEBUG(JOB)) + fprintf(debug_file, "(%d) withdrew token\n", getpid()); + return TRUE; +} + +#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/20120704/job.h b/20120704/job.h new file mode 100644 index 0000000..560b70b --- /dev/null +++ b/20120704/job.h @@ -0,0 +1,272 @@ +/* $NetBSD: job.h,v 1.40 2010/09/13 15:36:57 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. + */ +#ifndef _JOB_H_ +#define _JOB_H_ + +#define TMPPAT "makeXXXXXX" /* relative to tmpdir */ + +#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 NULL 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) 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. + * 6) A word of flags which determine how the module handles errors, + * echoing, etc. for the job + * + * 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. + */ +struct pollfd; + + +#ifdef USE_META +# include "meta.h" +#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 exit_status; /* from wait4() in signal handler */ + char job_state; /* status of the job entry */ +#define JOB_ST_FREE 0 /* Job is available */ +#define JOB_ST_SETUP 1 /* Job is allocated but otherwise invalid */ +#define JOB_ST_RUNNING 3 /* Job is running, pid valid */ +#define JOB_ST_FINISHED 4 /* Job is done (ie after SIGCHILD) */ + char job_suspended; + 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_TRACED 0x400 /* we've sent 'set -x' */ + + int jobPipe[2]; /* Pipe for readind output from job */ + struct pollfd *inPollfd; /* pollfd associated with inPipe */ + char outBuf[JOB_BUFSIZE + 1]; + /* Buffer for storing the output of the + * job, line by line */ + int curPos; /* Current position in op_outBuf */ + +#ifdef USE_META + struct BuildMon bm; +#endif +} Job; + +#define inPipe jobPipe[0] +#define outPipe jobPipe[1] + + +/*- + * 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 */ + const char *newline; /* string literal that results in a newline + * character when it appears outside of any + * 'quote' or "quote" characters */ + 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 jobTokensRunning; /* tokens currently "out" */ +extern int maxJobs; /* Max jobs we can run */ + +void Shell_Init(void); +const char *Shell_GetNewline(void); +void Job_Touch(GNode *, Boolean); +Boolean Job_CheckCommands(GNode *, void (*abortProc )(const char *, ...)); +#define CATCH_BLOCK 1 +void Job_CatchChildren(void); +void Job_CatchOutput(void); +void Job_Make(GNode *); +void Job_Init(void); +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); +Boolean Job_TokenWithdraw(void); +void Job_ServerStart(int, int, int); +void Job_SetPrefix(void); + +#endif /* _JOB_H_ */ diff --git a/20120704/lst.h b/20120704/lst.h new file mode 100644 index 0000000..e067407 --- /dev/null +++ b/20120704/lst.h @@ -0,0 +1,189 @@ +/* $NetBSD: lst.h,v 1.18 2009/01/23 21:58:27 dsl 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 <sys/param.h> +#include <stdlib.h> + +#include "sprite.h" + +/* + * basic typedef. This is what the Lst_ functions handle + */ + +typedef struct List *Lst; +typedef struct ListNode *LstNode; + +typedef void *DuplicateProc(void *); +typedef void FreeProc(void *); + +#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_InsertBefore(Lst, LstNode, void *); +/* Insert an element after another */ +ReturnStatus Lst_InsertAfter(Lst, LstNode, void *); +/* Place an element at the front of a lst. */ +ReturnStatus Lst_AtFront(Lst, void *); +/* Place an element at the end of a lst. */ +ReturnStatus Lst_AtEnd(Lst, void *); +/* Remove an element */ +ReturnStatus Lst_Remove(Lst, LstNode); +/* Replace a node with a new value */ +ReturnStatus Lst_Replace(LstNode, void *); +/* 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); +/* Return predecessor to given element */ +LstNode Lst_Prev(LstNode); +/* Get datum from LstNode */ +void *Lst_Datum(LstNode); + +/* + * Functions for entire lists + */ +/* Find an element in a list */ +LstNode Lst_Find(Lst, const void *, int (*)(const void *, const void *)); +/* Find an element starting from somewhere */ +LstNode Lst_FindFrom(Lst, LstNode, const void *, + int (*cProc)(const void *, const void *)); +/* + * See if the given datum is on the list. Returns the LstNode containing + * the datum + */ +LstNode Lst_Member(Lst, void *); +/* Apply a function to all elements of a lst */ +int Lst_ForEach(Lst, int (*)(void *, void *), void *); +/* + * 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. + */ +int Lst_ForEachFrom(Lst, LstNode, int (*)(void *, void *), + void *); +/* + * 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, void *); +/* Remove an element from head of queue */ +void *Lst_DeQueue(Lst); + +#endif /* _LST_H_ */ diff --git a/20120704/lst.lib/Makefile b/20120704/lst.lib/Makefile new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/20120704/lst.lib/Makefile diff --git a/20120704/lst.lib/lstAppend.c b/20120704/lst.lib/lstAppend.c new file mode 100644 index 0000000..4dafe83 --- /dev/null +++ b/20120704/lst.lib/lstAppend.c @@ -0,0 +1,122 @@ +/* $NetBSD: lstAppend.c,v 1.14 2009/01/23 21:26:30 dsl 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.14 2009/01/23 21:26:30 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstAppend.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstAppend.c,v 1.14 2009/01/23 21:26:30 dsl Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstAppend.c -- + * Add a new node with a new datum after an existing node + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_InsertAfter -- + * 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 NULL. + * + *----------------------------------------------------------------------- + */ +ReturnStatus +Lst_InsertAfter(Lst l, LstNode ln, void *d) +{ + List list; + ListNode lNode; + ListNode nLNode; + + if (LstValid (l) && (ln == NULL && LstIsEmpty (l))) { + goto ok; + } + + if (!LstValid (l) || LstIsEmpty (l) || ! LstNodeValid (ln, l)) { + return (FAILURE); + } + ok: + + list = l; + lNode = ln; + + PAlloc (nLNode, ListNode); + nLNode->datum = d; + nLNode->useCount = nLNode->flags = 0; + + if (lNode == NULL) { + if (list->isCirc) { + nLNode->nextPtr = nLNode->prevPtr = nLNode; + } else { + nLNode->nextPtr = nLNode->prevPtr = NULL; + } + list->firstPtr = list->lastPtr = nLNode; + } else { + nLNode->prevPtr = lNode; + nLNode->nextPtr = lNode->nextPtr; + + lNode->nextPtr = nLNode; + if (nLNode->nextPtr != NULL) { + nLNode->nextPtr->prevPtr = nLNode; + } + + if (lNode == list->lastPtr) { + list->lastPtr = nLNode; + } + } + + return (SUCCESS); +} + diff --git a/20120704/lst.lib/lstAtEnd.c b/20120704/lst.lib/lstAtEnd.c new file mode 100644 index 0000000..10f191a --- /dev/null +++ b/20120704/lst.lib/lstAtEnd.c @@ -0,0 +1,79 @@ +/* $NetBSD: lstAtEnd.c,v 1.13 2009/01/23 21:26:30 dsl 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.13 2009/01/23 21:26:30 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstAtEnd.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstAtEnd.c,v 1.13 2009/01/23 21:26:30 dsl 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, void *d) +{ + LstNode end; + + end = Lst_Last(l); + return (Lst_InsertAfter(l, end, d)); +} diff --git a/20120704/lst.lib/lstAtFront.c b/20120704/lst.lib/lstAtFront.c new file mode 100644 index 0000000..d8be166 --- /dev/null +++ b/20120704/lst.lib/lstAtFront.c @@ -0,0 +1,76 @@ +/* $NetBSD: lstAtFront.c,v 1.13 2009/01/23 21:26:30 dsl 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.13 2009/01/23 21:26:30 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstAtFront.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstAtFront.c,v 1.13 2009/01/23 21:26:30 dsl 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, void *d) +{ + LstNode front; + + front = Lst_First(l); + return (Lst_InsertBefore(l, front, d)); +} diff --git a/20120704/lst.lib/lstClose.c b/20120704/lst.lib/lstClose.c new file mode 100644 index 0000000..06b68c5 --- /dev/null +++ b/20120704/lst.lib/lstClose.c @@ -0,0 +1,86 @@ +/* $NetBSD: lstClose.c,v 1.11 2006/10/27 21:37:25 dsl 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.11 2006/10/27 21:37:25 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstClose.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstClose.c,v 1.11 2006/10/27 21:37:25 dsl 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 = l; + + if (LstValid(l) == TRUE) { + list->isOpen = FALSE; + list->atEnd = Unknown; + } +} + diff --git a/20120704/lst.lib/lstConcat.c b/20120704/lst.lib/lstConcat.c new file mode 100644 index 0000000..534d34e --- /dev/null +++ b/20120704/lst.lib/lstConcat.c @@ -0,0 +1,185 @@ +/* $NetBSD: lstConcat.c,v 1.16 2008/12/13 15:19:29 dsl 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.16 2008/12/13 15:19:29 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstConcat.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstConcat.c,v 1.16 2008/12/13 15:19:29 dsl 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 = l1; + List list2 = l2; + + if (!LstValid (l1) || !LstValid (l2)) { + return (FAILURE); + } + + if (flags == LST_CONCLINK) { + if (list2->firstPtr != NULL) { + /* + * 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 = NULL; + /* + * 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 != NULL) { + list1->lastPtr->nextPtr = list2->firstPtr; + } else { + list1->firstPtr = list2->firstPtr; + } + list1->lastPtr = list2->lastPtr; + } + if (list1->isCirc && list1->firstPtr != NULL) { + /* + * 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 != NULL) { + /* + * 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 = NULL; + for (last = list1->lastPtr, ln = list2->firstPtr; + ln != NULL; + ln = ln->nextPtr) + { + PAlloc (nln, ListNode); + nln->datum = ln->datum; + if (last != NULL) { + 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 = NULL; + } + + if (list2->isCirc) { + list2->lastPtr->nextPtr = list2->firstPtr; + } + } + + return (SUCCESS); +} + diff --git a/20120704/lst.lib/lstDatum.c b/20120704/lst.lib/lstDatum.c new file mode 100644 index 0000000..6e2d9ad --- /dev/null +++ b/20120704/lst.lib/lstDatum.c @@ -0,0 +1,77 @@ +/* $NetBSD: lstDatum.c,v 1.13 2009/01/23 21:26:30 dsl 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.13 2009/01/23 21:26:30 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstDatum.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstDatum.c,v 1.13 2009/01/23 21:26:30 dsl 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 NULL if the node is invalid. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +void * +Lst_Datum(LstNode ln) +{ + if (ln != NULL) { + return ((ln)->datum); + } else { + return NULL; + } +} + diff --git a/20120704/lst.lib/lstDeQueue.c b/20120704/lst.lib/lstDeQueue.c new file mode 100644 index 0000000..bdb05cc --- /dev/null +++ b/20120704/lst.lib/lstDeQueue.c @@ -0,0 +1,87 @@ +/* $NetBSD: lstDeQueue.c,v 1.14 2009/01/23 21:26:30 dsl 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.14 2009/01/23 21:26:30 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstDeQueue.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstDeQueue.c,v 1.14 2009/01/23 21:26:30 dsl 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 NULL if the list + * is empty. + * + * Side Effects: + * The head node is removed from the list. + * + *----------------------------------------------------------------------- + */ +void * +Lst_DeQueue(Lst l) +{ + void *rd; + ListNode tln; + + tln = Lst_First(l); + if (tln == NULL) { + return NULL; + } + + rd = tln->datum; + if (Lst_Remove(l, tln) == FAILURE) { + return NULL; + } else { + return (rd); + } +} + diff --git a/20120704/lst.lib/lstDestroy.c b/20120704/lst.lib/lstDestroy.c new file mode 100644 index 0000000..92c5b2b --- /dev/null +++ b/20120704/lst.lib/lstDestroy.c @@ -0,0 +1,101 @@ +/* $NetBSD: lstDestroy.c,v 1.16 2008/12/13 15:19:29 dsl 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.16 2008/12/13 15:19:29 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstDestroy.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstDestroy.c,v 1.16 2008/12/13 15:19:29 dsl 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 list, FreeProc *freeProc) +{ + ListNode ln; + ListNode tln = NULL; + + if (list == NULL) + return; + + /* To ease scanning */ + if (list->lastPtr != NULL) + list->lastPtr->nextPtr = NULL; + else { + free(list); + return; + } + + if (freeProc) { + for (ln = list->firstPtr; ln != NULL; ln = tln) { + tln = ln->nextPtr; + freeProc(ln->datum); + free(ln); + } + } else { + for (ln = list->firstPtr; ln != NULL; ln = tln) { + tln = ln->nextPtr; + free(ln); + } + } + + free(list); +} diff --git a/20120704/lst.lib/lstDupl.c b/20120704/lst.lib/lstDupl.c new file mode 100644 index 0000000..2174ff7 --- /dev/null +++ b/20120704/lst.lib/lstDupl.c @@ -0,0 +1,107 @@ +/* $NetBSD: lstDupl.c,v 1.16 2009/01/23 21:26:30 dsl 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.16 2009/01/23 21:26:30 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstDupl.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstDupl.c,v 1.16 2009/01/23 21:26:30 dsl 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 void *is + * given, the individual client elements will be duplicated as well. + * + * Input: + * l the list to duplicate + * copyProc A function to duplicate each void * + * + * Results: + * The new Lst structure or NULL if failure. + * + * Side Effects: + * A new list is created. + *----------------------------------------------------------------------- + */ +Lst +Lst_Duplicate(Lst l, DuplicateProc *copyProc) +{ + Lst nl; + ListNode ln; + List list = l; + + if (!LstValid (l)) { + return NULL; + } + + nl = Lst_Init(list->isCirc); + if (nl == NULL) { + return NULL; + } + + ln = list->firstPtr; + while (ln != NULL) { + if (copyProc != NULL) { + if (Lst_AtEnd(nl, copyProc(ln->datum)) == FAILURE) { + return NULL; + } + } else if (Lst_AtEnd(nl, ln->datum) == FAILURE) { + return NULL; + } + + if (list->isCirc && ln == list->lastPtr) { + ln = NULL; + } else { + ln = ln->nextPtr; + } + } + + return (nl); +} diff --git a/20120704/lst.lib/lstEnQueue.c b/20120704/lst.lib/lstEnQueue.c new file mode 100644 index 0000000..be386c9 --- /dev/null +++ b/20120704/lst.lib/lstEnQueue.c @@ -0,0 +1,78 @@ +/* $NetBSD: lstEnQueue.c,v 1.13 2009/01/23 21:26:30 dsl 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.13 2009/01/23 21:26:30 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstEnQueue.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstEnQueue.c,v 1.13 2009/01/23 21:26:30 dsl 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_InsertAfter. + * + * 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, void *d) +{ + if (LstValid (l) == FALSE) { + return (FAILURE); + } + + return (Lst_InsertAfter(l, Lst_Last(l), d)); +} + diff --git a/20120704/lst.lib/lstFind.c b/20120704/lst.lib/lstFind.c new file mode 100644 index 0000000..d07dbe7 --- /dev/null +++ b/20120704/lst.lib/lstFind.c @@ -0,0 +1,74 @@ +/* $NetBSD: lstFind.c,v 1.15 2009/01/23 21:58:28 dsl 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.15 2009/01/23 21:58:28 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstFind.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstFind.c,v 1.15 2009/01/23 21:58:28 dsl 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 NULL if none matches. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +LstNode +Lst_Find(Lst l, const void *d, int (*cProc)(const void *, const void *)) +{ + return (Lst_FindFrom(l, Lst_First(l), d, cProc)); +} + diff --git a/20120704/lst.lib/lstFindFrom.c b/20120704/lst.lib/lstFindFrom.c new file mode 100644 index 0000000..e2beab6 --- /dev/null +++ b/20120704/lst.lib/lstFindFrom.c @@ -0,0 +1,90 @@ +/* $NetBSD: lstFindFrom.c,v 1.15 2009/01/23 21:58:28 dsl 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.15 2009/01/23 21:58:28 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstFindFrom.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstFindFrom.c,v 1.15 2009/01/23 21:58:28 dsl 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 NULL + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +LstNode +Lst_FindFrom(Lst l, LstNode ln, const void *d, + int (*cProc)(const void *, const void *)) +{ + ListNode tln; + + if (!LstValid (l) || LstIsEmpty (l) || !LstNodeValid (ln, l)) { + return NULL; + } + + tln = ln; + + do { + if ((*cProc)(tln->datum, d) == 0) + return (tln); + tln = tln->nextPtr; + } while (tln != ln && tln != NULL); + + return NULL; +} + diff --git a/20120704/lst.lib/lstFirst.c b/20120704/lst.lib/lstFirst.c new file mode 100644 index 0000000..4e8334f --- /dev/null +++ b/20120704/lst.lib/lstFirst.c @@ -0,0 +1,77 @@ +/* $NetBSD: lstFirst.c,v 1.12 2008/12/13 15:19:29 dsl 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.12 2008/12/13 15:19:29 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstFirst.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstFirst.c,v 1.12 2008/12/13 15:19:29 dsl 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 NULL if the list is empty. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +LstNode +Lst_First(Lst l) +{ + if (!LstValid (l) || LstIsEmpty (l)) { + return NULL; + } else { + return (l->firstPtr); + } +} + diff --git a/20120704/lst.lib/lstForEach.c b/20120704/lst.lib/lstForEach.c new file mode 100644 index 0000000..917e4ea --- /dev/null +++ b/20120704/lst.lib/lstForEach.c @@ -0,0 +1,76 @@ +/* $NetBSD: lstForEach.c,v 1.13 2009/01/23 21:26:30 dsl 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.13 2009/01/23 21:26:30 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstForEach.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstForEach.c,v 1.13 2009/01/23 21:26:30 dsl 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*/ +int +Lst_ForEach(Lst l, int (*proc)(void *, void *), void *d) +{ + return Lst_ForEachFrom(l, Lst_First(l), proc, d); +} + diff --git a/20120704/lst.lib/lstForEachFrom.c b/20120704/lst.lib/lstForEachFrom.c new file mode 100644 index 0000000..c7f44ad --- /dev/null +++ b/20120704/lst.lib/lstForEachFrom.c @@ -0,0 +1,125 @@ +/* $NetBSD: lstForEachFrom.c,v 1.17 2009/01/23 21:26:30 dsl 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.17 2009/01/23 21:26:30 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstForEachFrom.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstForEachFrom.c,v 1.17 2009/01/23 21:26:30 dsl 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*/ +int +Lst_ForEachFrom(Lst l, LstNode ln, int (*proc)(void *, void *), + void *d) +{ + ListNode tln = ln; + List list = l; + ListNode next; + Boolean done; + int result; + + if (!LstValid (list) || LstIsEmpty (list)) { + return 0; + } + + 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 == NULL || 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); + + return result; +} + diff --git a/20120704/lst.lib/lstInit.c b/20120704/lst.lib/lstInit.c new file mode 100644 index 0000000..f98ac42 --- /dev/null +++ b/20120704/lst.lib/lstInit.c @@ -0,0 +1,85 @@ +/* $NetBSD: lstInit.c,v 1.12 2008/12/13 15:19:29 dsl 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.12 2008/12/13 15:19:29 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstInit.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstInit.c,v 1.12 2008/12/13 15:19:29 dsl 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 = NULL; + nList->lastPtr = NULL; + nList->isOpen = FALSE; + nList->isCirc = circ; + nList->atEnd = Unknown; + + return (nList); +} diff --git a/20120704/lst.lib/lstInsert.c b/20120704/lst.lib/lstInsert.c new file mode 100644 index 0000000..77187bb --- /dev/null +++ b/20120704/lst.lib/lstInsert.c @@ -0,0 +1,122 @@ +/* $NetBSD: lstInsert.c,v 1.14 2009/01/23 21:26:30 dsl 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.14 2009/01/23 21:26:30 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstInsert.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstInsert.c,v 1.14 2009/01/23 21:26:30 dsl Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstInsert.c -- + * Insert a new datum before an old one + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_InsertBefore -- + * 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_InsertBefore(Lst l, LstNode ln, void *d) +{ + ListNode nLNode; /* new lnode for d */ + ListNode lNode = ln; + List list = l; + + + /* + * check validity of arguments + */ + if (LstValid (l) && (LstIsEmpty (l) && ln == NULL)) + 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 == NULL) { + if (list->isCirc) { + nLNode->prevPtr = nLNode->nextPtr = nLNode; + } else { + nLNode->prevPtr = nLNode->nextPtr = NULL; + } + list->firstPtr = list->lastPtr = nLNode; + } else { + nLNode->prevPtr = lNode->prevPtr; + nLNode->nextPtr = lNode; + + if (nLNode->prevPtr != NULL) { + nLNode->prevPtr->nextPtr = nLNode; + } + lNode->prevPtr = nLNode; + + if (lNode == list->firstPtr) { + list->firstPtr = nLNode; + } + } + + return (SUCCESS); +} + diff --git a/20120704/lst.lib/lstInt.h b/20120704/lst.lib/lstInt.h new file mode 100644 index 0000000..34a2fbd --- /dev/null +++ b/20120704/lst.lib/lstInt.h @@ -0,0 +1,105 @@ +/* $NetBSD: lstInt.h,v 1.20 2009/01/24 14:43:29 dsl 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 "../lst.h" +#include "../make_malloc.h" + +typedef struct ListNode { + struct ListNode *prevPtr; /* previous element in list */ + struct ListNode *nextPtr; /* next in list */ + unsigned int useCount:8, /* Count of functions using the node. + * node may not be deleted until count + * goes to 0 */ + flags:8; /* Node status flags */ + void *datum; /* datum associated with this element */ +} *ListNode; +/* + * Flags required for synchronization + */ +#define LN_DELETED 0x0001 /* List node should be removed when done */ + +typedef enum { + Head, Middle, Tail, Unknown +} Where; + +typedef struct List { + 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. NULL if + * *just* opened */ + ListNode prevPtr; /* Previous node, if open. Used by + * Lst_Remove */ +} *List; + +/* + * PAlloc (var, ptype) -- + * Allocate a pointer-typedef structure 'ptype' into the variable 'var' + */ +#define PAlloc(var,ptype) var = (ptype) bmake_malloc(sizeof *(var)) + +/* + * LstValid (l) -- + * Return TRUE if the list l is valid + */ +#define LstValid(l) ((Lst)(l) != NULL) + +/* + * LstNodeValid (ln, l) -- + * Return TRUE if the LstNode ln is valid with respect to l + */ +#define LstNodeValid(ln, l) ((ln) != NULL) + +/* + * LstIsEmpty (l) -- + * TRUE if the list l is empty. + */ +#define LstIsEmpty(l) (((List)(l))->firstPtr == NULL) + +#endif /* _LSTINT_H_ */ diff --git a/20120704/lst.lib/lstIsAtEnd.c b/20120704/lst.lib/lstIsAtEnd.c new file mode 100644 index 0000000..70270d2 --- /dev/null +++ b/20120704/lst.lib/lstIsAtEnd.c @@ -0,0 +1,87 @@ +/* $NetBSD: lstIsAtEnd.c,v 1.13 2008/02/15 21:29:50 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstIsAtEnd.c,v 1.13 2008/02/15 21:29:50 christos Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstIsAtEnd.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstIsAtEnd.c,v 1.13 2008/02/15 21:29:50 christos 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 = l; + + return (!LstValid (l) || !list->isOpen || + (list->atEnd == Head) || (list->atEnd == Tail)); +} + diff --git a/20120704/lst.lib/lstIsEmpty.c b/20120704/lst.lib/lstIsEmpty.c new file mode 100644 index 0000000..8b1d6ed --- /dev/null +++ b/20120704/lst.lib/lstIsEmpty.c @@ -0,0 +1,75 @@ +/* $NetBSD: lstIsEmpty.c,v 1.11 2008/12/13 15:19:29 dsl 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.11 2008/12/13 15:19:29 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstIsEmpty.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstIsEmpty.c,v 1.11 2008/12/13 15:19:29 dsl 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 == NULL (or if + * the list itself is NULL). + *----------------------------------------------------------------------- + */ +Boolean +Lst_IsEmpty(Lst l) +{ + return ( ! LstValid (l) || LstIsEmpty(l)); +} + diff --git a/20120704/lst.lib/lstLast.c b/20120704/lst.lib/lstLast.c new file mode 100644 index 0000000..096ca24 --- /dev/null +++ b/20120704/lst.lib/lstLast.c @@ -0,0 +1,77 @@ +/* $NetBSD: lstLast.c,v 1.12 2008/12/13 15:19:29 dsl 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.12 2008/12/13 15:19:29 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstLast.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstLast.c,v 1.12 2008/12/13 15:19:29 dsl 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 NULL if the list is empty. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +LstNode +Lst_Last(Lst l) +{ + if (!LstValid(l) || LstIsEmpty (l)) { + return NULL; + } else { + return (l->lastPtr); + } +} + diff --git a/20120704/lst.lib/lstMember.c b/20120704/lst.lib/lstMember.c new file mode 100644 index 0000000..0ff2ed1 --- /dev/null +++ b/20120704/lst.lib/lstMember.c @@ -0,0 +1,74 @@ +/* $NetBSD: lstMember.c,v 1.13 2009/01/23 21:26:30 dsl 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.13 2009/01/23 21:26:30 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstMember.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstMember.c,v 1.13 2009/01/23 21:26:30 dsl 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, void *d) +{ + List list = l; + ListNode lNode; + + lNode = list->firstPtr; + if (lNode == NULL) { + return NULL; + } + + do { + if (lNode->datum == d) { + return lNode; + } + lNode = lNode->nextPtr; + } while (lNode != NULL && lNode != list->firstPtr); + + return NULL; +} diff --git a/20120704/lst.lib/lstNext.c b/20120704/lst.lib/lstNext.c new file mode 100644 index 0000000..5c2e0ee --- /dev/null +++ b/20120704/lst.lib/lstNext.c @@ -0,0 +1,120 @@ +/* $NetBSD: lstNext.c,v 1.12 2008/12/13 15:19:29 dsl 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.12 2008/12/13 15:19:29 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstNext.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstNext.c,v 1.12 2008/12/13 15:19:29 dsl 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 NULL if the list has yet to be opened. Also + * if the list is non-circular and the end has been reached, NULL + * is returned. + * + * Side Effects: + * the curPtr field is updated. + * + *----------------------------------------------------------------------- + */ +LstNode +Lst_Next(Lst l) +{ + ListNode tln; + List list = l; + + if ((LstValid (l) == FALSE) || + (list->isOpen == FALSE)) { + return NULL; + } + + list->prevPtr = list->curPtr; + + if (list->curPtr == NULL) { + 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 = NULL; + list->atEnd = Tail; + } + } else { + tln = list->curPtr->nextPtr; + list->curPtr = tln; + + if (tln == list->firstPtr || tln == NULL) { + /* + * 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 (tln); +} + diff --git a/20120704/lst.lib/lstOpen.c b/20120704/lst.lib/lstOpen.c new file mode 100644 index 0000000..941293e --- /dev/null +++ b/20120704/lst.lib/lstOpen.c @@ -0,0 +1,87 @@ +/* $NetBSD: lstOpen.c,v 1.12 2008/12/13 15:19:29 dsl 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.12 2008/12/13 15:19:29 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstOpen.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstOpen.c,v 1.12 2008/12/13 15:19:29 dsl 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 NULL 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); + } + (l)->isOpen = TRUE; + (l)->atEnd = LstIsEmpty (l) ? Head : Unknown; + (l)->curPtr = NULL; + + return (SUCCESS); +} + diff --git a/20120704/lst.lib/lstPrev.c b/20120704/lst.lib/lstPrev.c new file mode 100644 index 0000000..0ec865d --- /dev/null +++ b/20120704/lst.lib/lstPrev.c @@ -0,0 +1,79 @@ +/* $NetBSD: lstPrev.c,v 1.3 2008/12/13 15:19:29 dsl 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: lstPrev.c,v 1.3 2008/12/13 15:19:29 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstSucc.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstPrev.c,v 1.3 2008/12/13 15:19:29 dsl Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstPrev.c -- + * return the predecessor to a given node + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Prev -- + * Return the predecessor to the given node on its list. + * + * Results: + * The predecessor 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 + * predecessor). + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +LstNode +Lst_Prev(LstNode ln) +{ + if (ln == NULL) { + return NULL; + } else { + return (ln->prevPtr); + } +} + diff --git a/20120704/lst.lib/lstRemove.c b/20120704/lst.lib/lstRemove.c new file mode 100644 index 0000000..54d7b33 --- /dev/null +++ b/20120704/lst.lib/lstRemove.c @@ -0,0 +1,136 @@ +/* $NetBSD: lstRemove.c,v 1.14 2008/12/13 15:19:29 dsl 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.14 2008/12/13 15:19:29 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstRemove.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstRemove.c,v 1.14 2008/12/13 15:19:29 dsl 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 NULL 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 = l; + ListNode lNode = ln; + + if (!LstValid (l) || + !LstNodeValid (ln, l)) { + return (FAILURE); + } + + /* + * unlink it from the list + */ + if (lNode->nextPtr != NULL) { + lNode->nextPtr->prevPtr = lNode->prevPtr; + } + if (lNode->prevPtr != NULL) { + 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 == NULL), we set the + * end to be Unknown, since it is. + */ + if (list->isOpen && (list->curPtr == lNode)) { + list->curPtr = list->prevPtr; + if (list->curPtr == NULL) { + 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 = NULL; + } + + /* + * 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/20120704/lst.lib/lstReplace.c b/20120704/lst.lib/lstReplace.c new file mode 100644 index 0000000..090e91a --- /dev/null +++ b/20120704/lst.lib/lstReplace.c @@ -0,0 +1,78 @@ +/* $NetBSD: lstReplace.c,v 1.13 2009/01/23 21:26:30 dsl 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.13 2009/01/23 21:26:30 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstReplace.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstReplace.c,v 1.13 2009/01/23 21:26:30 dsl 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, void *d) +{ + if (ln == NULL) { + return (FAILURE); + } else { + (ln)->datum = d; + return (SUCCESS); + } +} + diff --git a/20120704/lst.lib/lstSucc.c b/20120704/lst.lib/lstSucc.c new file mode 100644 index 0000000..3f13aa5 --- /dev/null +++ b/20120704/lst.lib/lstSucc.c @@ -0,0 +1,79 @@ +/* $NetBSD: lstSucc.c,v 1.13 2008/12/13 15:19:29 dsl 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.13 2008/12/13 15:19:29 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstSucc.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstSucc.c,v 1.13 2008/12/13 15:19:29 dsl 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 == NULL) { + return NULL; + } else { + return (ln->nextPtr); + } +} + diff --git a/20120704/machine.sh b/20120704/machine.sh new file mode 100755 index 0000000..32a0f7a --- /dev/null +++ b/20120704/machine.sh @@ -0,0 +1,96 @@ +: +# derrived from /etc/rc_d/os.sh + +# RCSid: +# $Id: machine.sh,v 1.16 2010/10/17 00:05:51 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= + +# there is at least one case of `uname -p` outputting +# a bunch of usless drivel +case "$machine" in +unknown|*[!A-Za-z0-9_-]*) + 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/20120704/main.c b/20120704/main.c new file mode 100644 index 0000000..085c534 --- /dev/null +++ b/20120704/main.c @@ -0,0 +1,2078 @@ +/* $NetBSD: main.c,v 1.200 2012/06/12 19:21:51 joerg 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.200 2012/06/12 19:21:51 joerg Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993\ + The Regents of the University of California. All rights reserved."); +#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.200 2012/06/12 19:21:51 joerg 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 <sys/types.h> +#include <sys/time.h> +#include <sys/param.h> +#include <sys/resource.h> +#include <signal.h> +#include <sys/stat.h> +#ifdef MAKE_NATIVE +#include <sys/utsname.h> +#endif +#include "wait.h" + +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> + +#include "make.h" +#include "hash.h" +#include "dir.h" +#include "job.h" +#include "pathnames.h" +#include "trace.h" + +#ifdef USE_IOVEC +#include <sys/uio.h> +#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 maxJobTokens; /* -j 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 ignoreErrors; /* -i flag */ +Boolean beSilent; /* -s flag */ +Boolean oldVars; /* variable substitution style */ +Boolean checkEnvFirst; /* -e flag */ +Boolean parseWarnFatal; /* -W flag */ +Boolean jobServer; /* -J flag */ +static int jp_0 = -1, jp_1 = -1; /* ends of parent job pipe */ +Boolean varNoExportEnv; /* -X flag */ +Boolean doing_depend; /* Set while reading .depend */ +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(const void *, const void *); +static void usage(void) MAKE_ATTR_DEAD; + +static Boolean ignorePWD; /* if we use -C, PWD is meaningless */ +static char objdir[MAXPATHLEN + 1]; /* where we chdir'ed to */ +char curdir[MAXPATHLEN + 1]; /* Startup directory */ +char *progname; /* the program name */ +char *makeDependfile; +pid_t myPid; + +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; + +static void +parse_debug_options(const char *argvalue) +{ + const char *modules; + const char *mode; + char *fname; + int len; + + for (modules = argvalue; *modules; ++modules) { + switch (*modules) { + case 'A': + debug = ~0; + break; + case 'a': + debug |= DEBUG_ARCH; + break; + case 'C': + debug |= DEBUG_CWD; + 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 'l': + debug |= DEBUG_LOUD; + break; + case 'M': + debug |= DEBUG_META; + break; + case 'm': + debug |= DEBUG_MAKE; + break; + case 'n': + debug |= DEBUG_SCRIPT; + break; + case 'p': + debug |= DEBUG_PARSE; + 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; + case 'F': + if (debug_file != stdout && debug_file != stderr) + fclose(debug_file); + if (*++modules == '+') { + modules++; + mode = "a"; + } else + mode = "w"; + if (strcmp(modules, "stdout") == 0) { + debug_file = stdout; + goto debug_setbuf; + } + if (strcmp(modules, "stderr") == 0) { + debug_file = stderr; + goto debug_setbuf; + } + len = strlen(modules); + fname = malloc(len + 20); + memcpy(fname, modules, len + 1); + /* Let the filename be modified by the pid */ + if (strcmp(fname + len - 3, ".%d") == 0) + snprintf(fname + len - 2, 20, "%d", getpid()); + debug_file = fopen(fname, mode); + if (!debug_file) { + fprintf(stderr, "Cannot open debug file %s\n", + fname); + usage(); + } + free(fname); + goto debug_setbuf; + default: + (void)fprintf(stderr, + "%s: illegal argument to d option -- %c\n", + progname, *modules); + usage(); + } + } +debug_setbuf: + /* + * Make the debug_file unbuffered, and make + * stdout line buffered (unless debugfile == stdout). + */ + setvbuf(debug_file, NULL, _IONBF, 0); + if (debug_file != stdout) { + setvbuf(stdout, NULL, _IOLBF, 0); + } +} + +/*- + * 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 */ + +#define OPTFLAGS "BC:D:I:J:NST:V:WXd:ef:ij:km:nqrst" +/* 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] == ':') { + /* -<something> found, and <something> should have an arg */ + inOption = FALSE; + arginc = 1; + argvalue = optscan; + if(*argvalue == '\0') { + if (argc < 3) + goto noarg; + 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); + Var_Set(MAKE_MODE, "compat", VAR_GLOBAL, 0); + break; + case 'C': + if (chdir(argvalue) == -1) { + (void)fprintf(stderr, + "%s: chdir %s: %s\n", + progname, argvalue, + strerror(errno)); + exit(1); + } + if (getcwd(curdir, MAXPATHLEN) == NULL) { + (void)fprintf(stderr, "%s: %s.\n", progname, strerror(errno)); + exit(2); + } + ignorePWD = TRUE; + break; + case 'D': + if (argvalue == NULL || argvalue[0] == 0) goto noarg; + Var_Set(argvalue, "1", VAR_GLOBAL, 0); + Var_Append(MAKEFLAGS, "-D", VAR_GLOBAL); + Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); + break; + case 'I': + if (argvalue == NULL) goto noarg; + Parse_AddIncludeDir(argvalue); + Var_Append(MAKEFLAGS, "-I", VAR_GLOBAL); + Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); + break; + case 'J': + if (argvalue == NULL) goto noarg; + if (sscanf(argvalue, "%d,%d", &jp_0, &jp_1) != 2) { + (void)fprintf(stderr, + "%s: internal error -- J option malformed (%s)\n", + progname, argvalue); + usage(); + } + if ((fcntl(jp_0, F_GETFD, 0) < 0) || + (fcntl(jp_1, F_GETFD, 0) < 0)) { +#if 0 + (void)fprintf(stderr, + "%s: ###### warning -- J descriptors were closed!\n", + progname); + exit(2); +#endif + jp_0 = -1; + jp_1 = -1; + compatMake = TRUE; + } else { + Var_Append(MAKEFLAGS, "-J", VAR_GLOBAL); + Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); + jobServer = TRUE; + } + break; + case 'N': + noExecute = TRUE; + noRecursiveExecute = TRUE; + Var_Append(MAKEFLAGS, "-N", VAR_GLOBAL); + break; + case 'S': + keepgoing = FALSE; + Var_Append(MAKEFLAGS, "-S", VAR_GLOBAL); + break; + case 'T': + if (argvalue == NULL) goto noarg; + tracefile = bmake_strdup(argvalue); + Var_Append(MAKEFLAGS, "-T", VAR_GLOBAL); + Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); + break; + case 'V': + if (argvalue == NULL) goto noarg; + printVars = TRUE; + (void)Lst_AtEnd(variables, argvalue); + Var_Append(MAKEFLAGS, "-V", VAR_GLOBAL); + Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); + break; + case 'W': + parseWarnFatal = TRUE; + break; + case 'X': + varNoExportEnv = TRUE; + Var_Append(MAKEFLAGS, "-X", VAR_GLOBAL); + break; + case 'd': + if (argvalue == NULL) goto noarg; + /* If '-d-opts' don't pass to children */ + if (argvalue[0] == '-') + argvalue++; + else { + Var_Append(MAKEFLAGS, "-d", VAR_GLOBAL); + Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); + } + parse_debug_options(argvalue); + break; + case 'e': + checkEnvFirst = TRUE; + Var_Append(MAKEFLAGS, "-e", VAR_GLOBAL); + break; + case 'f': + if (argvalue == NULL) goto noarg; + (void)Lst_AtEnd(makefiles, argvalue); + break; + case 'i': + ignoreErrors = TRUE; + Var_Append(MAKEFLAGS, "-i", VAR_GLOBAL); + break; + case 'j': + if (argvalue == NULL) goto noarg; + forceJobs = TRUE; + maxJobs = strtol(argvalue, &p, 0); + if (*p != '\0' || maxJobs < 1) { + (void)fprintf(stderr, "%s: illegal argument to -j -- must be positive integer!\n", + progname); + exit(1); + } + Var_Append(MAKEFLAGS, "-j", VAR_GLOBAL); + Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); + Var_Set(".MAKE.JOBS", argvalue, VAR_GLOBAL, 0); + maxJobTokens = maxJobs; + break; + case 'k': + keepgoing = TRUE; + Var_Append(MAKEFLAGS, "-k", VAR_GLOBAL); + break; + case 'm': + if (argvalue == NULL) goto noarg; + /* look for magic parent directory search string */ + if (strncmp(".../", argvalue, 4) == 0) { + if (!Dir_FindHereOrAbove(curdir, argvalue+4, + found_path, sizeof(found_path))) + break; /* nothing doing */ + (void)Dir_AddDir(sysIncPath, found_path); + } else { + (void)Dir_AddDir(sysIncPath, argvalue); + } + Var_Append(MAKEFLAGS, "-m", VAR_GLOBAL); + Var_Append(MAKEFLAGS, argvalue, 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, bmake_strdup(argv[1])); + } + + return; +noarg: + (void)fprintf(stderr, "%s: option requires an argument -- %c\n", + progname, c); + usage(); +} + +/*- + * 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(const 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 = bmake_malloc(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); + if (argv == NULL) { + Error("Unterminated quoted string [%s]", buf); + free(buf); + return; + } + 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; +} + +/*- + * ReadAllMakefiles -- + * wrapper around ReadMakefile() to read all. + * + * Results: + * TRUE if ok, FALSE on error + */ +static int +ReadAllMakefiles(const void *p, const void *q) +{ + return (ReadMakefile(p, q) == 0); +} + +int +str2Lst_Append(Lst lp, char *str, const char *sep) +{ + char *cp; + int n; + + if (!sep) + sep = " \t"; + + for (n = 0, cp = strtok(str, sep); cp; cp = strtok(NULL, sep)) { + (void)Lst_AtEnd(lp, cp); + n++; + } + return (n); +} + +#ifdef SIGINFO +/*ARGSUSED*/ +static void +siginfo(int signo MAKE_ATTR_UNUSED) +{ + char dir[MAXPATHLEN]; + char str[2 * MAXPATHLEN]; + int len; + if (getcwd(dir, sizeof(dir)) == NULL) + return; + len = snprintf(str, sizeof(str), "%s: Working in: %s\n", progname, dir); + if (len > 0) + (void)write(STDERR_FILENO, str, (size_t)len); +} +#endif + +/* + * Allow makefiles some control over the mode we run in. + */ +void +MakeMode(const char *mode) +{ + char *mp = NULL; + + if (!mode) + mode = mp = Var_Subst(NULL, "${" MAKE_MODE ":tl}", VAR_GLOBAL, 0); + + if (mode && *mode) { + if (strstr(mode, "compat")) { + compatMake = TRUE; + forceJobs = FALSE; + } +#if USE_META + if (strstr(mode, "meta")) + meta_init(mode); +#endif + } + if (mp) + free(mp); +} + +/*- + * 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 = FALSE; /* FALSE if all targets up to date */ + struct stat sb, sa; + char *p1, *path, *pwd; + char mdpath[MAXPATHLEN]; +#ifdef FORCE_MACHINE + const char *machine = FORCE_MACHINE; +#else + const 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 */ +#ifdef MAKE_NATIVE + struct utsname utsname; +#endif + + /* default to writing debug to stderr */ + debug_file = stderr; + +#ifdef SIGINFO + (void)bmake_signal(SIGINFO, siginfo); +#endif + /* + * Set the seed to produce a different random sequence + * 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 + + /* + * 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 + 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 + } + + myPid = getpid(); /* remember this for vFork() */ + + /* + * Just in case MAKEOBJDIR wants us to do something tricky. + */ + Var_Init(); /* Initialize the lists of variables for + * parsing arguments */ + 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 */ + /* + * This is the traditional preference for makefiles. + */ +#ifndef MAKEFILE_PREFERENCE_LIST +# define MAKEFILE_PREFERENCE_LIST "makefile Makefile" +#endif + Var_Set(MAKEFILE_PREFERENCE, MAKEFILE_PREFERENCE_LIST, + VAR_GLOBAL, 0); + Var_Set(MAKE_DEPENDFILE, ".depend", VAR_GLOBAL, 0); + + 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 */ + debug = 0; /* No debug verbosity, please. */ + jobsRunning = FALSE; + + maxJobs = DEFMAXLOCAL; /* Set default local max concurrency */ + maxJobTokens = maxJobs; + compatMake = FALSE; /* No compat mode */ + ignorePWD = FALSE; + + /* + * 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(); + if (argv[0][0] == '/' || strchr(argv[0], '/') == NULL) { + /* + * Leave alone if it is an absolute path, or if it does + * not contain a '/' in which case we need to find it in + * the path, like execvp(3) and the shells do. + */ + p1 = argv[0]; + } else { + /* + * A relative path, canonicalize it. + */ + p1 = realpath(argv[0], mdpath); + if (!p1 || *p1 != '/' || stat(p1, &sb) < 0) { + p1 = argv[0]; /* realpath failed */ + } + } + Var_Set("MAKE", p1, VAR_GLOBAL, 0); + Var_Set(".MAKE", p1, 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); + + /* + * Set some other useful macros + */ + { + char tmp[64]; + const char *ep; + + if (!(ep = getenv(MAKE_LEVEL))) { +#ifdef MAKE_LEVEL_SAFE + if (!(ep = getenv(MAKE_LEVEL_SAFE))) +#endif + ep = "0"; + } + Var_Set(MAKE_LEVEL, ep, VAR_GLOBAL, 0); + snprintf(tmp, sizeof(tmp), "%u", myPid); + Var_Set(".MAKE.PID", tmp, VAR_GLOBAL, 0); + snprintf(tmp, sizeof(tmp), "%u", getppid()); + Var_Set(".MAKE.PPID", tmp, VAR_GLOBAL, 0); + } + Job_SetPrefix(); + + /* + * 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 + + /* + * Find where we are (now). + * We take care of PWD for the automounter below... + */ + if (getcwd(curdir, MAXPATHLEN) == NULL) { + (void)fprintf(stderr, "%s: getcwd: %s.\n", + progname, strerror(errno)); + exit(2); + } + + MainParseArgs(argc, argv); + + /* + * Verify that cwd is sane. + */ + if (stat(curdir, &sa) == -1) { + (void)fprintf(stderr, "%s: %s: %s.\n", + progname, curdir, strerror(errno)); + exit(2); + } + + /* + * All this code is so that we know where we are when we start up + * on a different machine with pmake. + * Overriding getcwd() with $PWD totally breaks MAKEOBJDIRPREFIX + * since the value of curdir can vary 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 (!ignorePWD && + (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 + Var_Set(".CURDIR", curdir, VAR_GLOBAL, 0); + + /* + * 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); + } + } + + /* + * 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 = NULL; + (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 != NULL; + 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 = bmake_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, NULL, ReadMakefile); + if (ln == NULL) + Fatal("%s: cannot open %s.", progname, + (char *)Lst_Datum(ln)); + } + + if (!Lst_IsEmpty(makefiles)) { + LstNode ln; + + ln = Lst_Find(makefiles, NULL, ReadAllMakefiles); + if (ln != NULL) + Fatal("%s: cannot open %s.", progname, + (char *)Lst_Datum(ln)); + } else { + p1 = Var_Subst(NULL, "${" MAKEFILE_PREFERENCE "}", + VAR_CMD, 0); + if (p1) { + (void)str2Lst_Append(makefiles, p1, NULL); + (void)Lst_Find(makefiles, NULL, ReadMakefile); + free(p1); + } + } + + /* In particular suppress .depend for '-r -V .OBJDIR -f /dev/null' */ + if (!noBuiltins || !printVars) { + makeDependfile = Var_Subst(NULL, "${.MAKE.DEPENDFILE:T}", + VAR_CMD, 0); + doing_depend = TRUE; + (void)ReadMakefile(makeDependfile, NULL); + doing_depend = FALSE; + } + + MakeMode(NULL); + + Var_Append("MFLAGS", Var_Value(MAKEFLAGS, VAR_GLOBAL, &p1), VAR_GLOBAL); + if (p1) + free(p1); + + if (!compatMake) + Job_ServerStart(maxJobTokens, jp_0, jp_1); + if (DEBUG(JOB)) + fprintf(debug_file, "job_pipe %d %d, maxjobs %d, tokens %d, compat %d\n", + jp_0, jp_1, maxJobs, maxJobTokens, 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. + * <directory>:<directory>:<directory>... + */ + 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 != NULL; + 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); + } + } else { + /* + * 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) { + /* + * 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) { + Job_Init(); + jobsRunning = TRUE; + } + + /* Traverse the graph, checking on all the targets */ + outOfDate = Make_Run(targs); + } else { + /* + * Compat_Init will take care of creating all the + * targets as well as initializing the module. + */ + Compat_Run(targs); + } + } + +#ifdef CLEANUP + Lst_Destroy(targs, NULL); + Lst_Destroy(variables, NULL); + Lst_Destroy(makefiles, NULL); + 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(); + + return outOfDate ? 1 : 0; +} + +/*- + * ReadMakefile -- + * Open and parse the given makefile. + * + * Results: + * 0 if ok. -1 if couldn't open file. + * + * Side Effects: + * lots + */ +static int +ReadMakefile(const void *p, const void *q MAKE_ATTR_UNUSED) +{ + const char *fname = p; /* makefile to read */ + int fd; + size_t len = MAXPATHLEN; + char *name, *path = bmake_malloc(len); + + if (!strcmp(fname, "-")) { + Parse_File(NULL /*stdin*/, -1); + Var_Set("MAKEFILE", "", VAR_GLOBAL, 0); + } else { + /* 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 = bmake_realloc(path, len = 2 * plen); + + (void)snprintf(path, len, "%s/%s", curdir, fname); + fd = open(path, O_RDONLY); + if (fd != -1) { + fname = path; + goto found; + } + + /* If curdir failed, try objdir (ala .depend) */ + plen = strlen(objdir) + strlen(fname) + 2; + if (len < plen) + path = bmake_realloc(path, len = 2 * plen); + (void)snprintf(path, len, "%s/%s", objdir, fname); + fd = open(path, O_RDONLY); + if (fd != -1) { + fname = path; + goto found; + } + } else { + fd = open(fname, O_RDONLY); + if (fd != -1) + 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 || (fd = open(name, O_RDONLY)) == -1) { + if (name) + free(name); + free(path); + return(-1); + } + 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 (!doing_depend) + Var_Set("MAKEFILE", fname, VAR_GLOBAL, 0); + Parse_File(fname, fd); + } + free(path); + return(0); +} + + +/* + * 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) { + if (DEBUG(CWD)) + fprintf(debug_file, "check_cwd: check is off.\n"); + return NULL; + } + + if (make[0] == NULL) { + if (Var_Exists("NOCHECKMAKECHDIR", VAR_GLOBAL)) { + Check_Cwd_Off = 1; + if (DEBUG(CWD)) + fprintf(debug_file, "check_cwd: turning check off.\n"); + 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) { + if (DEBUG(CWD)) + fprintf(debug_file, "check_cwd: empty command.\n"); + return NULL; /* initialization only */ + } + + if (getenv("MAKEOBJDIR") == NULL && + getenv("MAKEOBJDIRPREFIX") == NULL) { + if (DEBUG(CWD)) + fprintf(debug_file, "check_cwd: no obj dirs.\n"); + 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], "|&;") == (size_t)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, "%s: WARNING: raw arg ends in shell meta '%s'\n", + progname, av[i]); + } + } else + next_cmd = 0; + + cp = av[i]; + if (*cp == ';' || *cp == '&' || *cp == '|') + is_cmd = 1; + + if (DEBUG(CWD)) + fprintf(debug_file, "av[%d] == %s '%s'", + i, (is_cmd) ? "cmd" : "arg", av[i]); + 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) { + if (DEBUG(CWD)) + fprintf(debug_file, " == cd, done.\n"); + return NULL; + } + for (mp = make; *mp != NULL; ++mp) { + n = strlen(*mp); + if (strcmp(cp, *mp) == 0) { + if (DEBUG(CWD)) + fprintf(debug_file, " %s == '%s', chdir(%s)\n", + cp, *mp, cur_dir); + return cur_dir; + } + } + } + if (DEBUG(CWD)) + fprintf(debug_file, "\n"); + } + 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); + if (DEBUG(CWD)) + fprintf(debug_file, "splitting: '%s' -> %d words\n", + cmd, ac); + } else { + ac = 0; + av = NULL; + bp = NULL; + } + cp = Check_Cwd_av(ac, av, 1); + if (bp) + free(bp); + if (av) + free(av); + 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 errnum 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 **errnum) +{ + 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; + + + *errnum = 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) { + *errnum = "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]); + + Var_ExportVars(); + + (void)execv(shellPath, UNCONST(args)); + _exit(1); + /*NOTREACHED*/ + + case -1: + *errnum = "Couldn't exec \"%s\""; + goto bad; + + default: + /* + * No need for the writing half + */ + (void)close(fds[1]); + + Buf_Init(&buf, 0); + + do { + char result[BUFSIZ]; + cc = read(fds[0], result, sizeof(result)); + if (cc > 0) + Buf_AddBytes(&buf, cc, 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 = waitpid(cpid, &status, 0)) != cpid) && (pid >= 0)) { + JobReapChild(pid, status, FALSE); + continue; + } + cc = Buf_Size(&buf); + res = Buf_Destroy(&buf, FALSE); + + if (cc == 0) + *errnum = "Couldn't read shell's output for \"%s\""; + + if (WIFSIGNALED(status)) + *errnum = "\"%s\" exited on a signal"; + else if (WEXITSTATUS(status) != 0) + *errnum = "\"%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 = bmake_malloc(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; + FILE *err_file; + + err_file = debug_file; + if (err_file == stdout) + err_file = stderr; + (void)fflush(stdout); + for (;;) { + va_start(ap, fmt); + fprintf(err_file, "%s: ", progname); + (void)vfprintf(err_file, fmt, ap); + va_end(ap); + (void)fprintf(err_file, "\n"); + (void)fflush(err_file); + if (err_file == stderr) + break; + err_file = 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(); + + (void)fflush(stdout); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + (void)fflush(stderr); + + PrintOnError(NULL, 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)fflush(stdout); + (void)fprintf(stderr, "%s: ", progname); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + (void)fflush(stderr); + + PrintOnError(NULL, 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"); +} + +/* + * 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(s) (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] \n\ + [-C directory] [-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(void *a, void *b) +{ + printf("%lx ", (unsigned long) a); + return b ? 0 : 0; +} + + + +void +PrintOnError(GNode *gn, const char *s) +{ + static GNode *en = NULL; + char tmp[64]; + char *cp; + + if (s) + printf("%s", s); + + printf("\n%s: stopped in %s\n", progname, curdir); + + if (en) + return; /* we've been here! */ + if (gn) { + /* + * We can print this even if there is no .ERROR target. + */ + Var_Set(".ERROR_TARGET", gn->name, VAR_GLOBAL, 0); + } + strncpy(tmp, "${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'\n@}", + sizeof(tmp) - 1); + cp = Var_Subst(NULL, tmp, VAR_GLOBAL, 0); + if (cp) { + if (*cp) + printf("%s", cp); + free(cp); + } + /* + * Finally, see if there is a .ERROR target, and run it if so. + */ + en = Targ_FindNode(".ERROR", TARG_NOCREATE); + if (en) { + en->type |= OP_SPECIAL; + Compat_Make(en, en); + } +} + +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 + } +} + +char * +getTmpdir(void) +{ + static char *tmpdir = NULL; + + if (!tmpdir) { + struct stat st; + + /* + * Honor $TMPDIR but only if it is valid. + * Ensure it ends with /. + */ + tmpdir = Var_Subst(NULL, "${TMPDIR:tA:U" _PATH_TMP "}/", VAR_GLOBAL, 0); + if (stat(tmpdir, &st) < 0 || !S_ISDIR(st.st_mode)) { + free(tmpdir); + tmpdir = bmake_strdup(_PATH_TMP); + } + } + return tmpdir; +} + +/* + * Create and open a temp file using "pattern". + * If "fnamep" is provided set it to a copy of the filename created. + * Otherwise unlink the file once open. + */ +int +mkTempFile(const char *pattern, char **fnamep) +{ + static char *tmpdir = NULL; + char tfile[MAXPATHLEN]; + int fd; + + if (!pattern) + pattern = TMPPAT; + if (!tmpdir) + tmpdir = getTmpdir(); + if (pattern[0] == '/') { + snprintf(tfile, sizeof(tfile), "%s", pattern); + } else { + snprintf(tfile, sizeof(tfile), "%s%s", tmpdir, pattern); + } + if ((fd = mkstemp(tfile)) < 0) + Punt("Could not create temporary file %s: %s", tfile, strerror(errno)); + if (fnamep) { + *fnamep = bmake_strdup(tfile); + } else { + unlink(tfile); /* we just want the descriptor */ + } + return fd; +} diff --git a/20120704/make-bootstrap.sh.in b/20120704/make-bootstrap.sh.in new file mode 100755 index 0000000..d9ff9ff --- /dev/null +++ b/20120704/make-bootstrap.sh.in @@ -0,0 +1,84 @@ +#!/bin/sh + +set -e + +srcdir=@srcdir@ + +DEFAULT_SYS_PATH="@default_sys_path@" + +case "@use_meta@" in +yes) XDEFS="-DUSE_META ${XDEFS}";; +esac + +CC="@CC@" +CFLAGS="@CFLAGS@ -I. -I${srcdir} @DEFS@ @CPPFLAGS@ -DMAKE_NATIVE ${XDEFS}" + +MAKE_VERSION=`sed -n '/^MAKE_VERSION=/s,.*=[^0-9]*,,p' Makefile` + +MDEFS="-DMAKE_VERSION=\"$MAKE_VERSION\" \ +-D@force_machine@MACHINE=\"@machine@\" -DMACHINE_ARCH=\"@machine_arch@\" \ +-D_PATH_DEFSYSPATH=\"${DEFAULT_SYS_PATH}\"" + + +LDFLAGS="@LDFLAGS@" +LIBS="@LIBS@" + +do_compile2() { + obj="$1"; shift + src="$1"; shift + echo ${CC} -c ${CFLAGS} "$@" -o "$obj" "$src" + ${CC} -c ${CFLAGS} "$@" -o "$obj" "$src" +} + +do_compile() { + obj="$1"; shift + src=`basename "$obj" .o`.c + + for d in "$srcdir" "$srcdir/lst.lib" + do + test -s "$d/$src" || continue + + do_compile2 "$obj" "$d/$src" "$@" || exit 1 + return + done + echo "Unknown object file '$obj'" >&2 + exit 1 +} + +do_link() { + output="$1"; shift + echo ${CC} ${LDSTATIC} ${LDFLAGS} -o "$output" "$@" ${LIBS} + ${CC} ${LDSTATIC} ${LDFLAGS} -o "$output" "$@" ${LIBS} +} + +BASE_OBJECTS="arch.o buf.o compat.o cond.o dir.o for.o getopt hash.o \ +job.o make.o make_malloc.o parse.o sigcompat.o str.o strlist.o \ +suff.o targ.o trace.o var.o util.o" + +LST_OBJECTS="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 lstPrev.o" + +LIB_OBJECTS="@LIBOBJS@" + +do_compile main.o ${MDEFS} + +for o in ${BASE_OBJECTS} ${LST_OBJECTS} ${LIB_OBJECTS} +do + do_compile "$o" +done + +case "@use_meta@" in +yes) + case "@filemon_h@" in + */filemon.h) FDEFS="-DHAVE_FILEMON_H -I`dirname @filemon_h@`";; + esac + do_compile meta.o ${FDEFS} + BASE_OBJECTS="meta.o ${BASE_OBJECTS}" + ;; +esac + +do_link bmake main.o ${BASE_OBJECTS} ${LST_OBJECTS} ${LIB_OBJECTS} diff --git a/20120704/make-conf.h b/20120704/make-conf.h new file mode 100644 index 0000000..a85b86d --- /dev/null +++ b/20120704/make-conf.h @@ -0,0 +1,162 @@ +/* $NetBSD: config.h,v 1.21 2012/03/31 00:12:24 christos 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 + */ + +/* + * 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<xx> 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 + +/* + * GMAKEEXPORT + * Recognize gmake like variable export directives [export <VAR>=<VALUE>] + */ +#define GMAKEEXPORT + +/* + * 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_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/20120704/make.1 b/20120704/make.1 new file mode 100644 index 0000000..86f747b --- /dev/null +++ b/20120704/make.1 @@ -0,0 +1,2061 @@ +.\" $NetBSD: make.1,v 1.204 2012/04/24 20:12:16 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 April 24, 2012 +.Dt MAKE 1 +.Os +.Sh NAME +.Nm make +.Nd maintain program dependencies +.Sh SYNOPSIS +.Nm +.Op Fl BeikNnqrstWX +.Op Fl C Ar directory +.Op Fl D Ar variable +.Op Fl d Ar flags +.Op Fl f Ar makefile +.Op Fl I Ar directory +.Op Fl J Ar private +.Op Fl j Ar max_jobs +.Op Fl m Ar directory +.Op Fl T Ar file +.Op Fl V Ar variable +.Op Ar variable=value +.Op Ar target ... +.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 no +.Fl f Ar makefile +makefile option is given, +.Nm +will try to open +.Ql Pa makefile +then +.Ql Pa Makefile +in order to find the specifications. +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 "PMake \- A Tutorial" . +.Pp +.Nm +will prepend the contents of the +.Va MAKEFLAGS +environment variable to the command line arguments before parsing them. +.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 C Ar directory +Change to +.Ar directory +before reading the makefiles or doing anything else. +If multiple +.Fl C +options are specified, each is interpreted relative to the previous one: +.Fl C Pa / Fl C Pa etc +is equivalent to +.Fl C Pa /etc . +.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. +Unless the flags are preceded by +.Ql \- +they are added to the +.Va MAKEFLAGS +environment variable and will be processed by any child make processes. +By default, debugging information is printed to standard error, +but this can be changed using the +.Ar F +debugging flag. +The debugging output is always unbuffered; in addition, if debugging +is enabled but debugging output is not directed to standard output, +then the standard output is line buffered. +.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 current working directory. +.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 Ns Oo Sy \&+ Oc Ns Ar filename +Specify where debugging output is written. +This must be the last flag, because it consumes the remainder of +the argument. +If the character immediately after the +.Ql F +flag is +.Ql \&+ , +then the file will be opened in append mode; +otherwise the file will be overwritten. +If the file name is +.Ql stdout +or +.Ql stderr +then debugging output will be written to the +standard output or standard error output file descriptors respectively +(and the +.Ql \&+ +option has no effect). +Otherwise, the output will be written to the named file. +If the file name ends +.Ql .%d +then the +.Ql %d +is replaced by the pid. +.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 l +Print commands in Makefiles regardless of whether or not they are prefixed by +.Ql @ +or other "quiet" flags. +Also known as "loud" behavior. +.It Ar M +Print debugging information about "meta" mode decisions about targets. +.It Ar m +Print debugging information about making targets, including modification +dates. +.It Ar n +Don't delete the temporary command scripts created when running commands. +These temporary scripts are created in the directory +referred to by the +.Ev TMPDIR +environment variable, or in +.Pa /tmp +if +.Ev TMPDIR +is unset or set to the empty string. +The temporary scripts are created by +.Xr mkstemp 3 , +and have names of the form +.Pa makeXXXXXX . +.Em NOTE : +This can create many files in +.Ev TMPDIR +or +.Pa /tmp , +so use with care. +.It Ar p +Print debugging information about makefile parsing. +.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. +The value is saved in +.Va .MAKE.JOBS . +Turns compatibility mode off, unless the +.Ar B +flag is also specified. +When compatibility mode is off, all commands associated with a +target are executed in a single shell invocation as opposed to the +traditional one shell invocation per line. +This can break traditional scripts which change directories on each +command invocation and then expect to start with a fresh environment +on the next line. +It is more efficient to correct the scripts rather than turn backwards +compatibility on. +.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 +.Dq 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. +.Em NOTE : +References to undefined variables are +.Em not +expanded. +This can cause problems when variable modifiers are used. +.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 +If the variable name contains a dollar, then the name itself is expanded first. +This allows almost arbitrary variable names, however names containing dollar, +braces, parenthesis, or whitespace are really best avoided! +.Pp +If the result of expanding a variable contains a dollar sign +.Pq Ql \&$ +the string is expanded again. +.Pp +Variable substitution occurs at three distinct times, depending on where +the variable is being used. +.Bl -enum +.It +Variables in dependency lines are expanded as the line is read. +.It +Variables in shell commands are expanded when the shell command is +executed. +.It +.Dq .for +loop index variables are expanded on each loop iteration. +Note that other variables are not expanded inside loops so +the following example code: +.Bd -literal -offset indent + +.Dv .for i in 1 2 3 +a+= ${i} +j= ${i} +b+= ${j} +.Dv .endfor + +all: + @echo ${a} + @echo ${b} + +.Ed +will print: +.Bd -literal -offset indent +1 2 3 +3 3 3 + +.Ed +Because while ${a} contains +.Dq 1 2 3 +after the loop is executed, ${b} +contains +.Dq ${j} ${j} ${j} +which expands to +.Dq 3 3 3 +since after the loop completes ${j} contains +.Dq 3 . +.El +.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 +In suffix-transformation rules, the name/path of the source from which the +target is to be transformed (the +.Dq implied +source); also known as +.Ql Va \&\*[Lt] . +It is not defined in explicit rules. +.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 target, 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 built-in 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 compatibility +.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 Va .MAKE.DEPENDFILE +Names the makefile (default +.Ql Pa .depend ) +from which generated dependencies are read. +.It Va .MAKE.EXPORTED +The list of variables exported by +.Nm . +.It Va .MAKE.JOBS +The argument to the +.Fl j +option. +.It Va .MAKE.JOB.PREFIX +If +.Nm +is run with +.Ar j +then output for each target is prefixed with a token +.Ql --- target --- +the first part of which can be controlled via +.Va .MAKE.JOB.PREFIX . +.br +For example: +.Li .MAKE.JOB.PREFIX=${.newline}---${.MAKE:T}[${.MAKE.PID}] +would produce tokens like +.Ql ---make[1234] target --- +making it easier to track the degree of parallelism being achieved. +.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 .MAKE.LEVEL +The recursion depth of +.Nm . +The initial instance of +.Nm +will be 0, and an incremented value is put into the environment +to be seen by the next generation. +This allows tests like: +.Li .if ${.MAKE.LEVEL} == 0 +to protect things which should only be evaluated in the initial instance of +.Nm . +.It Va .MAKE.MAKEFILE_PREFERENCE +The ordered list of makefile names +(default +.Ql Pa makefile , +.Ql Pa Makefile ) +that +.Nm +will look for. +.It Va .MAKE.MAKEFILES +The list of makefiles read by +.Nm , +which is useful for tracking dependencies. +Each makefile is recorded only once, regardless of the number of times read. +.It Va .MAKE.MODE +Processed after reading all makefiles. +Can affect the mode that +.Nm +runs in. +It can contain a number of keywords: +.Bl -hang -width ignore-cmd +.It Pa compat +Like +.Fl B , +puts +.Nm +into "compat" mode. +.It Pa meta +Puts +.Nm +into "meta" mode, where meta files are created for each target +to capture the command run, the output generated and if +.Xr filemon 4 +is available, the system calls which are of interest to +.Nm . +The captured output can be very useful when diagnosing errors. +.It Pa curdirOk= Ar bf +Normally +.Nm +will not create .meta files in +.Ql Va .CURDIR . +This can be overridden by setting +.Va bf +to a value which represents True. +.It Pa env +For debugging, it can be useful to inlcude the environment +in the .meta file. +.It Pa verbose +If in "meta" mode, print a clue about the target being built. +This is useful if the build is otherwise running silently. +The message printed the value of: +.Va .MAKE.META.PREFIX . +.It Pa ignore-cmd +Some makefiles have commands which are simply not stable. +This keyword causes them to be ignored for +determining whether a target is out of date in "meta" mode. +See also +.Ic .NOMETA_CMP . +.It Pa silent= Ar bf +If +.Va bf +is True, when a .meta file is created, mark the target +.Ic .SILENT . +.El +.It Va .MAKE.META.BAILIWICK +In "meta" mode, provides a list of prefixes which +match the directories controlled by +.Nm . +If a file that was generated outside of +.Va .OBJDIR +but within said bailiwick is missing, +the current target is considered out-of-date. +.It Va .MAKE.META.CREATED +In "meta" mode, this variable contains a list of all the meta files +updated. +If not empty, it can be used to trigger processing of +.Va .MAKE.META.FILES . +.It Va .MAKE.META.FILES +In "meta" mode, this variable contains a list of all the meta files +used (updated or not). +This list can be used to process the meta files to extract dependency +information. +.It Va .MAKE.META.PREFIX +Defines the message printed for each meta file updated in "meta verbose" mode. +The default value is: +.Dl Building ${.TARGET:H:tA}/${.TARGET:T} +.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.PID +The process-id of +.Nm . +.It Va .MAKE.PPID +The parent process-id of +.Nm . +.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:S,^/usr/src,/var/obj,} +may be used. +This is especially useful with +.Ql Ev MAKEOBJDIR . +.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. +If you want to retain their current values, assign them to a variable +using assignment with expansion: +.Pq Ql Cm \&:= . +.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. +.It Ev .TARGETS +The list of targets explicitly specified on the command line, if any. +.It Ev VPATH +Colon-separated +.Pq Dq \&: +lists of directories that +.Nm +will search for files. +The variable is supported for compatibility with old make programs only, +use +.Ql Va .PATH +instead. +.El +.Ss Variable modifiers +Variable expansion may be modified to select or modify each word of the +variable (where a +.Dq 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 . +.Pp +A set of modifiers can be specified via a variable, as follows: +.Pp +.Dl modifier_variable=modifier[:...] +.Dl ${variable:${modifier_variable}[:...]} +.Pp +In this case the first modifier in the modifier_variable does not +start with a colon, since that must appear in the referencing +variable. +If any of the modifiers in the modifier_variable contain a dollar sign +.Pq Ql $ , +these must be doubled to avoid early expansion. +.Pp +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 Oo Oc ) +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 \&:gmtime +The value is a format string for +.Xr strftime 3 , +using the current +.Xr gmtime 3 . +.It Cm \&:hash +Compute a 32bit hash of the value and encode it as hex digits. +.It Cm \&:localtime +The value is a format string for +.Xr strftime 3 , +using the current +.Xr localtime 3 . +.It Cm \&:tA +Attempt to convert variable to an absolute path using +.Xr realpath 3 , +if that fails, the value is unchanged. +.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. +The common escapes (including octal numeric codes), work as expected. +.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 No \&/ Ar new_string No \&/ Op Cm 1gW +.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 No \&/ Ar replacement No \&/ Op Cm 1gW +.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 name (not its value), when parsed as a .if conditional +expression, evaluates to true, return as its value the +.Ar true_string , +otherwise return the +.Ar false_string . +Since the variable name is used as the expression, \&:\&? must be the +first modifier after the variable name itself - which will, of course, +usually contain variable expansions. +A common error is trying to use expressions like +.Dl ${NUMBERS:M42:?match:no} +which actually tests defined(NUMBERS), +to determine is any words match "42" you need to use something like: +.Dl ${"${NUMBERS:M42}" != \&"\&":?match:no} . +.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 @ Ar string Cm @ +.Sm on +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.}@} +.Pp +However a single character varaiable is often more readable: +.Dl ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@} +.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. +In order for this modifier to work, the name (node) must at least have +appeared on the rhs of a dependency. +.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 set a variable when shell commands +are being parsed. +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. +.Pp +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 .error Ar message +The message is printed along with the name of the makefile and line number, +then +.Nm +will exit. +.It Ic .export Ar variable ... +Export the specified global variable. +If no variable list is provided, all globals are exported +except for internal variables (those that start with +.Ql \&. ) . +This is not affected by the +.Fl X +flag, so should be used with caution. +For compatibility with other +.Nm +programs +.Ql export variable=value +is also accepted. +.Pp +Appending a variable name to +.Va .MAKE.EXPORTED +is equivalent to exporting a variable. +.It Ic .export-env Ar variable ... +The same as +.Ql .export , +except that the variable is not appended to +.Va .MAKE.EXPORTED . +This allows exporting a value to the environment which is different from that +used by +.Nm +internally. +.It Ic .info Ar message +The message is printed along with the name of the makefile and line number. +.It Ic .undef Ar variable +Un-define the specified global variable. +Only global variables may be un-defined. +.It Ic .unexport Ar variable ... +The opposite of +.Ql .export . +The specified global +.Va variable +will be removed from +.Va .MAKE.EXPORTED . +If no variable list is provided, all globals are unexported, +and +.Va .MAKE.EXPORTED +deleted. +.It Ic .unexport-env +Unexport all globals previously exported and +clear the environment inherited from the parent. +This operation will cause a memory leak of the original environment, +so should be used sparingly. +Testing for +.Va .MAKE.LEVEL +being 0, would make sense. +Also note that any variables which originated in the parent environment +should be explicitly preserved if desired. +For example: +.Bd -literal -offset indent +.Li .if ${.MAKE.LEVEL} == 0 +PATH := ${PATH} +.Li .unexport-env +.Li .export PATH +.Li .endif +.Pp +.Ed +Would result in an environment containing only +.Ql Ev PATH , +which is the minimal useful environment. +Actually +.Ql Ev .MAKE.LEVEL +will also be pushed into the new environment. +.It Ic .warning Ar message +The message prefixed by +.Ql Pa warning: +is printed along with the name of the makefile and line number. +.It Ic \&.if Oo \&! Oc Ns Ar expression Op Ar operator expression ... +Test the value of an expression. +.It Ic .ifdef Oo \&! Oc Ns Ar variable Op Ar operator variable ... +Test the value of a variable. +.It Ic .ifndef Oo \&! Oc Ns Ar variable Op Ar operator variable ... +Test the value of a variable. +.It Ic .ifmake Oo \&! Oc Ns Ar target Op Ar operator target ... +Test the target being built. +.It Ic .ifnmake Oo \&! Ns Oc Ar target Op Ar operator target ... +Test the target being built. +.It Ic .else +Reverse the sense of the last conditional. +.It Ic .elif Oo \&! Ns Oc Ar expression Op Ar operator expression ... +A combination of +.Ql Ic .else +followed by +.Ql Ic .if . +.It Ic .elifdef Oo \&! Oc Ns Ar variable Op Ar operator variable ... +A combination of +.Ql Ic .else +followed by +.Ql Ic .ifdef . +.It Ic .elifndef Oo \&! Oc Ns Ar variable Op Ar operator variable ... +A combination of +.Ql Ic .else +followed by +.Ql Ic .ifndef . +.It Ic .elifmake Oo \&! Oc Ns Ar target Op Ar operator target ... +A combination of +.Ql Ic .else +followed by +.Ql Ic .ifmake . +.It Ic .elifnmake Oo \&! Oc Ns Ar target Op Ar operator target ... +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 expressions, and it encounters +a (white-space separated) word it doesn't recognize, either the +.Dq make +or +.Dq defined +expression is applied to it, depending on the form of the conditional. +If the form is +.Ql Ic .ifdef , +.Ql Ic .ifndef , +or +.Ql Ic .if +the +.Dq defined +expression is applied. +Similarly, if the form is +.Ql Ic .ifmake +or +.Ql Ic .ifnmake , the +.Dq 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 Ic \&.for Ar variable Oo Ar variable ... Oc Ic in Ar expression +.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 .META +Create a meta file for the target, even if it is flagged as +.Ic .PHONY , +.Ic .MAKE , +or +.Ic .SPECIAL . +Usage in conjunction with +.Ic .MAKE +is the most likely case. +In "meta" mode, the target is out-of-date if the meta file is missing. +.It Ic .NOMETA +Do not create a meta file for the target. +Meta files are also not created for +.Ic .PHONY , +.Ic .MAKE , +or +.Ic .SPECIAL +targets. +.It Ic .NOMETA_CMP +Ignore differences in commands when deciding if target is out of date. +This is useful if the command contains a value which always changes. +If the number of commands change, though, the target will still be out of date. +.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. +Suffix-transformation rules are not applied to +.Ic .PHONY +targets. +.It Ic .PRECIOUS +When +.Nm +is interrupted, it normally 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. +Since the dependents of files are not made until the file itself +could be made, this also stops the dependents being built unless they +are needed for another branch of the dependency tree. +So given: +.Bd -literal +x: a .WAIT b + echo x +a: + echo a +b: b1 + echo b +b1: + echo b1 + +.Ed +the output is always +.Ql a , +.Ql b1 , +.Ql b , +.Ql x . +.br +The ordering imposed by +.Ic .WAIT +is only relevant for parallel makes. +.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 .ERROR +Any command lines attached to this target are executed when another target fails. +The +.Ic .ERROR_TARGET +variable is set to the target that failed. +See also +.Ic MAKE_PRINT_VAR_ON_ERROR . +.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. +This ordering does not add targets to the list of targets to be made. +Since the dependents of a target do not get built until the target itself +could be built, unless +.Ql a +is built by another part of the dependency graph, +the following is a dependency loop: +.Bd -literal +\&.ORDER: b a +b: a +.Ed +.Pp +The ordering imposed by +.Ic .ORDER +is only relevant for parallel makes. +.\" 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. +.It Ar newline +The string literal to pass the shell that results in a single newline +character when used outside of any quoting characters. +.El +Example: +.Bd -literal +\&.SHELL: name=ksh path=/bin/ksh hasErrCtl=true \e + check="set \-e" ignore="set +e" \e + echo="set \-v" quiet="set +v" filter="set +v" \e + echoFlag=v errFlag=e newline="'\en'" +.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. +It allows the creation of suffix-transformation rules. +.Pp +Example: +.Bd -literal +\&.SUFFIXES: .o +\&.c.o: + cc \-o ${.TARGET} \-c ${.IMPSRC} +.Ed +.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 , +.Ev PWD , +and +.Ev TMPDIR . +.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 COMPATIBILITY +The basic make syntax is compatible between different versions of make, +however the special variables, variable modifiers and conditionals are not. +.Pp +The way that parallel makes are scheduled changed in +.Nx 4.0 +so that .ORDER and .WAIT apply recursively to the dependent nodes. +The algorithms used may change again in the future. +.Pp +The way that .for loop variables are substituted changed after +.Nx 5.0 +so that they still appear to be variable expansions. +In particular this stops them being treated as syntax, and removes some +obscure problems using them in .if statements. +.Pp +Unlike other +.Nm +programs, this implementation by default executes all commands for a given +target using a single shell invocation. +This is done for both efficiency and to simplify error handling in remote +command invocations. +Typically this is transparent to the user, unless the target commands change +the current working directory using +.Dq cd +or +.Dq chdir . +To be compatible with Makefiles that do this, one can use +.Fl B +to disable this behavior. +.Sh SEE ALSO +.Xr mkdep 1 +.Sh HISTORY +A +.Nm +command appeared in +.At v7 . +This +.Nm +implementation is based on Adam De Boor's pmake program which was written +for Sprint at Berkeley. +It was designed to be a parallel distributed make running jobs on different +machines using a daemon called +.Dq customs . +.Sh BUGS +The +.Nm +syntax is difficult to parse without actually acting of the data. +For instance finding the end of a variable use should involve scanning each +the modifiers using the correct terminator for each field. +In many places +.Nm +just counts {} and () in order to find the end of a variable expansion. +.Pp +There is no way of escaping a space character in a filename. diff --git a/20120704/make.c b/20120704/make.c new file mode 100644 index 0000000..4fa4ff9 --- /dev/null +++ b/20120704/make.c @@ -0,0 +1,1561 @@ +/* $NetBSD: make.c,v 1.87 2012/06/12 19:21:51 joerg 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.87 2012/06/12 19:21:51 joerg Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)make.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: make.c,v 1.87 2012/06/12 19:21:51 joerg 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 cmgn 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 cmgn 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 + */ + +#include "make.h" +#include "hash.h" +#include "dir.h" +#include "job.h" + +static unsigned int checked = 1;/* Sequence # to detect recursion */ +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 MakeAddChild(void *, void *); +static int MakeFindChild(void *, void *); +static int MakeUnmark(void *, void *); +static int MakeAddAllSrc(void *, void *); +static int MakeTimeStamp(void *, void *); +static int MakeHandleUse(void *, void *); +static Boolean MakeStartJobs(void); +static int MakePrintStatus(void *, void *); +static int MakeCheckOrder(void *, void *); +static int MakeBuildChild(void *, void *); +static int MakeBuildParent(void *, void *); + +MAKE_ATTR_DEAD static void +make_abort(GNode *gn, int line) +{ + static int two = 2; + + fprintf(debug_file, "make_abort from line %d\n", line); + Targ_PrintNode(gn, &two); + Lst_ForEach(toBeMade, Targ_PrintNode, &two); + Targ_PrintGraph(3); + abort(); +} + +/*- + *----------------------------------------------------------------------- + * Make_TimeStamp -- + * Set the cmgn 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 cmgn 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 (pgn->cmgn == NULL || cgn->mtime > pgn->cmgn->mtime) { + pgn->cmgn = cgn; + } + return (0); +} + +/* + * Input: + * pgn the current parent + * cgn the child we've just examined + * + */ +static int +MakeTimeStamp(void *pgn, void *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 cmgn 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, 1); + if (DEBUG(MAKE)) { + if (gn->mtime != 0) { + fprintf(debug_file, "modified %s...", Targ_FmtTime(gn->mtime)); + } else { + fprintf(debug_file, "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)) { + fprintf(debug_file, ".USE node..."); + } + oodate = FALSE; + } else if ((gn->type & OP_LIB) && + ((gn->mtime==0) || Arch_IsLib(gn))) { + if (DEBUG(MAKE)) { + fprintf(debug_file, "library..."); + } + + /* + * always out of date if no children and :: target + * or non-existent. + */ + oodate = (gn->mtime == 0 || Arch_LibOODate(gn) || + (gn->cmgn == NULL && (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)) { + fprintf(debug_file, ".JOIN node..."); + } + if (DEBUG(MAKE)) { + fprintf(debug_file, "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) { + fprintf(debug_file, "! operator..."); + } else if (gn->type & OP_PHONY) { + fprintf(debug_file, ".PHONY node..."); + } else { + fprintf(debug_file, ".EXEC node..."); + } + } + oodate = TRUE; + } else if ((gn->cmgn != NULL && gn->mtime < gn->cmgn->mtime) || + (gn->cmgn == NULL && + ((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 (cmgn == NULL) 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->cmgn != NULL && gn->mtime < gn->cmgn->mtime) { + fprintf(debug_file, "modified before source %s...", + gn->cmgn->path); + } else if (gn->mtime == 0) { + fprintf(debug_file, "non-existent and no sources..."); + } else { + fprintf(debug_file, ":: 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) + fprintf(debug_file, "non existing child..."); + } + oodate = (gn->flags & FORCE) ? TRUE : FALSE; + } + +#ifdef USE_META + if (useMeta) { + oodate = meta_oodate(gn, oodate); + } +#endif + + /* + * 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, 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(void *gnp, void *lp) +{ + GNode *gn = (GNode *)gnp; + Lst l = (Lst) lp; + + if ((gn->flags & REMAKE) == 0 && !(gn->type & (OP_USE|OP_USEBEFORE))) { + if (DEBUG(MAKE)) + fprintf(debug_file, "MakeAddChild: need to examine %s%s\n", + gn->name, gn->cohort_num); + (void)Lst_EnQueue(l, 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 cmgn of the parent are + * updated; the unmade children count of the parent is decremented. + *----------------------------------------------------------------------- + */ +static int +MakeFindChild(void *gnp, void *pgnp) +{ + GNode *gn = (GNode *)gnp; + GNode *pgn = (GNode *)pgnp; + + (void)Dir_MTime(gn, 0); + 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) { + fprintf(debug_file, "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, NULL); + (void)Lst_Concat(pgn->commands, cmds, LST_CONCNEW); + Lst_Destroy(cmds, NULL); + } 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)) != NULL) { + 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 != NULL) + 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(void *cgnp, void *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, cgn)) != NULL) { + 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, 1); + +#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 && !(gn->type & OP_WAIT))) { + if (DEBUG(MAKE)) { + fprintf(debug_file, " recheck(%s): update time from %s to now\n", + gn->name, Targ_FmtTime(gn->mtime)); + } + gn->mtime = now; + } + else { + if (DEBUG(MAKE)) { + fprintf(debug_file, " 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. + * + * 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 cmgn 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; + + /* It is save to re-examine any nodes again */ + checked++; + + cname = Var_Value(TARGET, cgn, &p1); + if (p1) + free(p1); + + if (DEBUG(MAKE)) + fprintf(debug_file, "Make_Update: %s%s\n", cgn->name, cgn->cohort_num); + + /* + * 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%s: cohort has parents", cgn->name, cgn->cohort_num); + centurion->unmade_cohorts -= 1; + if (centurion->unmade_cohorts < 0) + Error("Graph cycles through centurion %s", centurion->name); + } else { + centurion = cgn; + } + parents = centurion->parents; + + /* If this was a .ORDER node, schedule the RHS */ + Lst_ForEach(centurion->order_succ, MakeBuildParent, Lst_First(toBeMade)); + + /* Now mark all the parents as having one less unmade child */ + if (Lst_Open(parents) == SUCCESS) { + while ((ln = Lst_Next(parents)) != NULL) { + pgn = (GNode *)Lst_Datum(ln); + if (DEBUG(MAKE)) + fprintf(debug_file, "inspect parent %s%s: flags %x, " + "type %x, made %d, unmade %d ", + pgn->name, pgn->cohort_num, pgn->flags, + pgn->type, pgn->made, pgn->unmade-1); + + if (!(pgn->flags & REMAKE)) { + /* This parent isn't needed */ + if (DEBUG(MAKE)) + fprintf(debug_file, "- not needed\n"); + continue; + } + if (mtime == 0 && !(cgn->type & OP_WAIT)) + pgn->flags |= FORCE; + + /* + * If the parent has the .MADE attribute, its timestamp got + * updated to that of its newest child, and its unmake + * child count got set to zero in Make_ExpandUse(). + * However other things might cause us to build one of its + * children - and so we mustn't do any processing here when + * the child build finishes. + */ + if (pgn->type & OP_MADE) { + if (DEBUG(MAKE)) + fprintf(debug_file, "- .MADE\n"); + 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 < MADE) { + if (DEBUG(MAKE)) + fprintf(debug_file, + "- centurion made %d, %d unmade cohorts\n", + centurion->made, centurion->unmade_cohorts); + continue; + } + + /* One more child of this parent is now made */ + pgn->unmade -= 1; + if (pgn->unmade < 0) { + if (DEBUG(MAKE)) { + fprintf(debug_file, "Graph cycles through %s%s\n", + pgn->name, pgn->cohort_num); + Targ_PrintGraph(2); + } + Error("Graph cycles through %s%s", pgn->name, pgn->cohort_num); + } + + /* We must always rescan the parents of .WAIT and .ORDER nodes. */ + if (pgn->unmade != 0 && !(centurion->type & OP_WAIT) + && !(centurion->flags & DONE_ORDER)) { + if (DEBUG(MAKE)) + fprintf(debug_file, "- unmade children\n"); + continue; + } + if (pgn->made != DEFERRED) { + /* + * Either this parent is on a different branch of the tree, + * or it on the RHS of a .WAIT directive + * or it is already on the toBeMade list. + */ + if (DEBUG(MAKE)) + fprintf(debug_file, "- not deferred\n"); + continue; + } + if (pgn->order_pred + && Lst_ForEach(pgn->order_pred, MakeCheckOrder, 0)) { + /* A .ORDER rule stops us building this */ + continue; + } + if (DEBUG(MAKE)) { + static int two = 2; + fprintf(debug_file, "- %s%s made, schedule %s%s (made %d)\n", + cgn->name, cgn->cohort_num, + pgn->name, pgn->cohort_num, pgn->made); + Targ_PrintNode(pgn, &two); + } + /* Ok, we can schedule the parent again */ + pgn->made = REQUESTED; + (void)Lst_EnQueue(toBeMade, pgn); + } + Lst_Close(parents); + } + + /* + * 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)) != NULL) { + 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(void *cgnp, void *pgnp MAKE_ATTR_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(void *cgnp, void *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) +{ + if (gn->flags & DONE_ALLSRC) + return; + + Lst_ForEach(gn->children, MakeUnmark, gn); + Lst_ForEach(gn->children, MakeAddAllSrc, 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); + } + gn->flags |= DONE_ALLSRC; +} + +/*- + *----------------------------------------------------------------------- + * 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 int +MakeCheckOrder(void *v_bn, void *ignore MAKE_ATTR_UNUSED) +{ + GNode *bn = v_bn; + + if (bn->made >= MADE || !(bn->flags & REMAKE)) + return 0; + if (DEBUG(MAKE)) + fprintf(debug_file, "MakeCheckOrder: Waiting for .ORDER node %s%s\n", + bn->name, bn->cohort_num); + return 1; +} + +static int +MakeBuildChild(void *v_cn, void *toBeMade_next) +{ + GNode *cn = v_cn; + + if (DEBUG(MAKE)) + fprintf(debug_file, "MakeBuildChild: inspect %s%s, made %d, type %x\n", + cn->name, cn->cohort_num, cn->made, cn->type); + if (cn->made > DEFERRED) + return 0; + + /* If this node is on the RHS of a .ORDER, check LHSs. */ + if (cn->order_pred && Lst_ForEach(cn->order_pred, MakeCheckOrder, 0)) { + /* Can't build this (or anything else in this child list) yet */ + cn->made = DEFERRED; + return 1; + } + + if (DEBUG(MAKE)) + fprintf(debug_file, "MakeBuildChild: schedule %s%s\n", + cn->name, cn->cohort_num); + + cn->made = REQUESTED; + if (toBeMade_next == NULL) + Lst_AtEnd(toBeMade, cn); + else + Lst_InsertBefore(toBeMade, toBeMade_next, cn); + + if (cn->unmade_cohorts != 0) + Lst_ForEach(cn->cohorts, MakeBuildChild, toBeMade_next); + + /* + * If this node is a .WAIT node with unmade chlidren + * then don't add the next sibling. + */ + return cn->type & OP_WAIT && cn->unmade > 0; +} + +/* When a .ORDER RHS node completes we do this on each LHS */ +static int +MakeBuildParent(void *v_pn, void *toBeMade_next) +{ + GNode *pn = v_pn; + + if (pn->made != DEFERRED) + return 0; + + if (MakeBuildChild(pn, toBeMade_next) == 0) { + /* Mark so that when this node is built we reschedule its parents */ + pn->flags |= DONE_ORDER; + } + + return 0; +} + +static Boolean +MakeStartJobs(void) +{ + GNode *gn; + int have_token = 0; + + while (!Lst_IsEmpty (toBeMade)) { + /* Get token now to avoid cycling job-list when we only have 1 token */ + if (!have_token && !Job_TokenWithdraw()) + break; + have_token = 1; + + gn = (GNode *)Lst_DeQueue(toBeMade); + if (DEBUG(MAKE)) + fprintf(debug_file, "Examining %s%s...\n", + gn->name, gn->cohort_num); + + if (gn->made != REQUESTED) { + if (DEBUG(MAKE)) + fprintf(debug_file, "state %d\n", gn->made); + + make_abort(gn, __LINE__); + } + + if (gn->checked == checked) { + /* We've already looked at this node since a job finished... */ + if (DEBUG(MAKE)) + fprintf(debug_file, "already checked %s%s\n", + gn->name, gn->cohort_num); + gn->made = DEFERRED; + continue; + } + gn->checked = checked; + + if (gn->unmade != 0) { + /* + * We can't build this yet, add all unmade children to toBeMade, + * just before the current first element. + */ + gn->made = DEFERRED; + Lst_ForEach(gn->children, MakeBuildChild, Lst_First(toBeMade)); + /* and drop this node on the floor */ + if (DEBUG(MAKE)) + fprintf(debug_file, "dropped %s%s\n", gn->name, gn->cohort_num); + continue; + } + + gn->made = BEINGMADE; + if (Make_OODate(gn)) { + if (DEBUG(MAKE)) { + fprintf(debug_file, "out-of-date\n"); + } + if (queryFlag) { + return (TRUE); + } + Make_DoAllVar(gn); + Job_Make(gn); + have_token = 0; + } else { + if (DEBUG(MAKE)) { + fprintf(debug_file, "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); + } + Make_Update(gn); + } + } + + if (have_token) + Job_TokenReturn(); + + 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 +MakePrintStatusOrder(void *ognp, void *gnp) +{ + GNode *ogn = ognp; + GNode *gn = gnp; + + if (!(ogn->flags & REMAKE) || ogn->made > REQUESTED) + /* not waiting for this one */ + return 0; + + printf(" `%s%s' has .ORDER dependency against %s%s " + "(made %d, flags %x, type %x)\n", + gn->name, gn->cohort_num, + ogn->name, ogn->cohort_num, ogn->made, ogn->flags, ogn->type); + if (DEBUG(MAKE) && debug_file != stdout) + fprintf(debug_file, " `%s%s' has .ORDER dependency against %s%s " + "(made %d, flags %x, type %x)\n", + gn->name, gn->cohort_num, + ogn->name, ogn->cohort_num, ogn->made, ogn->flags, ogn->type); + return 0; +} + +static int +MakePrintStatus(void *gnp, void *v_errors) +{ + GNode *gn = (GNode *)gnp; + int *errors = v_errors; + + if (gn->flags & DONECYCLE) + /* We've completely processed this node before, don't do it again. */ + return 0; + + if (gn->unmade == 0) { + gn->flags |= DONECYCLE; + switch (gn->made) { + case UPTODATE: + printf("`%s%s' is up to date.\n", gn->name, gn->cohort_num); + break; + case MADE: + break; + case UNMADE: + case DEFERRED: + case REQUESTED: + case BEINGMADE: + (*errors)++; + printf("`%s%s' was not built (made %d, flags %x, type %x)!\n", + gn->name, gn->cohort_num, gn->made, gn->flags, gn->type); + if (DEBUG(MAKE) && debug_file != stdout) + fprintf(debug_file, + "`%s%s' was not built (made %d, flags %x, type %x)!\n", + gn->name, gn->cohort_num, gn->made, gn->flags, gn->type); + /* Most likely problem is actually caused by .ORDER */ + Lst_ForEach(gn->order_pred, MakePrintStatusOrder, gn); + break; + default: + /* Errors - already counted */ + printf("`%s%s' not remade because of errors.\n", + gn->name, gn->cohort_num); + if (DEBUG(MAKE) && debug_file != stdout) + fprintf(debug_file, "`%s%s' not remade because of errors.\n", + gn->name, gn->cohort_num); + break; + } + return 0; + } + + if (DEBUG(MAKE)) + fprintf(debug_file, "MakePrintStatus: %s%s has %d unmade children\n", + gn->name, gn->cohort_num, gn->unmade); + /* + * If printing cycles and came to one that has unmade children, + * print out the cycle by recursing on its children. + */ + if (!(gn->flags & CYCLE)) { + /* Fist time we've seen this node, check all children */ + gn->flags |= CYCLE; + Lst_ForEach(gn->children, MakePrintStatus, errors); + /* Mark that this node needn't be processed again */ + gn->flags |= DONECYCLE; + return 0; + } + + /* Only output the error once per node */ + gn->flags |= DONECYCLE; + Error("Graph cycles through `%s%s'", gn->name, gn->cohort_num); + if ((*errors)++ > 100) + /* Abandon the whole error report */ + return 1; + + /* Reporting for our children will give the rest of the loop */ + Lst_ForEach(gn->children, MakePrintStatus, errors); + return 0; +} + + +/*- + *----------------------------------------------------------------------- + * Make_ExpandUse -- + * Expand .USE nodes and create a new targets list + * + * Input: + * targs the initial list of targets + * + * Side Effects: + *----------------------------------------------------------------------- + */ +void +Make_ExpandUse(Lst targs) +{ + GNode *gn; /* a temporary pointer */ + Lst examine; /* List of targets to examine */ + + examine = Lst_Duplicate(targs, NULL); + + /* + * 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->flags & REMAKE) + /* We've looked at this one already */ + continue; + gn->flags |= REMAKE; + if (DEBUG(MAKE)) + fprintf(debug_file, "Make_ExpandUse: examine %s%s\n", + gn->name, gn->cohort_num); + + if ((gn->type & OP_DOUBLEDEP) && !Lst_IsEmpty (gn->cohorts)) { + /* Append all the 'cohorts' to the list of things to examine */ + Lst new; + new = Lst_Duplicate(gn->cohorts, NULL); + Lst_Concat(new, examine, LST_CONCLINK); + examine = new; + } + + /* + * 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, 0); + Var_Set(TARGET, gn->path ? gn->path : gn->name, gn, 0); + Lst_ForEach(gn->children, MakeUnmark, gn); + Lst_ForEach(gn->children, MakeHandleUse, gn); + + if ((gn->type & OP_MADE) == 0) + Suff_FindDeps(gn); + else { + /* Pretend we made all this node's children */ + Lst_ForEach(gn->children, MakeFindChild, gn); + if (gn->unmade != 0) + printf("Warning: %s%s still has %d unmade children\n", + gn->name, gn->cohort_num, gn->unmade); + } + + if (gn->unmade != 0) + Lst_ForEach(gn->children, MakeAddChild, examine); + } + + Lst_Destroy(examine, NULL); +} + +/*- + *----------------------------------------------------------------------- + * Make_ProcessWait -- + * Convert .WAIT nodes into dependencies + * + * Input: + * targs the initial list of targets + * + *----------------------------------------------------------------------- + */ + +static int +link_parent(void *cnp, void *pnp) +{ + GNode *cn = cnp; + GNode *pn = pnp; + + Lst_AtEnd(pn->children, cn); + Lst_AtEnd(cn->parents, pn); + pn->unmade++; + return 0; +} + +static int +add_wait_dep(void *v_cn, void *v_wn) +{ + GNode *cn = v_cn; + GNode *wn = v_wn; + + if (cn == wn) + return 1; + + if (cn == NULL || wn == NULL) { + printf("bad wait dep %p %p\n", cn, wn); + exit(4); + } + if (DEBUG(MAKE)) + fprintf(debug_file, ".WAIT: add dependency %s%s -> %s\n", + cn->name, cn->cohort_num, wn->name); + + Lst_AtEnd(wn->children, cn); + wn->unmade++; + Lst_AtEnd(cn->parents, wn); + return 0; +} + +static void +Make_ProcessWait(Lst targs) +{ + GNode *pgn; /* 'parent' node we are examining */ + GNode *cgn; /* Each child in turn */ + LstNode owln; /* Previous .WAIT node */ + Lst examine; /* List of targets to examine */ + LstNode ln; + + /* + * We need all the nodes to have a common parent in order for the + * .WAIT and .ORDER scheduling to work. + * Perhaps this should be done earlier... + */ + + pgn = Targ_NewGN(".MAIN"); + pgn->flags = REMAKE; + pgn->type = OP_PHONY | OP_DEPENDS; + /* Get it displayed in the diag dumps */ + Lst_AtFront(Targ_List(), pgn); + + Lst_ForEach(targs, link_parent, pgn); + + /* Start building with the 'dummy' .MAIN' node */ + MakeBuildChild(pgn, NULL); + + examine = Lst_Init(FALSE); + Lst_AtEnd(examine, pgn); + + while (!Lst_IsEmpty (examine)) { + pgn = Lst_DeQueue(examine); + + /* We only want to process each child-list once */ + if (pgn->flags & DONE_WAIT) + continue; + pgn->flags |= DONE_WAIT; + if (DEBUG(MAKE)) + fprintf(debug_file, "Make_ProcessWait: examine %s\n", pgn->name); + + if ((pgn->type & OP_DOUBLEDEP) && !Lst_IsEmpty (pgn->cohorts)) { + /* Append all the 'cohorts' to the list of things to examine */ + Lst new; + new = Lst_Duplicate(pgn->cohorts, NULL); + Lst_Concat(new, examine, LST_CONCLINK); + examine = new; + } + + owln = Lst_First(pgn->children); + Lst_Open(pgn->children); + for (; (ln = Lst_Next(pgn->children)) != NULL; ) { + cgn = Lst_Datum(ln); + if (cgn->type & OP_WAIT) { + /* Make the .WAIT node depend on the previous children */ + Lst_ForEachFrom(pgn->children, owln, add_wait_dep, cgn); + owln = ln; + } else { + Lst_AtEnd(examine, cgn); + } + } + Lst_Close(pgn->children); + } + + Lst_Destroy(examine, NULL); +} + +/*- + *----------------------------------------------------------------------- + * 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 */ + + /* Start trying to make the current targets... */ + toBeMade = Lst_Init(FALSE); + + Make_ExpandUse(targs); + Make_ProcessWait(targs); + + if (DEBUG(MAKE)) { + fprintf(debug_file, "#***# full graph\n"); + Targ_PrintGraph(1); + } + + 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()); + } + /* + * 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) || jobTokensRunning > 0) { + Job_CatchOutput(); + (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. + */ + if (DEBUG(MAKE)) + fprintf(debug_file, "done: errors %d\n", errors); + if (errors == 0) { + Lst_ForEach(targs, MakePrintStatus, &errors); + if (DEBUG(MAKE)) { + fprintf(debug_file, "done: errors %d\n", errors); + if (errors) + Targ_PrintGraph(4); + } + } + return errors != 0; +} diff --git a/20120704/make.h b/20120704/make.h new file mode 100644 index 0000000..384d109 --- /dev/null +++ b/20120704/make.h @@ -0,0 +1,518 @@ +/* $NetBSD: make.h,v 1.89 2012/06/12 19:21:51 joerg 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 <sys/types.h> +#include <sys/param.h> + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#ifdef HAVE_STRING_H +#include <string.h> +#else +#include <strings.h> +#endif +#include <unistd.h> +#include <sys/cdefs.h> + +#if defined(__GNUC__) +#define MAKE_GNUC_PREREQ(x, y) \ + ((__GNUC__ == (x) && __GNUC_MINOR__ >= (y)) || \ + (__GNUC__ > (x))) +#else /* defined(__GNUC__) */ +#define MAKE_GNUC_PREREQx, y) 0 +#endif /* defined(__GNUC__) */ + +#if MAKE_GNUC_PREREQ(2, 7) +#define MAKE_ATTR_UNUSED __attribute__((__unused__)) +#else +#define MAKE_ATTR_UNUSED /* delete */ +#endif + +#if MAKE_GNUC_PREREQ(2, 5) +#define MAKE_ATTR_DEAD __attribute__((__noreturn__)) +#elif defined(__GNUC__) +#define MAKE_ATTR_DEAD __volatile +#else +#define MAKE_ATTR_DEAD /* delete */ +#endif + +#if MAKE_GNUC_PREREQ(2, 7) +#define MAKE_ATTR_PRINTFLIKE(fmtarg, firstvararg) \ + __attribute__((__format__ (__printf__, fmtarg, firstvararg))) +#else +#define MAKE_ATTR_PRINTFLIKE(fmtarg, firstvararg) /* delete */ +#endif + +#include "sprite.h" +#include "lst.h" +#include "hash.h" +#include "make-conf.h" +#include "buf.h" +#include "make_malloc.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 (parents) + * 11) a list of nodes on which this depends (children) + * 12) a list of nodes that depend on this, as gleaned from the + * transformation rules (iParents) + * 13) a list of ancestor nodes, which includes parents, iParents, + * and recursive parents of parents + * 14) a list of nodes of the same name created by the :: operator + * 15) a list of nodes that must be made (if they're made) before + * this node can be, but that do not enter into the datedness of + * this node. + * 16) a list of nodes that must be made (if they're made) before + * this node or any child of this node can be, but that do not + * enter into the datedness of this node. + * 17) 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. + * 18) a Lst of ``local'' variables that are specific to this target + * and this target only (qv. var.c [$@ $< $?, etc.]) + * 19) 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 flags; +#define REMAKE 0x1 /* this target needs to be (re)made */ +#define CHILDMADE 0x2 /* children of this target were made */ +#define FORCE 0x4 /* children don't exist, and we pretend made */ +#define DONE_WAIT 0x8 /* Set by Make_ProcessWait() */ +#define DONE_ORDER 0x10 /* Build requested by .ORDER processing */ +#define FROM_DEPEND 0x20 /* Node created from .depend */ +#define DONE_ALLSRC 0x40 /* We do it once only */ +#define CYCLE 0x1000 /* Used by MakePrintStatus */ +#define DONECYCLE 0x2000 /* Used by MakePrintStatus */ + enum enum_made { + UNMADE, DEFERRED, REQUESTED, BEINGMADE, + MADE, UPTODATE, ERROR, ABORTED + } made; /* Set to reflect the state of processing + * on this node: + * UNMADE - Not examined yet + * DEFERRED - Examined once (building child) + * REQUESTED - on toBeMade list + * BEINGMADE - Target is already being made. + * Indicates a cycle in the graph. + * 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). + */ + int unmade; /* The number of unmade children */ + + time_t mtime; /* Its modification time */ + struct GNode *cmgn; /* The 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 order_pred; /* .ORDER nodes we need made */ + Lst order_succ; /* .ORDER nodes who need us */ + + char cohort_num[8]; /* #n for this cohort */ + 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 */ + unsigned int checked; /* Last time we tried to makle this node */ + + 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) */ + const char *fname; /* filename where the GNode got defined */ + int lineno; /* line number where the GNode got defined */ +} GNode; + +/* + * 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 */ +#define OP_WAIT 0x00040000 /* .WAIT phony node */ +#define OP_NOMETA 0x00080000 /* .NOMETA do not create a .meta file */ +#define OP_META 0x00100000 /* .META we _do_ want a .meta file */ +#define OP_NOMETA_CMP 0x00200000 /* Do not compare commands in .meta file */ +/* 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 NULL pointer will be returned. + */ +#define TARG_NOCREATE 0x00 /* don't create it */ +#define TARG_CREATE 0x01 /* create node if not found */ +#define TARG_NOHASH 0x02 /* don't look in/add to hash table */ + +/* + * 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 "<F" /* file part of IMPSRC */ +#define DIMPSRC "<D" /* directory part of IMPSRC */ +#define FPREFIX "*F" /* file part of PREFIX */ +#define DPREFIX "*D" /* directory part of PREFIX */ + +/* + * Global Variables + */ +extern Lst create; /* The list of target names specified on the + * command line. used to resolve #if + * make(...) statements */ +extern Lst dirSearchPath; /* The list of directories to search when + * looking for targets */ + +extern Boolean compatMake; /* True if we are make compatible */ +extern Boolean ignoreErrors; /* True if should ignore all errors */ +extern Boolean beSilent; /* True if should print no commands */ +extern Boolean noExecute; /* True if should execute nothing */ +extern Boolean noRecursiveExecute; /* True if should execute nothing */ +extern Boolean allPrecious; /* True if every target is precious */ +extern Boolean keepgoing; /* True if should continue on unaffected + * portions of the graph when have an error + * in one portion */ +extern Boolean touchFlag; /* TRUE if targets should just be 'touched' + * if out of date. Set by the -t flag */ +extern Boolean queryFlag; /* TRUE if we aren't supposed to really make + * anything, just see if the targets are out- + * of-date */ +extern Boolean doing_depend; /* TRUE if processing .depend */ + +extern Boolean checkEnvFirst; /* TRUE if environment should be searched for + * variables before the global context */ +extern Boolean jobServer; /* a jobServer already exists */ + +extern Boolean parseWarnFatal; /* TRUE if makefile parsing warnings are + * treated as errors */ + +extern Boolean varNoExportEnv; /* TRUE if we should not export variables + * set on the command line to the env. */ + +extern GNode *DEFAULT; /* .DEFAULT rule */ + +extern GNode *VAR_GLOBAL; /* Variables defined in a global context, e.g + * in the Makefile itself */ +extern GNode *VAR_CMD; /* Variables defined on the command line */ +extern GNode *VAR_FOR; /* Iteration variables */ +extern char var_Error[]; /* Value returned by Var_Parse when an error + * is encountered. It actually points to + * an empty string, so naive callers needn't + * worry about it. */ + +extern time_t now; /* The time at the start of this whole + * process */ + +extern Boolean oldVars; /* Do old-style variable substitution */ + +extern Lst sysIncPath; /* The system include path. */ +extern Lst defIncPath; /* The default include path. */ + +extern char curdir[]; /* Startup directory */ +extern char *progname; /* The program name */ +extern char *makeDependfile; /* .depend */ +extern char **savedEnv; /* if we replaced environ this will be non-NULL */ + +/* + * We cannot vfork() in a child of vfork(). + * Most systems do not enforce this but some do. + */ +#define vFork() ((getpid() == myPid) ? vfork() : fork()) +extern pid_t myPid; + +#define MAKEFLAGS ".MAKEFLAGS" +#define MAKEOVERRIDES ".MAKEOVERRIDES" +#define MAKE_JOB_PREFIX ".MAKE.JOB.PREFIX" /* prefix for job target output */ +#define MAKE_EXPORTED ".MAKE.EXPORTED" /* variables we export */ +#define MAKE_MAKEFILES ".MAKE.MAKEFILES" /* all the makefiles we read */ +#define MAKE_LEVEL ".MAKE.LEVEL" /* recursion level */ +#define MAKEFILE_PREFERENCE ".MAKE.MAKEFILE_PREFERENCE" +#define MAKE_DEPENDFILE ".MAKE.DEPENDFILE" /* .depend */ +#define MAKE_MODE ".MAKE.MODE" + +#ifdef NEED_MAKE_LEVEL_SAFE +# define MAKE_LEVEL_SAFE "_MAKE_LEVEL" /* some shells will not pass .MAKE. */ +#endif + +/* + * debug control: + * There is one bit per module. It is up to the module what debug + * information to print. + */ +FILE *debug_file; /* Output written here - default stdout */ +extern int debug; +#define DEBUG_ARCH 0x00001 +#define DEBUG_COND 0x00002 +#define DEBUG_DIR 0x00004 +#define DEBUG_GRAPH1 0x00008 +#define DEBUG_GRAPH2 0x00010 +#define DEBUG_JOB 0x00020 +#define DEBUG_MAKE 0x00040 +#define DEBUG_SUFF 0x00080 +#define DEBUG_TARG 0x00100 +#define DEBUG_VAR 0x00200 +#define DEBUG_FOR 0x00400 +#define DEBUG_SHELL 0x00800 +#define DEBUG_ERROR 0x01000 +#define DEBUG_LOUD 0x02000 +#define DEBUG_META 0x04000 + +#define DEBUG_GRAPH3 0x10000 +#define DEBUG_SCRIPT 0x20000 +#define DEBUG_PARSE 0x40000 +#define DEBUG_CWD 0x80000 + +#define CONCAT(a,b) a##b + +#define DEBUG(module) (debug & CONCAT(DEBUG_,module)) + +#include "nonints.h" + +int Make_TimeStamp(GNode *, GNode *); +Boolean Make_OODate(GNode *); +void Make_ExpandUse(Lst); +time_t Make_Recheck(GNode *); +void Make_HandleUse(GNode *, GNode *); +void Make_Update(GNode *); +void Make_DoAllVar(GNode *); +Boolean Make_Run(Lst); +char * Check_Cwd_Cmd(const char *); +void Check_Cwd(const char **); +void PrintOnError(GNode *, const char *); +void Main_ExportMAKEFLAGS(Boolean); +Boolean Main_SetObjdir(const char *); +int mkTempFile(const char *, char **); +int str2Lst_Append(Lst, char *, const char *); + +#ifdef __GNUC__ +#define UNCONST(ptr) ({ \ + union __unconst { \ + const void *__cp; \ + void *__p; \ + } __d; \ + __d.__cp = ptr, __d.__p; }) +#else +#define UNCONST(ptr) (void *)(ptr) +#endif + +#ifndef MIN +#define MIN(a, b) ((a < b) ? a : b) +#endif +#ifndef MAX +#define MAX(a, b) ((a > b) ? a : b) +#endif + +#endif /* _MAKE_H_ */ diff --git a/20120704/make_malloc.c b/20120704/make_malloc.c new file mode 100644 index 0000000..b8ac23f --- /dev/null +++ b/20120704/make_malloc.c @@ -0,0 +1,119 @@ +/* $NetBSD: make_malloc.c,v 1.10 2012/06/20 17:46:28 sjg Exp $ */ + +/*- + * Copyright (c) 2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 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. + */ + +#ifdef MAKE_NATIVE +#include <sys/cdefs.h> +__RCSID("$NetBSD: make_malloc.c,v 1.10 2012/06/20 17:46:28 sjg Exp $"); +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include "make.h" + +#ifndef USE_EMALLOC +static void enomem(void) MAKE_ATTR_DEAD; + +/* + * enomem -- + * die when out of memory. + */ +static void +enomem(void) +{ + (void)fprintf(stderr, "%s: %s.\n", progname, strerror(ENOMEM)); + exit(2); +} + +/* + * bmake_malloc -- + * malloc, but die on error. + */ +void * +bmake_malloc(size_t len) +{ + void *p; + + if ((p = malloc(len)) == NULL) + enomem(); + return(p); +} + +/* + * bmake_strdup -- + * strdup, but die on error. + */ +char * +bmake_strdup(const char *str) +{ + size_t len; + char *p; + + len = strlen(str) + 1; + if ((p = malloc(len)) == NULL) + enomem(); + return memcpy(p, str, len); +} + +/* + * bmake_strndup -- + * strndup, but die on error. + */ +char * +bmake_strndup(const char *str, size_t max_len) +{ + size_t len; + char *p; + + if (str == NULL) + return NULL; + + len = strlen(str); + if (len > max_len) + len = max_len; + p = bmake_malloc(len + 1); + memcpy(p, str, len); + p[len] = '\0'; + + return(p); +} + +/* + * bmake_realloc -- + * realloc, but die on error. + */ +void * +bmake_realloc(void *ptr, size_t size) +{ + if ((ptr = realloc(ptr, size)) == NULL) + enomem(); + return(ptr); +} +#endif diff --git a/20120704/make_malloc.h b/20120704/make_malloc.h new file mode 100644 index 0000000..36d3eff --- /dev/null +++ b/20120704/make_malloc.h @@ -0,0 +1,41 @@ +/* $NetBSD: make_malloc.h,v 1.4 2009/01/24 14:43:29 dsl Exp $ */ + +/*- + * Copyright (c) 2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 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 USE_EMALLOC +void *bmake_malloc(size_t); +void *bmake_realloc(void *, size_t); +char *bmake_strdup(const char *); +char *bmake_strndup(const char *, size_t); +#else +#include <util.h> +#define bmake_malloc(x) emalloc(x) +#define bmake_realloc(x,y) erealloc(x,y) +#define bmake_strdup(x) estrdup(x) +#define bmake_strndup(x,y) estrndup(x,y) +#endif + diff --git a/20120704/meta.c b/20120704/meta.c new file mode 100644 index 0000000..77af4e8 --- /dev/null +++ b/20120704/meta.c @@ -0,0 +1,1346 @@ +/* $NetBSD: meta.c,v 1.25 2012/06/27 17:22:58 sjg Exp $ */ + +/* + * Implement 'meta' mode. + * Adapted from John Birrell's patches to FreeBSD make. + * --sjg + */ +/* + * Copyright (c) 2009-2010, Juniper Networks, Inc. + * Portions Copyright (c) 2009, John Birrell. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#if defined(USE_META) + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <libgen.h> +#include <errno.h> +#if !defined(HAVE_CONFIG_H) || defined(HAVE_ERR_H) +#include <err.h> +#endif + +#include "make.h" +#include "job.h" + +#ifdef HAVE_FILEMON_H +# include <filemon.h> +#endif +#if !defined(USE_FILEMON) && defined(FILEMON_SET_FD) +# define USE_FILEMON +#endif + +static BuildMon Mybm; /* for compat */ +static Lst metaBailiwick; /* our scope of control */ + +Boolean useMeta = FALSE; +static Boolean useFilemon = FALSE; +static Boolean writeMeta = FALSE; +static Boolean metaEnv = FALSE; /* don't save env unless asked */ +static Boolean metaVerbose = FALSE; +static Boolean metaIgnoreCMDs = FALSE; /* ignore CMDs in .meta files */ +static Boolean metaCurdirOk = FALSE; /* write .meta in .CURDIR Ok? */ +static Boolean metaSilent = FALSE; /* if we have a .meta be SILENT */ + +extern Boolean forceJobs; +extern Boolean comatMake; +extern char **environ; + +#define MAKE_META_PREFIX ".MAKE.META.PREFIX" + +#ifndef N2U +# define N2U(n, u) (((n) + ((u) - 1)) / (u)) +#endif +#ifndef ROUNDUP +# define ROUNDUP(n, u) (N2U((n), (u)) * (u)) +#endif + +#if !defined(HAVE_STRSEP) +# define strsep(s, d) stresep((s), (d), 0) +#endif + +/* + * Filemon is a kernel module which snoops certain syscalls. + * + * C chdir + * E exec + * F [v]fork + * L [sym]link + * M rename + * R read + * W write + * S stat + * + * See meta_oodate below - we mainly care about 'E' and 'R'. + * + * We can still use meta mode without filemon, but + * the benefits are more limited. + */ +#ifdef USE_FILEMON +# ifndef _PATH_FILEMON +# define _PATH_FILEMON "/dev/filemon" +# endif + +/* + * Open the filemon device. + */ +static void +filemon_open(BuildMon *pbm) +{ + int retry; + + pbm->mon_fd = pbm->filemon_fd = -1; + if (!useFilemon) + return; + + for (retry = 5; retry >= 0; retry--) { + if ((pbm->filemon_fd = open(_PATH_FILEMON, O_RDWR)) >= 0) + break; + } + + if (pbm->filemon_fd < 0) { + useFilemon = FALSE; + warn("Could not open %s", _PATH_FILEMON); + return; + } + + /* + * We use a file outside of '.' + * to avoid a FreeBSD kernel bug where unlink invalidates + * cwd causing getcwd to do a lot more work. + * We only care about the descriptor. + */ + pbm->mon_fd = mkTempFile("filemon.XXXXXX", NULL); + if (ioctl(pbm->filemon_fd, FILEMON_SET_FD, &pbm->mon_fd) < 0) { + err(1, "Could not set filemon file descriptor!"); + } + /* we don't need these once we exec */ + (void)fcntl(pbm->mon_fd, F_SETFD, 1); + (void)fcntl(pbm->filemon_fd, F_SETFD, 1); +} + +/* + * Read the build monitor output file and write records to the target's + * metadata file. + */ +static void +filemon_read(FILE *mfp, int fd) +{ + FILE *fp; + char buf[BUFSIZ]; + + /* Check if we're not writing to a meta data file.*/ + if (mfp == NULL) { + if (fd >= 0) + close(fd); /* not interested */ + return; + } + /* rewind */ + (void)lseek(fd, (off_t)0, SEEK_SET); + if ((fp = fdopen(fd, "r")) == NULL) + err(1, "Could not read build monitor file '%d'", fd); + + fprintf(mfp, "-- filemon acquired metadata --\n"); + + while (fgets(buf, sizeof(buf), fp)) { + fprintf(mfp, "%s", buf); + } + fflush(mfp); + clearerr(fp); + fclose(fp); +} +#endif + +/* + * when realpath() fails, + * we use this, to clean up ./ and ../ + */ +static void +eat_dots(char *buf, size_t bufsz, int dots) +{ + char *cp; + char *cp2; + const char *eat; + size_t eatlen; + + switch (dots) { + case 1: + eat = "/./"; + eatlen = 2; + break; + case 2: + eat = "/../"; + eatlen = 3; + break; + default: + return; + } + + do { + cp = strstr(buf, eat); + if (cp) { + cp2 = cp + eatlen; + if (dots == 2 && cp > buf) { + do { + cp--; + } while (cp > buf && *cp != '/'); + } + if (*cp == '/') { + strlcpy(cp, cp2, bufsz - (cp - buf)); + } else { + return; /* can't happen? */ + } + } + } while (cp); +} + +static char * +meta_name(struct GNode *gn, char *mname, size_t mnamelen, + const char *dname, + const char *tname) +{ + char buf[MAXPATHLEN]; + char cwd[MAXPATHLEN]; + char *rp; + char *cp; + char *tp; + char *p[4]; /* >= number of possible uses */ + int i; + + i = 0; + if (!dname) + dname = Var_Value(".OBJDIR", gn, &p[i++]); + if (!tname) + tname = Var_Value(TARGET, gn, &p[i++]); + + if (realpath(dname, cwd)) + dname = cwd; + + /* + * Weed out relative paths from the target file name. + * We have to be careful though since if target is a + * symlink, the result will be unstable. + * So we use realpath() just to get the dirname, and leave the + * basename as given to us. + */ + if ((cp = strrchr(tname, '/'))) { + if (realpath(tname, buf)) { + if ((rp = strrchr(buf, '/'))) { + rp++; + cp++; + if (strcmp(cp, rp) != 0) + strlcpy(rp, cp, sizeof(buf) - (rp - buf)); + } + tname = buf; + } else { + /* + * We likely have a directory which is about to be made. + * We pretend realpath() succeeded, to have a chance + * of generating the same meta file name that we will + * next time through. + */ + if (tname[0] == '/') { + strlcpy(buf, tname, sizeof(buf)); + } else { + snprintf(buf, sizeof(buf), "%s/%s", cwd, tname); + } + eat_dots(buf, sizeof(buf), 1); /* ./ */ + eat_dots(buf, sizeof(buf), 2); /* ../ */ + tname = buf; + } + } + /* on some systems dirname may modify its arg */ + tp = bmake_strdup(tname); + if (strcmp(dname, dirname(tp)) == 0) + snprintf(mname, mnamelen, "%s.meta", tname); + else { + snprintf(mname, mnamelen, "%s/%s.meta", dname, tname); + + /* + * Replace path separators in the file name after the + * current object directory path. + */ + cp = mname + strlen(dname) + 1; + + while (*cp != '\0') { + if (*cp == '/') + *cp = '_'; + cp++; + } + } + free(tp); + for (i--; i >= 0; i--) { + if (p[i]) + free(p[i]); + } + return (mname); +} + +/* + * Return true if running ${.MAKE} + * Bypassed if target is flagged .MAKE + */ +static int +is_submake(void *cmdp, void *gnp) +{ + static char *p_make = NULL; + static int p_len; + char *cmd = cmdp; + GNode *gn = gnp; + char *mp = NULL; + char *cp; + char *cp2; + int rc = 0; /* keep looking */ + + if (!p_make) { + p_make = Var_Value(".MAKE", gn, &cp); + p_len = strlen(p_make); + } + cp = strchr(cmd, '$'); + if ((cp)) { + mp = Var_Subst(NULL, cmd, gn, FALSE); + cmd = mp; + } + cp2 = strstr(cmd, p_make); + if ((cp2)) { + switch (cp2[p_len]) { + case '\0': + case ' ': + case '\t': + case '\n': + rc = 1; + break; + } + if (cp2 > cmd && rc > 0) { + switch (cp2[-1]) { + case ' ': + case '\t': + case '\n': + break; + default: + rc = 0; /* no match */ + break; + } + } + } + if (mp) + free(mp); + return (rc); +} + +typedef struct meta_file_s { + FILE *fp; + GNode *gn; +} meta_file_t; + +static int +printCMD(void *cmdp, void *mfpp) +{ + meta_file_t *mfp = mfpp; + char *cmd = cmdp; + char *cp = NULL; + + if (strchr(cmd, '$')) { + cmd = cp = Var_Subst(NULL, cmd, mfp->gn, FALSE); + } + fprintf(mfp->fp, "CMD %s\n", cmd); + if (cp) + free(cp); + return 0; +} + +/* + * Certain node types never get a .meta file + */ +#define SKIP_META_TYPE(_type) do { \ + if ((gn->type & __CONCAT(OP_, _type))) { \ + if (DEBUG(META)) { \ + fprintf(debug_file, "Skipping meta for %s: .%s\n", \ + gn->name, __STRING(_type)); \ + } \ + return (NULL); \ + } \ +} while (0) + +static FILE * +meta_create(BuildMon *pbm, GNode *gn) +{ + meta_file_t mf; + char buf[MAXPATHLEN]; + char objdir[MAXPATHLEN]; + char **ptr; + const char *dname; + const char *tname; + char *fname; + const char *cp; + char *p[4]; /* >= possible uses */ + int i; + struct stat fs; + + + /* This may be a phony node which we don't want meta data for... */ + /* Skip .meta for .BEGIN, .END, .ERROR etc as well. */ + /* Or it may be explicitly flagged as .NOMETA */ + SKIP_META_TYPE(NOMETA); + /* Unless it is explicitly flagged as .META */ + if (!(gn->type & OP_META)) { + SKIP_META_TYPE(PHONY); + SKIP_META_TYPE(SPECIAL); + SKIP_META_TYPE(MAKE); + } + + mf.fp = NULL; + + i = 0; + + dname = Var_Value(".OBJDIR", gn, &p[i++]); + tname = Var_Value(TARGET, gn, &p[i++]); + + /* The object directory may not exist. Check it.. */ + if (stat(dname, &fs) != 0) { + if (DEBUG(META)) + fprintf(debug_file, "Skipping meta for %s: no .OBJDIR\n", + gn->name); + goto out; + } + /* Check if there are no commands to execute. */ + if (Lst_IsEmpty(gn->commands)) { + if (DEBUG(META)) + fprintf(debug_file, "Skipping meta for %s: no commands\n", + gn->name); + goto out; + } + + /* make sure these are canonical */ + if (realpath(dname, objdir)) + dname = objdir; + + /* If we aren't in the object directory, don't create a meta file. */ + if (!metaCurdirOk && strcmp(curdir, dname) == 0) { + if (DEBUG(META)) + fprintf(debug_file, "Skipping meta for %s: .OBJDIR == .CURDIR\n", + gn->name); + goto out; + } + if (!(gn->type & OP_META)) { + /* We do not generate .meta files for sub-makes */ + if (Lst_ForEach(gn->commands, is_submake, gn)) { + if (DEBUG(META)) + fprintf(debug_file, "Skipping meta for %s: .MAKE\n", + gn->name); + goto out; + } + } + + if (metaVerbose) { + char *mp; + + /* Describe the target we are building */ + mp = Var_Subst(NULL, "${" MAKE_META_PREFIX "}", gn, 0); + if (*mp) + fprintf(stdout, "%s\n", mp); + free(mp); + } + /* Get the basename of the target */ + if ((cp = strrchr(tname, '/')) == NULL) { + cp = tname; + } else { + cp++; + } + + fflush(stdout); + + if (strcmp(cp, makeDependfile) == 0) + goto out; + + if (!writeMeta) + /* Don't create meta data. */ + goto out; + + fname = meta_name(gn, pbm->meta_fname, sizeof(pbm->meta_fname), + dname, tname); + +#ifdef DEBUG_META_MODE + if (DEBUG(META)) + fprintf(debug_file, "meta_create: %s\n", fname); +#endif + + if ((mf.fp = fopen(fname, "w")) == NULL) + err(1, "Could not open meta file '%s'", fname); + + fprintf(mf.fp, "# Meta data file %s\n", fname); + + mf.gn = gn; + + Lst_ForEach(gn->commands, printCMD, &mf); + + fprintf(mf.fp, "CWD %s\n", getcwd(buf, sizeof(buf))); + fprintf(mf.fp, "TARGET %s\n", tname); + + if (metaEnv) { + for (ptr = environ; *ptr != NULL; ptr++) + fprintf(mf.fp, "ENV %s\n", *ptr); + } + + fprintf(mf.fp, "-- command output --\n"); + fflush(mf.fp); + + Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL); + Var_Append(".MAKE.META.CREATED", fname, VAR_GLOBAL); + + gn->type |= OP_META; /* in case anyone wants to know */ + if (metaSilent) { + gn->type |= OP_SILENT; + } + out: + for (i--; i >= 0; i--) { + if (p[i]) + free(p[i]); + } + + return (mf.fp); +} + +static Boolean +boolValue(char *s) +{ + switch(*s) { + case '0': + case 'N': + case 'n': + case 'F': + case 'f': + return FALSE; + } + return TRUE; +} + +void +meta_init(const char *make_mode) +{ + static int once = 0; + char *cp; + + useMeta = TRUE; + useFilemon = TRUE; + writeMeta = TRUE; + + if (make_mode) { + if (strstr(make_mode, "env")) + metaEnv = TRUE; + if (strstr(make_mode, "verb")) + metaVerbose = TRUE; + if (strstr(make_mode, "read")) + writeMeta = FALSE; + if (strstr(make_mode, "nofilemon")) + useFilemon = FALSE; + if ((cp = strstr(make_mode, "curdirok="))) { + metaCurdirOk = boolValue(&cp[9]); + } + if ((cp = strstr(make_mode, "silent="))) { + metaSilent = boolValue(&cp[7]); + } + if (strstr(make_mode, "ignore-cmd")) + metaIgnoreCMDs = TRUE; + /* for backwards compatability */ + Var_Set(".MAKE.META_CREATED", "${.MAKE.META.CREATED}", VAR_GLOBAL, 0); + Var_Set(".MAKE.META_FILES", "${.MAKE.META.FILES}", VAR_GLOBAL, 0); + } + if (metaVerbose && !Var_Exists(MAKE_META_PREFIX, VAR_GLOBAL)) { + /* + * The default value for MAKE_META_PREFIX + * prints the absolute path of the target. + * This works be cause :H will generate '.' if there is no / + * and :tA will resolve that to cwd. + */ + Var_Set(MAKE_META_PREFIX, "Building ${.TARGET:H:tA}/${.TARGET:T}", VAR_GLOBAL, 0); + } + if (once) + return; + once = 1; + memset(&Mybm, 0, sizeof(Mybm)); + /* + * We consider ourselves master of all within ${.MAKE.META.BAILIWICK} + */ + metaBailiwick = Lst_Init(FALSE); + cp = Var_Subst(NULL, "${.MAKE.META.BAILIWICK:O:u:tA}", VAR_GLOBAL, 0); + if (cp) { + str2Lst_Append(metaBailiwick, cp, NULL); + } +} + +/* + * In each case below we allow for job==NULL + */ +void +meta_job_start(Job *job, GNode *gn) +{ + BuildMon *pbm; + + if (job != NULL) { + pbm = &job->bm; + } else { + pbm = &Mybm; + } + pbm->mfp = meta_create(pbm, gn); +#ifdef USE_FILEMON_ONCE + /* compat mode we open the filemon dev once per command */ + if (job == NULL) + return; +#endif +#ifdef USE_FILEMON + if (pbm->mfp != NULL && useFilemon) { + filemon_open(pbm); + } else { + pbm->mon_fd = pbm->filemon_fd = -1; + } +#endif +} + +/* + * The child calls this before doing anything. + * It does not disturb our state. + */ +void +meta_job_child(Job *job) +{ +#ifdef USE_FILEMON + BuildMon *pbm; + pid_t pid; + + if (job != NULL) { + pbm = &job->bm; + } else { + pbm = &Mybm; + } + pid = getpid(); + if (pbm->mfp != NULL && useFilemon) { + if (ioctl(pbm->filemon_fd, FILEMON_SET_PID, &pid) < 0) { + err(1, "Could not set filemon pid!"); + } + } +#endif +} + +void +meta_job_error(Job *job, GNode *gn, int flags, int status) +{ + char cwd[MAXPATHLEN]; + BuildMon *pbm; + + if (job != NULL) { + pbm = &job->bm; + } else { + if (!gn) + gn = job->node; + pbm = &Mybm; + } + if (pbm->mfp != NULL) { + fprintf(pbm->mfp, "*** Error code %d%s\n", + status, + (flags & JOB_IGNERR) ? + "(ignored)" : ""); + } + if (gn) { + Var_Set(".ERROR_TARGET", gn->path ? gn->path : gn->name, VAR_GLOBAL, 0); + } + getcwd(cwd, sizeof(cwd)); + Var_Set(".ERROR_CWD", cwd, VAR_GLOBAL, 0); + if (pbm && pbm->meta_fname[0]) { + Var_Set(".ERROR_META_FILE", pbm->meta_fname, VAR_GLOBAL, 0); + } + meta_job_finish(job); +} + +void +meta_job_output(Job *job, char *cp, const char *nl) +{ + BuildMon *pbm; + + if (job != NULL) { + pbm = &job->bm; + } else { + pbm = &Mybm; + } + if (pbm->mfp != NULL) { + if (metaVerbose) { + static char *meta_prefix = NULL; + static int meta_prefix_len; + + if (!meta_prefix) { + char *cp2; + + meta_prefix = Var_Subst(NULL, "${" MAKE_META_PREFIX "}", VAR_GLOBAL, 0); + if ((cp2 = strchr(meta_prefix, '$'))) + meta_prefix_len = cp2 - meta_prefix; + else + meta_prefix_len = strlen(meta_prefix); + } + if (strncmp(cp, meta_prefix, meta_prefix_len) == 0) { + cp = strchr(cp+1, '\n'); + if (!cp++) + return; + } + } + fprintf(pbm->mfp, "%s%s", cp, nl); + } +} + +void +meta_cmd_finish(void *pbmp) +{ +#ifdef USE_FILEMON + BuildMon *pbm = pbmp; + + if (!pbm) + pbm = &Mybm; + + if (pbm->filemon_fd >= 0) { + close(pbm->filemon_fd); + filemon_read(pbm->mfp, pbm->mon_fd); + pbm->filemon_fd = pbm->mon_fd = -1; + } +#endif +} + +void +meta_job_finish(Job *job) +{ + BuildMon *pbm; + + if (job != NULL) { + pbm = &job->bm; + } else { + pbm = &Mybm; + } + if (pbm->mfp != NULL) { + meta_cmd_finish(pbm); + fclose(pbm->mfp); + pbm->mfp = NULL; + pbm->meta_fname[0] = '\0'; + } +} + +/* + * Fetch a full line from fp - growing bufp if needed + * Return length in bufp. + */ +static int +fgetLine(char **bufp, size_t *szp, int o, FILE *fp) +{ + char *buf = *bufp; + size_t bufsz = *szp; + struct stat fs; + int x; + + if (fgets(&buf[o], bufsz - o, fp) != NULL) { + check_newline: + x = o + strlen(&buf[o]); + if (buf[x - 1] == '\n') + return x; + /* + * We need to grow the buffer. + * The meta file can give us a clue. + */ + if (fstat(fileno(fp), &fs) == 0) { + size_t newsz; + char *p; + + newsz = ROUNDUP((fs.st_size / 2), BUFSIZ); + if (newsz <= bufsz) + newsz = ROUNDUP(fs.st_size, BUFSIZ); + if (DEBUG(META)) + fprintf(debug_file, "growing buffer %u -> %u\n", + (unsigned)bufsz, (unsigned)newsz); + p = bmake_realloc(buf, newsz); + if (p) { + *bufp = buf = p; + *szp = bufsz = newsz; + /* fetch the rest */ + if (!fgets(&buf[x], bufsz - x, fp)) + return x; /* truncated! */ + goto check_newline; + } + } + } + return 0; +} + +static int +prefix_match(void *p, void *q) +{ + const char *prefix = p; + const char *path = q; + size_t n = strlen(prefix); + + return (0 == strncmp(path, prefix, n)); +} + +static int +string_match(const void *p, const void *q) +{ + const char *p1 = p; + const char *p2 = q; + + return strcmp(p1, p2); +} + + +/* + * When running with 'meta' functionality, a target can be out-of-date + * if any of the references in it's meta data file is more recent. + * We have to track the latestdir on a per-process basis. + */ +#define LDIR_VNAME_FMT ".meta.%d.ldir" + +/* + * It is possible that a .meta file is corrupted, + * if we detect this we want to reproduce it. + * Setting oodate TRUE will have that effect. + */ +#define CHECK_VALID_META(p) if (!(p && *p)) { \ + warnx("%s: %d: malformed", fname, lineno); \ + oodate = TRUE; \ + continue; \ + } + +Boolean +meta_oodate(GNode *gn, Boolean oodate) +{ + static char *tmpdir = NULL; + static char cwd[MAXPATHLEN]; + char ldir_vname[64]; + char latestdir[MAXPATHLEN]; + char fname[MAXPATHLEN]; + char fname1[MAXPATHLEN]; + char fname2[MAXPATHLEN]; + char *p; + char *cp; + static size_t cwdlen = 0; + static size_t tmplen = 0; + FILE *fp; + Boolean ignoreOODATE = FALSE; + Lst missingFiles; + + if (oodate) + return oodate; /* we're done */ + + missingFiles = Lst_Init(FALSE); + + /* + * We need to check if the target is out-of-date. This includes + * checking if the expanded command has changed. This in turn + * requires that all variables are set in the same way that they + * would be if the target needs to be re-built. + */ + Make_DoAllVar(gn); + + meta_name(gn, fname, sizeof(fname), NULL, NULL); + +#ifdef DEBUG_META_MODE + if (DEBUG(META)) + fprintf(debug_file, "meta_oodate: %s\n", fname); +#endif + + if ((fp = fopen(fname, "r")) != NULL) { + static char *buf = NULL; + static size_t bufsz; + int lineno = 0; + int lastpid = 0; + int pid; + int f = 0; + int x; + LstNode ln; + struct stat fs; + + if (!buf) { + bufsz = 8 * BUFSIZ; + buf = bmake_malloc(bufsz); + } + + if (!cwdlen) { + if (getcwd(cwd, sizeof(cwd)) == NULL) + err(1, "Could not get current working directory"); + cwdlen = strlen(cwd); + } + + if (!tmpdir) { + tmpdir = getTmpdir(); + tmplen = strlen(tmpdir); + } + + /* we want to track all the .meta we read */ + Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL); + + ln = Lst_First(gn->commands); + while (!oodate && (x = fgetLine(&buf, &bufsz, 0, fp)) > 0) { + lineno++; + if (buf[x - 1] == '\n') + buf[x - 1] = '\0'; + else { + warnx("%s: %d: line truncated at %u", fname, lineno, x); + oodate = TRUE; + break; + } + /* Find the start of the build monitor section. */ + if (!f) { + if (strncmp(buf, "-- filemon", 10) == 0) { + f = 1; + continue; + } + if (strncmp(buf, "# buildmon", 10) == 0) { + f = 1; + continue; + } + } + + /* Delimit the record type. */ + p = buf; +#ifdef DEBUG_META_MODE + if (DEBUG(META)) + fprintf(debug_file, "%s: %d: %s\n", fname, lineno, buf); +#endif + strsep(&p, " "); + if (f) { + /* + * We are in the 'filemon' output section. + * Each record from filemon follows the general form: + * + * <key> <pid> <data> + * + * Where: + * <key> is a single letter, denoting the syscall. + * <pid> is the process that made the syscall. + * <data> is the arguments (of interest). + */ + switch(buf[0]) { + case '#': /* comment */ + case 'V': /* version */ + break; + default: + /* + * We need to track pathnames per-process. + * + * Each process run by make, starts off in the 'CWD' + * recorded in the .meta file, if it chdirs ('C') + * elsewhere we need to track that - but only for + * that process. If it forks ('F'), we initialize + * the child to have the same cwd as its parent. + * + * We also need to track the 'latestdir' of + * interest. This is usually the same as cwd, but + * not if a process is reading directories. + * + * Each time we spot a different process ('pid') + * we save the current value of 'latestdir' in a + * variable qualified by 'lastpid', and + * re-initialize 'latestdir' to any pre-saved + * value for the current 'pid' and 'CWD' if none. + */ + CHECK_VALID_META(p); + pid = atoi(p); + if (pid > 0 && pid != lastpid) { + char *ldir; + char *tp; + + if (lastpid > 0) { + /* We need to remember this. */ + Var_Set(ldir_vname, latestdir, VAR_GLOBAL, 0); + } + snprintf(ldir_vname, sizeof(ldir_vname), LDIR_VNAME_FMT, pid); + lastpid = pid; + ldir = Var_Value(ldir_vname, VAR_GLOBAL, &tp); + if (ldir) { + strlcpy(latestdir, ldir, sizeof(latestdir)); + if (tp) + free(tp); + } else + strlcpy(latestdir, cwd, sizeof(latestdir)); + } + /* Skip past the pid. */ + if (strsep(&p, " ") == NULL) + continue; +#ifdef DEBUG_META_MODE + if (DEBUG(META)) + fprintf(debug_file, "%s: %d: cwd=%s ldir=%s\n", fname, lineno, cwd, latestdir); +#endif + break; + } + + CHECK_VALID_META(p); + + /* Process according to record type. */ + switch (buf[0]) { + case 'X': /* eXit */ + Var_Delete(ldir_vname, VAR_GLOBAL); + lastpid = 0; /* no need to save ldir_vname */ + break; + + case 'F': /* [v]Fork */ + { + char cldir[64]; + int child; + + child = atoi(p); + if (child > 0) { + snprintf(cldir, sizeof(cldir), LDIR_VNAME_FMT, child); + Var_Set(cldir, latestdir, VAR_GLOBAL, 0); + } + } + break; + + case 'C': /* Chdir */ + /* Update the latest directory. */ + strlcpy(latestdir, p, sizeof(latestdir)); + break; + + case 'M': /* renaMe */ + if (Lst_IsEmpty(missingFiles)) + break; + /* 'L' and 'M' put single quotes around the args */ + if (*p == '\'') { + char *ep; + + p++; + if ((ep = strchr(p, '\''))) + *ep = '\0'; + } + /* FALLTHROUGH */ + case 'D': /* unlink */ + if (*p == '/' && !Lst_IsEmpty(missingFiles)) { + /* remove p from the missingFiles list if present */ + if ((ln = Lst_Find(missingFiles, p, string_match)) != NULL) { + char *tp = Lst_Datum(ln); + Lst_Remove(missingFiles, ln); + free(tp); + } + } + break; + case 'L': /* Link */ + /* we want the target */ + if (strsep(&p, " ") == NULL) + continue; + CHECK_VALID_META(p); + /* 'L' and 'M' put single quotes around the args */ + if (*p == '\'') { + char *ep; + + p++; + if ((ep = strchr(p, '\''))) + *ep = '\0'; + } + /* FALLTHROUGH */ + case 'W': /* Write */ + /* + * If a file we generated within our bailiwick + * but outside of .OBJDIR is missing, + * we need to do it again. + */ + /* ignore non-absolute paths */ + if (*p != '/') + break; + + if (Lst_IsEmpty(metaBailiwick)) + break; + + /* ignore cwd - normal dependencies handle those */ + if (strncmp(p, cwd, cwdlen) == 0) + break; + + if (!Lst_ForEach(metaBailiwick, prefix_match, p)) + break; + + /* tmpdir might be within */ + if (tmplen > 0 && strncmp(p, tmpdir, tmplen) == 0) + break; + + /* ignore anything containing the string "tmp" */ + if ((strstr("tmp", p))) + break; + + if (stat(p, &fs) < 0) { + Lst_AtEnd(missingFiles, bmake_strdup(p)); + } + break; + case 'R': /* Read */ + case 'E': /* Exec */ + /* + * Check for runtime files that can't + * be part of the dependencies because + * they are _expected_ to change. + */ + if (strncmp(p, "/tmp/", 5) == 0 || + (tmplen > 0 && strncmp(p, tmpdir, tmplen) == 0)) + break; + + if (strncmp(p, "/var/", 5) == 0) + break; + + /* Ignore device files. */ + if (strncmp(p, "/dev/", 5) == 0) + break; + + /* Ignore /etc/ files. */ + if (strncmp(p, "/etc/", 5) == 0) + break; + + if ((cp = strrchr(p, '/'))) { + cp++; + /* + * We don't normally expect to see this, + * but we do expect it to change. + */ + if (strcmp(cp, makeDependfile) == 0) + break; + } + + /* + * The rest of the record is the file name. + * Check if it's not an absolute path. + */ + { + char *sdirs[4]; + char **sdp; + int sdx = 0; + int found = 0; + + if (*p == '/') { + sdirs[sdx++] = p; /* done */ + } else { + if (strcmp(".", p) == 0) + continue; /* no point */ + + /* Check vs latestdir */ + snprintf(fname1, sizeof(fname1), "%s/%s", latestdir, p); + sdirs[sdx++] = fname1; + + if (strcmp(latestdir, cwd) != 0) { + /* Check vs cwd */ + snprintf(fname2, sizeof(fname2), "%s/%s", cwd, p); + sdirs[sdx++] = fname2; + } + } + sdirs[sdx++] = NULL; + + for (sdp = sdirs; *sdp && !found; sdp++) { +#ifdef DEBUG_META_MODE + if (DEBUG(META)) + fprintf(debug_file, "%s: %d: looking for: %s\n", fname, lineno, *sdp); +#endif + if (stat(*sdp, &fs) == 0) { + found = 1; + p = *sdp; + } + } + if (found) { +#ifdef DEBUG_META_MODE + if (DEBUG(META)) + fprintf(debug_file, "%s: %d: found: %s\n", fname, lineno, p); +#endif + if (!S_ISDIR(fs.st_mode) && + fs.st_mtime > gn->mtime) { + if (DEBUG(META)) + fprintf(debug_file, "%s: %d: file '%s' is newer than the target...\n", fname, lineno, p); + oodate = TRUE; + } else if (S_ISDIR(fs.st_mode)) { + /* Update the latest directory. */ + realpath(p, latestdir); + } + } else if (errno == ENOENT && *p == '/' && + strncmp(p, cwd, cwdlen) != 0) { + /* + * A referenced file outside of CWD is missing. + * We cannot catch every eventuality here... + */ + if (DEBUG(META)) + fprintf(debug_file, "%s: %d: file '%s' may have moved?...\n", fname, lineno, p); + oodate = TRUE; + } + } + break; + default: + break; + } + } else if (strcmp(buf, "CMD") == 0) { + /* + * Compare the current command with the one in the + * meta data file. + */ + if (ln == NULL) { + if (DEBUG(META)) + fprintf(debug_file, "%s: %d: there were more build commands in the meta data file than there are now...\n", fname, lineno); + oodate = TRUE; + } else { + char *cmd = (char *)Lst_Datum(ln); + + if (!ignoreOODATE) { + if (strstr(cmd, "$?")) + ignoreOODATE = TRUE; + else if ((cp = strstr(cmd, ".OODATE"))) { + /* check for $[{(].OODATE[)}] */ + if (cp > cmd + 2 && cp[-2] == '$') + ignoreOODATE = TRUE; + } + if (ignoreOODATE && DEBUG(META)) + fprintf(debug_file, "%s: %d: cannot compare commands using .OODATE\n", fname, lineno); + } + cmd = Var_Subst(NULL, cmd, gn, TRUE); + + if ((cp = strchr(cmd, '\n'))) { + int n; + + /* + * This command contains newlines, we need to + * fetch more from the .meta file before we + * attempt a comparison. + */ + /* first put the newline back at buf[x - 1] */ + buf[x - 1] = '\n'; + do { + /* now fetch the next line */ + if ((n = fgetLine(&buf, &bufsz, x, fp)) <= 0) + break; + x = n; + lineno++; + if (buf[x - 1] != '\n') { + warnx("%s: %d: line truncated at %u", fname, lineno, x); + break; + } + cp = strchr(++cp, '\n'); + } while (cp); + if (buf[x - 1] == '\n') + buf[x - 1] = '\0'; + } + if (!ignoreOODATE && + !(gn->type & OP_NOMETA_CMP) && + strcmp(p, cmd) != 0) { + if (DEBUG(META)) + fprintf(debug_file, "%s: %d: a build command has changed\n%s\nvs\n%s\n", fname, lineno, p, cmd); + if (!metaIgnoreCMDs) + oodate = TRUE; + } + free(cmd); + ln = Lst_Succ(ln); + } + } else if (strcmp(buf, "CWD") == 0) { + /* + * Check if there are extra commands now + * that weren't in the meta data file. + */ + if (!oodate && ln != NULL) { + if (DEBUG(META)) + fprintf(debug_file, "%s: %d: there are extra build commands now that weren't in the meta data file\n", fname, lineno); + oodate = TRUE; + } + if (strcmp(p, cwd) != 0) { + if (DEBUG(META)) + fprintf(debug_file, "%s: %d: the current working directory has changed from '%s' to '%s'\n", fname, lineno, p, curdir); + oodate = TRUE; + } + } + } + + fclose(fp); + if (!Lst_IsEmpty(missingFiles)) { + if (DEBUG(META)) + fprintf(debug_file, "%s: missing files: %s...\n", + fname, (char *)Lst_Datum(Lst_First(missingFiles))); + oodate = TRUE; + Lst_Destroy(missingFiles, (FreeProc *)free); + } + } else { + if ((gn->type & OP_META)) { + if (DEBUG(META)) + fprintf(debug_file, "%s: required but missing\n", fname); + oodate = TRUE; + } + } + if (oodate && ignoreOODATE) { + /* + * Target uses .OODATE, so we need to re-compute it. + * We need to clean up what Make_DoAllVar() did. + */ + Var_Delete(ALLSRC, gn); + Var_Delete(OODATE, gn); + gn->flags &= ~DONE_ALLSRC; + } + return oodate; +} + +/* support for compat mode */ + +static int childPipe[2]; + +void +meta_compat_start(void) +{ +#ifdef USE_FILEMON_ONCE + /* + * We need to re-open filemon for each cmd. + */ + BuildMon *pbm = &Mybm; + + if (pbm->mfp != NULL && useFilemon) { + filemon_open(pbm); + } else { + pbm->mon_fd = pbm->filemon_fd = -1; + } +#endif + if (pipe(childPipe) < 0) + Punt("Cannot create pipe: %s", strerror(errno)); + /* Set close-on-exec flag for both */ + (void)fcntl(childPipe[0], F_SETFD, 1); + (void)fcntl(childPipe[1], F_SETFD, 1); +} + +void +meta_compat_child(void) +{ + meta_job_child(NULL); + if (dup2(childPipe[1], 1) < 0 || + dup2(1, 2) < 0) { + execError("dup2", "pipe"); + _exit(1); + } +} + +void +meta_compat_parent(void) +{ + FILE *fp; + char buf[BUFSIZ]; + + close(childPipe[1]); /* child side */ + fp = fdopen(childPipe[0], "r"); + while (fgets(buf, sizeof(buf), fp)) { + meta_job_output(NULL, buf, ""); + printf("%s", buf); + } + fclose(fp); +} + +#endif /* USE_META */ diff --git a/20120704/meta.h b/20120704/meta.h new file mode 100644 index 0000000..1ce01ca --- /dev/null +++ b/20120704/meta.h @@ -0,0 +1,54 @@ +/* $NetBSD: meta.h,v 1.2 2011/03/30 22:03:49 sjg Exp $ */ + +/* + * Things needed for 'meta' mode. + */ +/* + * Copyright (c) 2009-2010, Juniper Networks, 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. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +typedef struct BuildMon { + char meta_fname[MAXPATHLEN]; + int filemon_fd; + int mon_fd; + FILE *mfp; +} BuildMon; + +extern Boolean useMeta; + +struct Job; /* not defined yet */ +void meta_init(const char *); +void meta_job_start(struct Job *, GNode *); +void meta_job_child(struct Job *); +void meta_job_error(struct Job *, GNode *, int, int); +void meta_job_output(struct Job *, char *, const char *); +void meta_cmd_finish(void *); +void meta_job_finish(struct Job *); +Boolean meta_oodate(GNode *, Boolean); +void meta_compat_start(void); +void meta_compat_child(void); +void meta_compat_parent(void); diff --git a/20120704/mkdeps.sh b/20120704/mkdeps.sh new file mode 100755 index 0000000..dd87c4f --- /dev/null +++ b/20120704/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 <sjg@zen.void.oz.au> +# + +# RCSid: +# $Id: mkdeps.sh,v 1.23 2002/11/29 06:58:59 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 <stdio.h>" > /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/20120704/nonints.h b/20120704/nonints.h new file mode 100644 index 0000000..eeb197e --- /dev/null +++ b/20120704/nonints.h @@ -0,0 +1,198 @@ +/* $NetBSD: nonints.h,v 1.64 2012/06/12 19:21:51 joerg 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 + */ + +/* 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(void *, void *); +void Compat_Run(Lst); +int Compat_Make(void *, void *); + +/* cond.c */ +struct If; +int Cond_EvalExpression(const struct If *, char *, Boolean *, int); +int Cond_Eval(char *); +void Cond_restore_depth(unsigned int); +unsigned int Cond_save_depth(void); + +/* for.c */ +int For_Eval(char *); +int For_Accum(char *); +void For_Run(int); + +/* job.c */ +#ifdef WAIT_T +void JobReapChild(pid_t, WAIT_T, Boolean); +#endif + +/* main.c */ +void Main_ParseArgLine(const char *); +void MakeMode(const char *); +int main(int, char **); +char *Cmd_Exec(const char *, const char **); +void Error(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2); +void Fatal(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2) MAKE_ATTR_DEAD; +void Punt(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2) MAKE_ATTR_DEAD; +void DieHorribly(void) MAKE_ATTR_DEAD; +int PrintAddr(void *, void *); +void Finish(int) MAKE_ATTR_DEAD; +int eunlink(const char *); +void execError(const char *, const char *); +char *getTmpdir(void); + +/* parse.c */ +void Parse_Error(int, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3); +Boolean Parse_AnyExport(void); +Boolean Parse_IsVar(char *); +void Parse_DoVar(char *, GNode *); +void Parse_AddIncludeDir(char *); +void Parse_File(const char *, int); +void Parse_Init(void); +void Parse_End(void); +void Parse_SetInput(const char *, int, int, char *(*)(void *, size_t *), void *); +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(void *, void *); +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(void *, void *); +int Targ_PrintNode(void *, void *); +char *Targ_FmtTime(time_t); +void Targ_PrintType(int); +void Targ_PrintGraph(int); +void Targ_Propagate(void); +void Targ_Propagate_Wait(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 *, void **); +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 *); +void Var_ExportVars(void); +void Var_Export(char *, int); +void Var_UnExport(char *); + +/* util.c */ +void (*bmake_signal(int, void (*)(int)))(int); diff --git a/20120704/os.sh b/20120704/os.sh new file mode 100755 index 0000000..9e45f37 --- /dev/null +++ b/20120704/os.sh @@ -0,0 +1,228 @@ +: +# 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 <sjg@crufty.net> + +# RCSid: +# $Id: os.sh,v 1.44 2010/06/29 15:37:21 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` + +# there is at least one case of `uname -p` outputting +# a bunch of usless drivel +case "$MACHINE_ARCH" in +*[!A-Za-z0-9_-]*) MACHINE_ARCH="$MACHINE";; +esac + +# 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; } + ;; +QNX) + case $MACHINE in + x86pc) MACHINE_ARCH=i386;; + esac + ;; +Haiku) + case $MACHINE in + BeBox) MACHINE_ARCH=powerpc;; + BeMac) MACHINE_ARCH=powerpc;; + BePC) MACHINE_ARCH=i386;; + esac + ;; +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/20120704/parse.c b/20120704/parse.c new file mode 100644 index 0000000..0b18f5d --- /dev/null +++ b/20120704/parse.c @@ -0,0 +1,3122 @@ +/* $NetBSD: parse.c,v 1.185 2012/06/12 19:21:51 joerg 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.185 2012/06/12 19:21:51 joerg Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)parse.c 8.3 (Berkeley) 3/19/94"; +#else +__RCSID("$NetBSD: parse.c,v 1.185 2012/06/12 19:21:51 joerg 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 <sys/types.h> +#include <sys/stat.h> +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdio.h> + +#include "make.h" +#include "hash.h" +#include "dir.h" +#include "job.h" +#include "buf.h" +#include "pathnames.h" + +#ifdef HAVE_MMAP +#include <sys/mman.h> + +#ifndef MAP_COPY +#define MAP_COPY MAP_PRIVATE +#endif +#ifndef MAP_FILE +#define MAP_FILE 0 +#endif +#endif + +//////////////////////////////////////////////////////////// +// types and constants + +/* + * Structure for a file being read ("included file") + */ +typedef struct IFile { + const char *fname; /* name of file */ + int lineno; /* current line number in file */ + int first_lineno; /* line number of start of text */ + int cond_depth; /* 'if' nesting when file opened */ + char *P_str; /* point to base of string buffer */ + char *P_ptr; /* point to next char of string buffer */ + char *P_end; /* point to the end of string buffer */ + char *(*nextbuf)(void *, size_t *); /* Function to get more data */ + void *nextbuf_arg; /* Opaque arg for nextbuf() */ + struct loadedfile *lf; /* loadedfile object, if any */ +} IFile; + + +/* + * 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 + +/* + * Tokens for target attributes + */ +typedef enum { + Begin, /* .BEGIN */ + Default, /* .DEFAULT */ + End, /* .END */ + dotError, /* .ERROR */ + Ignore, /* .IGNORE */ + Includes, /* .INCLUDES */ + Interrupt, /* .INTERRUPT */ + Libs, /* .LIBS */ + Meta, /* .META */ + MFlags, /* .MFLAGS or .MAKEFLAGS */ + Main, /* .MAIN and we don't have anything user-specified to + * make */ + NoExport, /* .NOEXPORT */ + NoMeta, /* .NOMETA */ + NoMetaCmp, /* .NOMETA_CMP */ + 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; + +/* + * Other tokens + */ +#define LPAREN '(' +#define RPAREN ')' + + +//////////////////////////////////////////////////////////// +// result data + +/* + * The main target to create. This is the first target on the first + * dependency line in the first makefile. + */ +static GNode *mainNode; + +//////////////////////////////////////////////////////////// +// eval state + +/* targets we're working on */ +static Lst targets; + +#ifdef CLEANUP +/* command lines for targets */ +static Lst targCmds; +#endif + +/* + * 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 + */ +static ParseSpecial specType; + +/* + * Predecessor node for handling .ORDER. Initialized to NULL when .ORDER + * seen, then set to each successive source on the line. + */ +static GNode *predecessor; + +//////////////////////////////////////////////////////////// +// parser state + +/* true if currently in a dependency line or its commands */ +static Boolean inLine; + +/* number of fatal errors */ +static int fatals = 0; + +/* + * Variables for doing includes + */ + +/* current file being read */ +static IFile *curFile; + +/* stack of IFiles generated by .includes */ +static Lst includes; + +/* include paths (lists of directories) */ +Lst parseIncPath; /* dirs for "..." includes */ +Lst sysIncPath; /* dirs for <...> includes */ +Lst defIncPath; /* default for sysIncPath */ + +//////////////////////////////////////////////////////////// +// parser tables + +/* + * 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 const 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 }, +{ ".ERROR", dotError, 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 }, +{ ".META", Meta, OP_META }, +{ ".MFLAGS", MFlags, 0 }, +{ ".NOMETA", NoMeta, OP_NOMETA }, +{ ".NOMETA_CMP", NoMetaCmp, OP_NOMETA_CMP }, +{ ".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 }, +}; + +//////////////////////////////////////////////////////////// +// local functions + +static int ParseIsEscaped(const char *, const char *); +static void ParseErrorInternal(const char *, size_t, int, const char *, ...) + MAKE_ATTR_PRINTFLIKE(4,5); +static void ParseVErrorInternal(FILE *, const char *, size_t, int, const char *, va_list) + MAKE_ATTR_PRINTFLIKE(5, 0); +static int ParseFindKeyword(const char *); +static int ParseLinkSrc(void *, void *); +static int ParseDoOp(void *, void *); +static void ParseDoSrc(int, const char *); +static int ParseFindMain(void *, void *); +static int ParseAddDir(void *, void *); +static int ParseClearPath(void *, void *); +static void ParseDoDependency(char *); +static int ParseAddCmd(void *, void *); +static void ParseHasCommands(void *); +static void ParseDoInclude(char *); +static void ParseSetParseFile(const char *); +#ifdef SYSVINCLUDE +static void ParseTraditionalInclude(char *); +#endif +#ifdef GMAKEEXPORT +static void ParseGmakeExport(char *); +#endif +static int ParseEOF(void); +static char *ParseReadLine(void); +static void ParseFinishLine(void); +static void ParseMark(GNode *); + +//////////////////////////////////////////////////////////// +// file loader + +struct loadedfile { + const char *path; /* name, for error reports */ + char *buf; /* contents buffer */ + size_t len; /* length of contents */ + size_t maplen; /* length of mmap area, or 0 */ + Boolean used; /* XXX: have we used the data yet */ +}; + +/* + * Constructor/destructor for loadedfile + */ +static struct loadedfile * +loadedfile_create(const char *path) +{ + struct loadedfile *lf; + + lf = bmake_malloc(sizeof(*lf)); + lf->path = (path == NULL ? "(stdin)" : path); + lf->buf = NULL; + lf->len = 0; + lf->maplen = 0; + lf->used = FALSE; + return lf; +} + +static void +loadedfile_destroy(struct loadedfile *lf) +{ + if (lf->buf != NULL) { + if (lf->maplen > 0) { +#ifdef HAVE_MMAP + munmap(lf->buf, lf->maplen); +#endif + } else { + free(lf->buf); + } + } + free(lf); +} + +/* + * nextbuf() operation for loadedfile, as needed by the weird and twisted + * logic below. Once that's cleaned up, we can get rid of lf->used... + */ +static char * +loadedfile_nextbuf(void *x, size_t *len) +{ + struct loadedfile *lf = x; + + if (lf->used) { + return NULL; + } + lf->used = TRUE; + *len = lf->len; + return lf->buf; +} + +/* + * Try to get the size of a file. + */ +static ReturnStatus +load_getsize(int fd, size_t *ret) +{ + struct stat st; + + if (fstat(fd, &st) < 0) { + return FAILURE; + } + + if (!S_ISREG(st.st_mode)) { + return FAILURE; + } + + /* + * st_size is an off_t, which is 64 bits signed; *ret is + * size_t, which might be 32 bits unsigned or 64 bits + * unsigned. Rather than being elaborate, just punt on + * files that are more than 2^31 bytes. We should never + * see a makefile that size in practice... + * + * While we're at it reject negative sizes too, just in case. + */ + if (st.st_size < 0 || st.st_size > 0x7fffffff) { + return FAILURE; + } + + *ret = (size_t) st.st_size; + return SUCCESS; +} + +/* + * Read in a file. + * + * Until the path search logic can be moved under here instead of + * being in the caller in another source file, we need to have the fd + * passed in already open. Bleh. + * + * If the path is NULL use stdin and (to insure against fd leaks) + * assert that the caller passed in -1. + */ +static struct loadedfile * +loadfile(const char *path, int fd) +{ + struct loadedfile *lf; +#ifdef HAVE_MMAP + long pagesize; +#endif + ssize_t result; + size_t bufpos; + + lf = loadedfile_create(path); + + if (path == NULL) { + assert(fd == -1); + fd = STDIN_FILENO; + } else { +#if 0 /* notyet */ + fd = open(path, O_RDONLY); + if (fd < 0) { + ... + Error("%s: %s", path, strerror(errno)); + exit(1); + } +#endif + } + +#ifdef HAVE_MMAP + if (load_getsize(fd, &lf->len) == SUCCESS) { + /* found a size, try mmap */ + pagesize = sysconf(_SC_PAGESIZE); + if (pagesize <= 0) { + pagesize = 0x1000; + } + /* round size up to a page */ + lf->maplen = pagesize * ((lf->len + pagesize - 1)/pagesize); + + /* + * XXX hack for dealing with empty files; remove when + * we're no longer limited by interfacing to the old + * logic elsewhere in this file. + */ + if (lf->maplen == 0) { + lf->maplen = pagesize; + } + + /* + * FUTURE: remove PROT_WRITE when the parser no longer + * needs to scribble on the input. + */ + lf->buf = mmap(NULL, lf->maplen, PROT_READ|PROT_WRITE, + MAP_FILE|MAP_COPY, fd, 0); + if (lf->buf != MAP_FAILED) { + /* succeeded */ + if (lf->len == lf->maplen && lf->buf[lf->len - 1] != '\n') { + char *b = malloc(lf->len + 1); + b[lf->len] = '\n'; + memcpy(b, lf->buf, lf->len++); + munmap(lf->buf, lf->maplen); + lf->maplen = 0; + lf->buf = b; + } + goto done; + } + } +#endif + /* cannot mmap; load the traditional way */ + + lf->maplen = 0; + lf->len = 1024; + lf->buf = bmake_malloc(lf->len); + + bufpos = 0; + while (1) { + assert(bufpos <= lf->len); + if (bufpos == lf->len) { + lf->len *= 2; + lf->buf = bmake_realloc(lf->buf, lf->len); + } + result = read(fd, lf->buf + bufpos, lf->len - bufpos); + if (result < 0) { + Error("%s: read error: %s", path, strerror(errno)); + exit(1); + } + if (result == 0) { + break; + } + bufpos += result; + } + assert(bufpos <= lf->len); + lf->len = bufpos; + + /* truncate malloc region to actual length (maybe not useful) */ + if (lf->len > 0) { + lf->buf = bmake_realloc(lf->buf, lf->len); + } + +#ifdef HAVE_MMAP +done: +#endif + if (path != NULL) { + close(fd); + } + return lf; +} + +//////////////////////////////////////////////////////////// +// old code + +/*- + *---------------------------------------------------------------------- + * 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(const 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(FILE *f, const char *cfname, size_t clineno, int type, + const char *fmt, va_list ap) +{ + static Boolean fatal_warning_error_printed = FALSE; + + (void)fprintf(f, "%s: ", progname); + + if (cfname != NULL) { + (void)fprintf(f, "\""); + if (*cfname != '/' && strcmp(cfname, "(stdin)") != 0) { + char *cp; + const char *dir; + + /* + * Nothing is more annoying 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(f, "%s/%s", dir, cfname); + } else + (void)fprintf(f, "%s", cfname); + + (void)fprintf(f, "\" line %d: ", (int)clineno); + } + if (type == PARSE_WARNING) + (void)fprintf(f, "warning: "); + (void)vfprintf(f, fmt, ap); + (void)fprintf(f, "\n"); + (void)fflush(f); + 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(const char *cfname, size_t clineno, int type, + const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + (void)fflush(stdout); + ParseVErrorInternal(stderr, cfname, clineno, type, fmt, ap); + va_end(ap); + + if (debug_file != stderr && debug_file != stdout) { + va_start(ap, fmt); + ParseVErrorInternal(debug_file, 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; + const char *fname; + size_t lineno; + + if (curFile == NULL) { + fname = NULL; + lineno = 0; + } else { + fname = curFile->fname; + lineno = curFile->lineno; + } + + va_start(ap, fmt); + (void)fflush(stdout); + ParseVErrorInternal(stderr, fname, lineno, type, fmt, ap); + va_end(ap); + + if (debug_file != stderr && debug_file != stdout) { + va_start(ap, fmt); + ParseVErrorInternal(debug_file, fname, lineno, type, fmt, ap); + va_end(ap); + } +} + + +/* + * ParseMessage + * Parse a .info .warning or .error directive + * + * The input is the line minus the ".". We substitute + * variables, print the message and exit(1) (for .error) or just print + * a warning if the directive is malformed. + */ +static Boolean +ParseMessage(char *line) +{ + int mtype; + + switch(*line) { + case 'i': + mtype = 0; + break; + case 'w': + mtype = PARSE_WARNING; + break; + case 'e': + mtype = PARSE_FATAL; + break; + default: + Parse_Error(PARSE_WARNING, "invalid syntax: \".%s\"", line); + return FALSE; + } + + while (isalpha((u_char)*line)) + line++; + if (!isspace((u_char)*line)) + return FALSE; /* not for us */ + while (isspace((u_char)*line)) + line++; + + line = Var_Subst(NULL, line, VAR_CMD, 0); + Parse_Error(mtype, "%s", line); + free(line); + + if (mtype == PARSE_FATAL) { + /* Terminate immediately. */ + exit(1); + } + return TRUE; +} + +/*- + *--------------------------------------------------------------------- + * 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(void *pgnp, void *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, cgn); + if (specType == Not) + (void)Lst_AtEnd(cgn->parents, pgn); + pgn->unmade += 1; + if (DEBUG(PARSE)) { + fprintf(debug_file, "# ParseLinkSrc: added child %s - %s\n", pgn->name, cgn->name); + Targ_PrintNode(pgn, 0); + Targ_PrintNode(cgn, 0); + } + 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(void *gnp, void *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_FindNode(gn->name, TARG_NOHASH); + /* + * 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, cohort); + cohort->centurion = gn; + gn->unmade_cohorts += 1; + snprintf(cohort->cohort_num, sizeof cohort->cohort_num, "#%d", + gn->unmade_cohorts); + } 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); +} + +/*- + *--------------------------------------------------------------------- + * 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 + * + * 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, const char *src) +{ + GNode *gn = NULL; + static int wait_number = 0; + char wait_src[16]; + + 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, &op); + return; + } + if (parseKeywords[keywd].spec == Wait) { + /* + * We add a .WAIT node in the dependency list. + * After any dynamic dependencies (and filename globbing) + * have happened, it is given a dependency on the each + * previous child back to and previous .WAIT node. + * The next child won't be scheduled until the .WAIT node + * is built. + * We give each .WAIT node a unique name (mainly for diag). + */ + snprintf(wait_src, sizeof wait_src, ".WAIT_%u", ++wait_number); + gn = Targ_FindNode(wait_src, TARG_NOHASH); + gn->type = OP_WAIT | OP_PHONY | OP_DEPENDS | OP_NOTMAIN; + Lst_ForEach(targets, ParseLinkSrc, gn); + 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, bmake_strdup(src)); + /* + * Add the name to the .TARGETS variable as well, so the user can + * 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 != NULL) { + (void)Lst_AtEnd(predecessor->order_succ, gn); + (void)Lst_AtEnd(gn->order_pred, predecessor); + if (DEBUG(PARSE)) { + fprintf(debug_file, "# ParseDoSrc: added Order dependency %s - %s\n", + predecessor->name, gn->name); + Targ_PrintNode(predecessor, 0); + Targ_PrintNode(gn, 0); + } + } + /* + * 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. + */ + + /* Find/create the 'src' node and attach to all targets */ + gn = Targ_FindNode(src, TARG_CREATE); + if (tOp) { + gn->type |= tOp; + } else { + Lst_ForEach(targets, ParseLinkSrc, gn); + } + break; + } +} + +/*- + *----------------------------------------------------------------------- + * 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(void *gnp, void *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(void *path, void *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(void *path, void *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 */ + char *lstart = line; + + if (DEBUG(PARSE)) + fprintf(debug_file, "ParseDoDependency(%s)\n", line); + tOp = 0; + + specType = Not; + paths = NULL; + + curTargs = 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; + void *freeIt; + char *result; + + result = Var_Parse(cp, VAR_CMD, TRUE, &length, &freeIt); + if (freeIt) + free(freeIt); + cp += length-1; + } + } + + 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); + goto out; + } 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, lstart[0] == '.' ? "Unknown directive" + : "Need an operator"); + goto out; + } + *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"); + goto out; + } + + 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 + * .ERROR + * .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 NULL + */ + switch (specType) { + case ExPath: + if (paths == NULL) { + paths = Lst_Init(FALSE); + } + (void)Lst_AtEnd(paths, dirSearchPath); + break; + case Main: + if (!Lst_IsEmpty(create)) { + specType = Not; + } + break; + case Begin: + case End: + case dotError: + case Interrupt: + gn = Targ_FindNode(line, TARG_CREATE); + gn->type |= OP_NOTMAIN|OP_SPECIAL; + (void)Lst_AtEnd(targets, gn); + break; + case Default: + gn = Targ_NewGN(".DEFAULT"); + gn->type |= (OP_NOTMAIN|OP_TRANSFORM); + (void)Lst_AtEnd(targets, gn); + DEFAULT = gn; + break; + case NotParallel: + maxJobs = 1; + break; + case SingleShell: + compatMake = TRUE; + break; + case Order: + predecessor = NULL; + break; + default: + break; + } + } else if (strncmp(line, ".PATH", 5) == 0) { + /* + * .PATH<suffix> 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 == NULL) { + Parse_Error(PARSE_FATAL, + "Suffix '%s' not defined (yet)", + &line[5]); + goto out; + } else { + if (paths == NULL) { + paths = Lst_Init(FALSE); + } + (void)Lst_AtEnd(paths, 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, 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, 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 warning = FALSE; + + while (*cp && (ParseIsEscaped(lstart, cp) || + ((*cp != '!') && (*cp != ':')))) { + if (ParseIsEscaped(lstart, cp) || + (*cp != ' ' && *cp != '\t')) { + warning = TRUE; + } + cp++; + } + if (warning) { + 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, NULL); + curTargs = NULL; + + 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 dotError: + 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, lstart[0] == '.' ? "Unknown directive" + : "Missing dependency operator"); + goto out; + } + + cp++; /* Advance beyond operator */ + + Lst_ForEach(targets, ParseDoOp, &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, 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"); + goto out; + } + *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, 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, NULL); + } + if (specType == ExPath) + Dir_SetPATH(); + } else { + 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. + */ + for (; *cp && !isspace ((unsigned char)*cp); 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; + } + } + + 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); + goto out; + } + + while (!Lst_IsEmpty (sources)) { + gn = (GNode *)Lst_DeQueue(sources); + ParseDoSrc(tOp, gn->name); + } + Lst_Destroy(sources, NULL); + cp = line; + } else { + if (*cp) { + *cp = '\0'; + cp += 1; + } + + ParseDoSrc(tOp, line); + } + while (*cp && isspace ((unsigned char)*cp)) { + cp++; + } + line = cp; + } + } + + if (mainNode == NULL) { + /* + * 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, NULL); + } + +out: + if (curTargs) + Lst_Destroy(curTargs, NULL); +} + +/*- + *--------------------------------------------------------------------- + * 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 */ + char ch; + int level = 0; +#define ISEQOPERATOR(c) \ + (((c) == '+') || ((c) == ':') || ((c) == '?') || ((c) == '!')) + + /* + * Skip to variable name + */ + for (;(*line == ' ') || (*line == '\t'); line++) + continue; + + /* Scan for one of the assignment operators outside a variable expansion */ + while ((ch = *line++) != 0) { + if (ch == '(' || ch == '{') { + level++; + continue; + } + if (ch == ')' || ch == '}') { + level--; + continue; + } + if (level != 0) + continue; + while (ch == ' ' || ch == '\t') { + ch = *line++; + wasSpace = TRUE; + } + if (ch == '=') + return TRUE; + if (*line == '=' && ISEQOPERATOR(ch)) + return TRUE; + if (wasSpace) + return FALSE; + } + + return FALSE; +} + +/*- + *--------------------------------------------------------------------- + * 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 */ + int depth; + + /* + * Skip to variable name + */ + while ((*line == ' ') || (*line == '\t')) { + line++; + } + + /* + * Skip to operator character, nulling out whitespace as we go + * XXX Rather than counting () and {} we should look for $ and + * then expand the variable. + */ + for (depth = 0, cp = line + 1; depth != 0 || *cp != '='; cp++) { + if (*cp == '(' || *cp == '{') { + depth++; + continue; + } + if (*cp == ')' || *cp == '}') { + depth--; + continue; + } + if (depth == 0 && 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 *error; + + 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, &error); + Var_Set(line, res, ctxt, 0); + free(res); + + if (error) + Parse_Error(PARSE_WARNING, error, 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(); + } else if (strcmp(line, MAKE_JOB_PREFIX) == 0) { + Job_SetPrefix(); + } else if (strcmp(line, MAKE_EXPORTED) == 0) { + Var_Export(cp, 0); + } + 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(void *gnp, void *cmd) +{ + GNode *gn = (GNode *)gnp; + + /* Add to last (ie current) cohort for :: targets */ + if ((gn->type & OP_DOUBLEDEP) && !Lst_IsEmpty (gn->cohorts)) + gn = (GNode *)Lst_Datum(Lst_Last(gn->cohorts)); + + /* if target already supplied, ignore commands */ + 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(void *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 +Parse_include_file(char *file, Boolean isSystem, int silent) +{ + struct loadedfile *lf; + char *fullname; /* full pathname of file */ + char *newName; + char *prefEnd, *incdir; + int fd; + int i; + + /* + * 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 = file[0] == '/' ? bmake_strdup(file) : NULL; + + if (fullname == NULL && !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. + */ + + incdir = bmake_strdup(curFile->fname); + prefEnd = strrchr(incdir, '/'); + if (prefEnd != NULL) { + *prefEnd = '\0'; + /* Now do lexical processing of leading "../" on the filename */ + for (i = 0; strncmp(file + i, "../", 3) == 0; i += 3) { + prefEnd = strrchr(incdir + 1, '/'); + if (prefEnd == NULL || strcmp(prefEnd, "/..") == 0) + break; + *prefEnd = '\0'; + } + newName = str_concat(incdir, file + i, STR_ADDSLASH); + fullname = Dir_FindFile(newName, parseIncPath); + if (fullname == NULL) + fullname = Dir_FindFile(newName, dirSearchPath); + free(newName); + } + free(incdir); + + 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. + * If we have a suffix specific path we should use that. + */ + char *suff; + Lst suffPath = NULL; + + if ((suff = strrchr(file, '.'))) { + suffPath = Suff_GetPath(suff); + if (suffPath != NULL) { + fullname = Dir_FindFile(file, suffPath); + } + } + if (fullname == NULL) { + 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) { + if (!silent) + Parse_Error(PARSE_FATAL, "Could not find %s", file); + return; + } + + /* Actually open the file... */ + fd = open(fullname, O_RDONLY); + if (fd == -1) { + if (!silent) + Parse_Error(PARSE_FATAL, "Cannot open %s", fullname); + free(fullname); + return; + } + + /* load it */ + lf = loadfile(fullname, fd); + + /* Start reading from this file next */ + Parse_SetInput(fullname, 0, -1, loadedfile_nextbuf, lf); + curFile->lf = lf; +} + +static void +ParseDoInclude(char *line) +{ + char endc; /* the character which ends the file spec */ + char *cp; /* current position in file spec */ + 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 == '<') { + endc = '>'; + } else { + 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); + + Parse_include_file(file, endc == '>', silent); + free(file); +} + + +/*- + *--------------------------------------------------------------------- + * 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(const char *filename) +{ + char *slash, *dirname; + const char *pd, *pf; + int len; + + slash = strrchr(filename, '/'); + if (slash == NULL) { + Var_Set(".PARSEDIR", pd = curdir, VAR_GLOBAL, 0); + Var_Set(".PARSEFILE", pf = filename, VAR_GLOBAL, 0); + dirname= NULL; + } else { + len = slash - filename; + dirname = bmake_malloc(len + 1); + memcpy(dirname, filename, len); + dirname[len] = '\0'; + Var_Set(".PARSEDIR", pd = dirname, VAR_GLOBAL, 0); + Var_Set(".PARSEFILE", pf = slash + 1, VAR_GLOBAL, 0); + } + if (DEBUG(PARSE)) + fprintf(debug_file, "ParseSetParseFile: ${.PARSEDIR} = `%s' " + "${.PARSEFILE} = `%s'\n", pd, pf); + free(dirname); +} + +/* + * Track the makefiles we read - so makefiles can + * set dependencies on them. + * Avoid adding anything more than once. + */ + +static void +ParseTrackInput(const char *name) +{ + char *old; + char *fp = NULL; + size_t name_len = strlen(name); + + old = Var_Value(MAKE_MAKEFILES, VAR_GLOBAL, &fp); + if (old) { + /* does it contain name? */ + for (; old != NULL; old = strchr(old, ' ')) { + if (*old == ' ') + old++; + if (memcmp(old, name, name_len) == 0 + && (old[name_len] == 0 || old[name_len] == ' ')) + goto cleanup; + } + } + Var_Append (MAKE_MAKEFILES, name, VAR_GLOBAL); + cleanup: + if (fp) { + free(fp); + } +} + + +/*- + *--------------------------------------------------------------------- + * Parse_setInput -- + * Start Parsing from the given source + * + * 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_SetInput(const char *name, int line, int fd, + char *(*nextbuf)(void *, size_t *), void *arg) +{ + char *buf; + size_t len; + + if (name == NULL) + name = curFile->fname; + else + ParseTrackInput(name); + + if (DEBUG(PARSE)) + fprintf(debug_file, "Parse_SetInput: file %s, line %d, fd %d, nextbuf %p, arg %p\n", + name, line, fd, nextbuf, arg); + + if (fd == -1 && nextbuf == NULL) + /* sanity */ + return; + + if (curFile != NULL) + /* Save exiting file info */ + Lst_AtFront(includes, curFile); + + /* Allocate and fill in new structure */ + curFile = bmake_malloc(sizeof *curFile); + + /* + * 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. + */ + curFile->fname = name; + curFile->lineno = line; + curFile->first_lineno = line; + curFile->nextbuf = nextbuf; + curFile->nextbuf_arg = arg; + curFile->lf = NULL; + + assert(nextbuf != NULL); + + /* Get first block of input data */ + buf = curFile->nextbuf(curFile->nextbuf_arg, &len); + if (buf == NULL) { + /* Was all a waste of time ... */ + free(curFile); + return; + } + curFile->P_str = buf; + curFile->P_ptr = buf; + curFile->P_end = buf+len; + + curFile->cond_depth = Cond_save_depth(); + ParseSetParseFile(name); +} + +#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 *cp; /* current position in file spec */ + int done = 0; + int silent = (line[0] != 'i') ? 1 : 0; + char *file = &line[silent + 7]; + char *all_files; + + if (DEBUG(PARSE)) { + fprintf(debug_file, "ParseTraditionalInclude: %s\n", file); + } + + /* + * Skip over whitespace + */ + while (isspace((unsigned char)*file)) + file++; + + /* + * Substitute for any variables in the file name before trying to + * find the thing. + */ + all_files = Var_Subst(NULL, file, VAR_CMD, FALSE); + + if (*file == '\0') { + Parse_Error(PARSE_FATAL, + "Filename missing from \"include\""); + return; + } + + for (file = all_files; !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; + + Parse_include_file(file, FALSE, silent); + } + free(all_files); +} +#endif + +#ifdef GMAKEEXPORT +/*- + *--------------------------------------------------------------------- + * ParseGmakeExport -- + * Parse export <variable>=<value> + * + * And set the environment with it. + * + * Results: + * None + * + * Side Effects: + * None + *--------------------------------------------------------------------- + */ +static void +ParseGmakeExport(char *line) +{ + char *variable = &line[6]; + char *value; + + if (DEBUG(PARSE)) { + fprintf(debug_file, "ParseGmakeExport: %s\n", variable); + } + + /* + * Skip over whitespace + */ + while (isspace((unsigned char)*variable)) + variable++; + + for (value = variable; *value && *value != '='; value++) + continue; + + if (*value != '=') { + Parse_Error(PARSE_FATAL, + "Variable/Value missing from \"export\""); + return; + } + + /* + * Expand the value before putting it in the environment. + */ + value = Var_Subst(NULL, value, VAR_CMD, FALSE); + setenv(variable, value, 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(void) +{ + char *ptr; + size_t len; + + assert(curFile->nextbuf != NULL); + + /* get next input buffer, if any */ + ptr = curFile->nextbuf(curFile->nextbuf_arg, &len); + curFile->P_ptr = ptr; + curFile->P_str = ptr; + curFile->P_end = ptr + len; + curFile->lineno = curFile->first_lineno; + if (ptr != NULL) { + /* Iterate again */ + return CONTINUE; + } + + /* Ensure the makefile (or loop) didn't have mismatched conditionals */ + Cond_restore_depth(curFile->cond_depth); + + if (curFile->lf != NULL) { + loadedfile_destroy(curFile->lf); + curFile->lf = NULL; + } + + /* Dispose of curFile info */ + /* Leak curFile->fname because all the gnodes have pointers to it */ + free(curFile->P_str); + free(curFile); + + curFile = Lst_DeQueue(includes); + + if (curFile == NULL) { + /* We've run out of input */ + Var_Delete(".PARSEDIR", VAR_GLOBAL); + Var_Delete(".PARSEFILE", VAR_GLOBAL); + return DONE; + } + + if (DEBUG(PARSE)) + fprintf(debug_file, "ParseEOF: returning to file %s, line %d\n", + curFile->fname, curFile->lineno); + + /* Restore the PARSEDIR/PARSEFILE variables */ + ParseSetParseFile(curFile->fname); + return (CONTINUE); +} + +#define PARSE_RAW 1 +#define PARSE_SKIP 2 + +static char * +ParseGetLine(int flags, int *length) +{ + IFile *cf = curFile; + char *ptr; + char ch; + char *line; + char *line_end; + char *escaped; + char *comment; + char *tp; + + /* Loop through blank lines and comment lines */ + for (;;) { + cf->lineno++; + line = cf->P_ptr; + ptr = line; + line_end = line; + escaped = NULL; + comment = NULL; + for (;;) { + if (cf->P_end != NULL && ptr == cf->P_end) { + /* end of buffer */ + ch = 0; + break; + } + ch = *ptr; + if (ch == 0 || (ch == '\\' && ptr[1] == 0)) { + if (cf->P_end == NULL) + /* End of string (aka for loop) data */ + break; + if (cf->nextbuf != NULL) { + /* + * End of this buffer; return EOF and outer logic + * will get the next one. (eww) + */ + break; + } + Parse_Error(PARSE_FATAL, "Zero byte read from file"); + return NULL; + } + + if (ch == '\\') { + /* Don't treat next character as special, remember first one */ + if (escaped == NULL) + escaped = ptr; + if (ptr[1] == '\n') + cf->lineno++; + ptr += 2; + line_end = ptr; + continue; + } + if (ch == '#' && comment == NULL) { + /* Remember first '#' for comment stripping */ + /* Unless previous char was '[', as in modifier :[#] */ + if (!(ptr > line && ptr[-1] == '[')) + comment = line_end; + } + ptr++; + if (ch == '\n') + break; + if (!isspace((unsigned char)ch)) + /* We are not interested in trailing whitespace */ + line_end = ptr; + } + + /* Save next 'to be processed' location */ + cf->P_ptr = ptr; + + /* Check we have a non-comment, non-blank line */ + if (line_end == line || comment == line) { + if (ch == 0) + /* At end of file */ + return NULL; + /* Parse another line */ + continue; + } + + /* We now have a line of data */ + *line_end = 0; + + if (flags & PARSE_RAW) { + /* Leave '\' (etc) in line buffer (eg 'for' lines) */ + *length = line_end - line; + return line; + } + + if (flags & PARSE_SKIP) { + /* Completely ignore non-directives */ + if (line[0] != '.') + continue; + /* We could do more of the .else/.elif/.endif checks here */ + } + break; + } + + /* Brutally ignore anything after a non-escaped '#' in non-commands */ + if (comment != NULL && line[0] != '\t') { + line_end = comment; + *line_end = 0; + } + + /* If we didn't see a '\\' then the in-situ data is fine */ + if (escaped == NULL) { + *length = line_end - line; + return line; + } + + /* Remove escapes from '\n' and '#' */ + tp = ptr = escaped; + escaped = line; + for (; ; *tp++ = ch) { + ch = *ptr++; + if (ch != '\\') { + if (ch == 0) + break; + continue; + } + + ch = *ptr++; + if (ch == 0) { + /* Delete '\\' at end of buffer */ + tp--; + break; + } + + if (ch == '#' && line[0] != '\t') + /* Delete '\\' from before '#' on non-command lines */ + continue; + + if (ch != '\n') { + /* Leave '\\' in buffer for later */ + *tp++ = '\\'; + /* Make sure we don't delete an escaped ' ' from the line end */ + escaped = tp + 1; + continue; + } + + /* Escaped '\n' replace following whitespace with a single ' ' */ + while (ptr[0] == ' ' || ptr[0] == '\t') + ptr++; + ch = ' '; + } + + /* Delete any trailing spaces - eg from empty continuations */ + while (tp > escaped && isspace((unsigned char)tp[-1])) + tp--; + + *tp = 0; + *length = tp - line; + return line; +} + +/*- + *--------------------------------------------------------------------- + * ParseReadLine -- + * Read an entire line from the input file. Called only by Parse_File. + * + * Results: + * A line w/o its newline + * + * Side Effects: + * Only those associated with reading a character + *--------------------------------------------------------------------- + */ +static char * +ParseReadLine(void) +{ + char *line; /* Result */ + int lineLength; /* Length of result */ + int lineno; /* Saved line # */ + int rval; + + for (;;) { + line = ParseGetLine(0, &lineLength); + if (line == NULL) + return NULL; + + if (line[0] != '.') + return line; + + /* + * 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 { + line = ParseGetLine(PARSE_SKIP, &lineLength); + } while (line && Cond_Eval(line) != COND_PARSE); + if (line == NULL) + break; + continue; + case COND_PARSE: + continue; + case COND_INVALID: /* Not a conditional line */ + /* Check for .for loops */ + rval = For_Eval(line); + if (rval == 0) + /* Not a .for line */ + break; + if (rval < 0) + /* Syntax error - error printed, ignore line */ + continue; + /* Start of a .for loop */ + lineno = curFile->lineno; + /* Accumulate loop lines until matching .endfor */ + do { + line = ParseGetLine(PARSE_RAW, &lineLength); + if (line == NULL) { + Parse_Error(PARSE_FATAL, + "Unexpected end of file in for loop."); + break; + } + } while (For_Accum(line)); + /* Stash each iteration as a new 'input file' */ + For_Run(lineno); + /* Read next line from for-loop buffer */ + continue; + } + return (line); + } +} + +/*- + *----------------------------------------------------------------------- + * 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, 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 + * fd Open file to makefile to parse + * + * Results: + * None + * + * Side Effects: + * closes fd. + * 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, int fd) +{ + char *cp; /* pointer into the line */ + char *line; /* the line we're working on */ + struct loadedfile *lf; + + lf = loadfile(name, fd); + + inLine = FALSE; + fatals = 0; + + if (name == NULL) { + name = "(stdin)"; + } + + Parse_SetInput(name, 0, -1, loadedfile_nextbuf, lf); + curFile->lf = lf; + + do { + for (; (line = ParseReadLine()) != NULL; ) { + if (DEBUG(PARSE)) + fprintf(debug_file, "ParseReadLine (%d): '%s'\n", + curFile->lineno, line); + if (*line == '.') { + /* + * Lines that begin with the special character may be + * include or undef directives. + * On the other hand they can be suffix rules (.c.o: ...) + * or just dependencies for filenames that start '.'. + */ + 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); + continue; + } + 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); + continue; + } else if (strncmp(cp, "export", 6) == 0) { + for (cp += 6; isspace((unsigned char) *cp); cp++) + continue; + Var_Export(cp, 1); + continue; + } else if (strncmp(cp, "unexport", 8) == 0) { + Var_UnExport(cp); + continue; + } else if (strncmp(cp, "info", 4) == 0 || + strncmp(cp, "error", 5) == 0 || + strncmp(cp, "warning", 7) == 0) { + if (ParseMessage(cp)) + continue; + } + } + + if (*line == '\t') { + /* + * If a line starts with a tab, it can only hope to be + * a creation command. + */ + cp = line + 1; + shellCommand: + for (; isspace ((unsigned char)*cp); cp++) { + continue; + } + if (*cp) { + if (!inLine) + Parse_Error(PARSE_FATAL, + "Unassociated shell command \"%s\"", + cp); + /* + * 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 + */ + if (targets) { + cp = bmake_strdup(cp); + Lst_ForEach(targets, ParseAddCmd, cp); +#ifdef CLEANUP + Lst_AtEnd(targCmds, cp); +#endif + } + } + continue; + } + +#ifdef SYSVINCLUDE + 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); + continue; + } +#endif +#ifdef GMAKEEXPORT + if (strncmp(line, "export", 6) == 0 && + isspace((unsigned char) line[6]) && + strchr(line, ':') == NULL) { + /* + * It's a Gmake "export". + */ + ParseGmakeExport(line); + continue; + } +#endif + if (Parse_IsVar(line)) { + ParseFinishLine(); + Parse_DoVar(line, VAR_GLOBAL); + continue; + } + +#ifndef POSIX + /* + * 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. + */ + cp = line; + if (isspace((unsigned char) line[0])) { + while ((*cp != '\0') && isspace((unsigned char) *cp)) + cp++; + while (*cp && (ParseIsEscaped(line, cp) || + (*cp != ':') && (*cp != '!'))) { + cp++; + } + if (*cp == '\0') { + if (inLine) { + Parse_Error(PARSE_WARNING, + "Shell command needs a leading tab"); + goto shellCommand; + } + } + } +#endif + ParseFinishLine(); + + /* + * For some reason - probably to make the parser impossible - + * a ';' can be used to separate commands from dependencies. + * Attempt to avoid ';' inside substitution patterns. + */ + { + int level = 0; + + for (cp = line; *cp != 0; cp++) { + if (*cp == '\\' && cp[1] != 0) { + cp++; + continue; + } + if (*cp == '$' && + (cp[1] == '(' || cp[1] == '{')) { + level++; + continue; + } + if (level > 0) { + if (*cp == ')' || *cp == '}') { + level--; + continue; + } + } else if (*cp == ';') { + break; + } + } + } + if (*cp != 0) + /* Terminate the dependency list at the ';' */ + *cp++ = 0; + else + cp = NULL; + + /* + * 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... + */ + line = Var_Subst(NULL, line, VAR_CMD, TRUE); + + /* + * Need a non-circular list for the target nodes + */ + if (targets) + Lst_Destroy(targets, NULL); + + targets = Lst_Init(FALSE); + inLine = TRUE; + + ParseDoDependency(line); + free(line); + + /* If there were commands after a ';', add them now */ + if (cp != NULL) { + goto shellCommand; + } + } + /* + * Reached EOF, but it may be just EOF of an include file... + */ + } while (ParseEOF() == CONTINUE); + + if (fatals) { + (void)fflush(stdout); + (void)fprintf(stderr, + "%s: Fatal errors encountered -- cannot continue", + progname); + PrintOnError(NULL, NULL); + exit(1); + } +} + +/*- + *--------------------------------------------------------------------- + * Parse_Init -- + * initialize the parsing module + * + * Results: + * none + * + * Side Effects: + * the parseIncPath list is initialized... + *--------------------------------------------------------------------- + */ +void +Parse_Init(void) +{ + mainNode = NULL; + 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, NULL); + Lst_Destroy(defIncPath, Dir_Destroy); + Lst_Destroy(sysIncPath, Dir_Destroy); + Lst_Destroy(parseIncPath, Dir_Destroy); + Lst_Destroy(includes, NULL); /* 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 == NULL) { + Punt("no target to make."); + /*NOTREACHED*/ + } else if (mainNode->type & OP_DOUBLEDEP) { + (void)Lst_AtEnd(mainList, mainNode); + Lst_Concat(mainList, mainNode->cohorts, LST_CONCNEW); + } + else + (void)Lst_AtEnd(mainList, 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 = curFile->fname; + gn->lineno = curFile->lineno; +} diff --git a/20120704/pathnames.h b/20120704/pathnames.h new file mode 100644 index 0000000..9c597b1 --- /dev/null +++ b/20120704/pathnames.h @@ -0,0 +1,62 @@ +/* $NetBSD: pathnames.h,v 1.17 2009/04/11 09:41:18 apb 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.13 2009/08/26 23:43:42 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 <paths.h> +#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 +#ifndef _PATH_TMP +#define _PATH_TMP "/tmp/" /* with trailing slash */ +#endif diff --git a/20120704/ranlib.h b/20120704/ranlib.h new file mode 100644 index 0000000..503c7c1 --- /dev/null +++ b/20120704/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.5 2005/11/01 02:35:15 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 "!<arch>\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/20120704/realpath.c b/20120704/realpath.c new file mode 100644 index 0000000..1ef2cd8 --- /dev/null +++ b/20120704/realpath.c @@ -0,0 +1,196 @@ +/* $Id: realpath.c,v 1.2 2010/04/21 17:47:49 sjg Exp $ */ +/* from: $NetBSD: getcwd.c,v 1.45 2007/10/26 19:48:14 christos Exp $ */ + +/* + * Copyright (c) 1989, 1991, 1993, 1995 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce 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. + */ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#ifndef HAVE_REALPATH + +#include <sys/cdefs.h> +#include <sys/param.h> +#include <sys/stat.h> + +#include <errno.h> +#ifdef HAVE_STDLIB_H +# include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +# include <string.h> +#endif +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +/* + * char *realpath(const char *path, char resolved[MAXPATHLEN]); + * + * Find the real name of path, by removing all ".", ".." and symlink + * components. Returns (resolved) on success, or (NULL) on failure, + * in which case the path which caused trouble is left in (resolved). + */ +char * +realpath(const char *path, char *resolved) +{ + struct stat sb; + int idx = 0, n, nlnk = 0; + const char *q; + char *p, wbuf[2][MAXPATHLEN]; + size_t len; + + if (!path || !resolved || path == resolved) + return (NULL); + + /* + * Build real path one by one with paying an attention to ., + * .. and symbolic link. + */ + + /* + * `p' is where we'll put a new component with prepending + * a delimiter. + */ + p = resolved; + + if (*path == 0) { + *p = 0; + errno = ENOENT; + return (NULL); + } + + /* If relative path, start from current working directory. */ + if (*path != '/') { + /* check for resolved pointer to appease coverity */ + if (resolved && getcwd(resolved, MAXPATHLEN) == NULL) { + p[0] = '.'; + p[1] = 0; + return (NULL); + } + len = strlen(resolved); + if (len > 1) + p += len; + } + +loop: + /* Skip any slash. */ + while (*path == '/') + path++; + + if (*path == 0) { + if (p == resolved) + *p++ = '/'; + *p = 0; + return (resolved); + } + + /* Find the end of this component. */ + q = path; + do + q++; + while (*q != '/' && *q != 0); + + /* Test . or .. */ + if (path[0] == '.') { + if (q - path == 1) { + path = q; + goto loop; + } + if (path[1] == '.' && q - path == 2) { + /* Trim the last component. */ + if (p != resolved) + while (*--p != '/') + ; + path = q; + goto loop; + } + } + + /* Append this component. */ + if (p - resolved + 1 + q - path + 1 > MAXPATHLEN) { + errno = ENAMETOOLONG; + if (p == resolved) + *p++ = '/'; + *p = 0; + return (NULL); + } + p[0] = '/'; + memcpy(&p[1], path, + /* LINTED We know q > path. */ + q - path); + p[1 + q - path] = 0; + + /* + * If this component is a symlink, toss it and prepend link + * target to unresolved path. + */ + if (lstat(resolved, &sb) == -1) { + return (NULL); + } + if (S_ISLNK(sb.st_mode)) { + if (nlnk++ >= MAXSYMLINKS) { + errno = ELOOP; + return (NULL); + } + n = readlink(resolved, wbuf[idx], sizeof(wbuf[0]) - 1); + if (n < 0) + return (NULL); + if (n == 0) { + errno = ENOENT; + return (NULL); + } + + /* Append unresolved path to link target and switch to it. */ + if (n + (len = strlen(q)) + 1 > sizeof(wbuf[0])) { + errno = ENAMETOOLONG; + return (NULL); + } + memcpy(&wbuf[idx][n], q, len + 1); + path = wbuf[idx]; + idx ^= 1; + + /* If absolute symlink, start from root. */ + if (*path == '/') + p = resolved; + goto loop; + } + if (*q == '/' && !S_ISDIR(sb.st_mode)) { + errno = ENOTDIR; + return (NULL); + } + + /* Advance both resolved and unresolved path. */ + p += 1 + q - path; + path = q; + goto loop; +} +#endif diff --git a/20120704/setenv.c b/20120704/setenv.c new file mode 100644 index 0000000..efd0f74 --- /dev/null +++ b/20120704/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.5 1996/09/04 22:10:42 sjg Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +/* + * __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/20120704/sigcompat.c b/20120704/sigcompat.c new file mode 100644 index 0000000..608538d --- /dev/null +++ b/20120704/sigcompat.c @@ -0,0 +1,325 @@ +/* + * 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 <sjg@crufty.net> + */ +/* + * @(#)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 <signal.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#if defined(sun) && !(defined(__svr4__) || defined(__SVR4)) +# define NO_SIGCOMPAT +#endif +#if defined(__MINT__) +# 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.23 2011/02/14 00:07:11 sjg Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#undef signal +#include <stdio.h> +#include <string.h> +#include <sys/param.h> +#include <sys/cdefs.h> +#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 +# if defined(__hpux__) || defined(__hpux) +# define MASK_T long +# else +# define MASK_T int +# endif +#endif +/* I just hate HPsUX */ +#if (defined(__HPUX_VERSION) && __HPUX_VERSION > 9) || defined(__hpux) +# 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(int sig, SIG_HDLR(*handler)(int)))(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(int sig, SIG_HDLR(*handler)(int)))(int) +{ + extern SIG_HDLR(*Signal(int, void (*)(int)))(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(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(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_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_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(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(int signo, struct sigvec *sv, struct sigvec *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(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/20120704/sprite.h b/20120704/sprite.h new file mode 100644 index 0000000..6ec4fe2 --- /dev/null +++ b/20120704/sprite.h @@ -0,0 +1,116 @@ +/* $NetBSD: sprite.h,v 1.11 2009/01/23 21:26:30 dsl 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 + +#endif /* _SPRITE */ diff --git a/20120704/str.c b/20120704/str.c new file mode 100644 index 0000000..bc324b8 --- /dev/null +++ b/20120704/str.c @@ -0,0 +1,508 @@ +/* $NetBSD: str.c,v 1.34 2012/03/03 23:16:47 dholland 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.34 2012/03/03 23:16:47 dholland Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)str.c 5.8 (Berkeley) 6/1/90"; +#else +__RCSID("$NetBSD: str.c,v 1.34 2012/03/03 23:16:47 dholland 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 = bmake_malloc((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. + * + * If expand is TRUE, quotes are removed and escape sequences + * such as \r, \t, etc... are expanded. + * + * returns -- + * Pointer to the array of pointers to the words. + * Memory containing the actual words in *buffer. + * Both of these must be free'd by the caller. + * Number of words in *store_argc. + */ +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 = bmake_malloc((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 = bmake_malloc(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) { + if (!expand) { + start = t; + *t++ = ch; + } else + start = t + 1; + p++; + inquote = '\0'; + 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 **)bmake_realloc(argv, + (argmax + 1) * sizeof(char *)); + } + argv[argc++] = start; + start = NULL; + if (ch == '\n' || ch == '\0') { + if (expand && inquote) { + free(argv); + free(*buffer); + *buffer = NULL; + return NULL; + } + goto done; + } + continue; + case '\\': + if (!expand) { + if (!start) + start = t; + *t++ = '\\'; + if (*(p+1) == '\0') /* catch '\' at end of line */ + continue; + 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). + * + * XXX this function does not detect or report malformed patterns. + * + * 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, pat); + /* skip the % */ + pat = m + 1; + } + + /* Copy the pattern */ + Buf_AddBytes(buf, len, src); + + /* append the rest */ + Buf_AddBytes(buf, strlen(pat), pat); +} diff --git a/20120704/stresep.c b/20120704/stresep.c new file mode 100644 index 0000000..b1b5714 --- /dev/null +++ b/20120704/stresep.c @@ -0,0 +1,89 @@ +/* $NetBSD: stresep.c,v 1.2 2007/12/06 22:07:07 seb 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. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <sys/cdefs.h> + +#include <assert.h> +#include <string.h> + +#if !defined(HAVE_STRESEP) +char * stresep(char **stringp, const char *delim, int esc); +/* + * Get next token from string *stringp, where tokens are possibly-empty + * strings separated by characters from delim. If esc is not NUL, then + * the characters followed by esc are ignored and are not taken into account + * when splitting the string. + * + * Writes NULs into the string at *stringp to end tokens. + * delim need not remain constant from call to call. + * On return, *stringp points past the last NUL written (if there might + * be further tokens), or is NULL (if there are definitely no more tokens). + * + * If *stringp is NULL, stresep returns NULL. + */ +char * +stresep(char **stringp, const char *delim, int esc) +{ + char *s; + const char *spanp; + int c, sc; + char *tok; + + if (stringp == NULL || delim == NULL) + return NULL; + + if ((s = *stringp) == NULL) + return NULL; + for (tok = s;;) { + c = *s++; + while (esc != '\0' && c == esc) { + (void)strcpy(s - 1, s); + c = *s++; + } + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *stringp = s; + return tok; + } + } while (sc != 0); + } +} +#endif + diff --git a/20120704/strlcpy.c b/20120704/strlcpy.c new file mode 100644 index 0000000..b59b2f4 --- /dev/null +++ b/20120704/strlcpy.c @@ -0,0 +1,63 @@ +/* $NetBSD: strlcpy.c,v 1.3 2007/06/04 18:19:27 christos Exp $ */ +/* $OpenBSD: strlcpy.c,v 1.7 2003/04/12 21:56:39 millert Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE + * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#ifndef HAVE_STRLCPY + +#include <sys/cdefs.h> + +#include <sys/types.h> +#include <string.h> + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t +strlcpy(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + + if (!dst || !src) + return 0; + + /* Copy as many bytes as will fit */ + if (n != 0 && --n != 0) { + do { + if ((*d++ = *s++) == 0) + break; + } while (--n != 0); + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} +#endif diff --git a/20120704/strlist.c b/20120704/strlist.c new file mode 100644 index 0000000..3fb2f7d --- /dev/null +++ b/20120704/strlist.c @@ -0,0 +1,93 @@ +/* $NetBSD: strlist.c,v 1.4 2009/01/24 11:59:39 dsl Exp $ */ + +/*- + * Copyright (c) 2008 - 2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David Laight. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce 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 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: strlist.c,v 1.4 2009/01/24 11:59:39 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: strlist.c,v 1.4 2009/01/24 11:59:39 dsl Exp $"); +#endif /* not lint */ +#endif + +#include <stddef.h> +#include <stdlib.h> +#include "strlist.h" +#include "make_malloc.h" + +void +strlist_init(strlist_t *sl) +{ + sl->sl_num = 0; + sl->sl_max = 0; + sl->sl_items = NULL; +} + +void +strlist_clean(strlist_t *sl) +{ + char *str; + int i; + + STRLIST_FOREACH(str, sl, i) + free(str); + free(sl->sl_items); + + sl->sl_num = 0; + sl->sl_max = 0; + sl->sl_items = NULL; +} + +void +strlist_add_str(strlist_t *sl, char *str, unsigned int info) +{ + unsigned int n; + strlist_item_t *items; + + if (str == NULL) + return; + + n = sl->sl_num + 1; + sl->sl_num = n; + items = sl->sl_items; + if (n >= sl->sl_max) { + items = bmake_realloc(items, (n + 7) * sizeof *sl->sl_items); + sl->sl_items = items; + sl->sl_max = n + 6; + } + items += n - 1; + items->si_str = str; + items->si_info = info; + items[1].si_str = NULL; /* STRLIST_FOREACH() terminator */ +} diff --git a/20120704/strlist.h b/20120704/strlist.h new file mode 100644 index 0000000..2fc049e --- /dev/null +++ b/20120704/strlist.h @@ -0,0 +1,62 @@ +/* $NetBSD: strlist.h,v 1.3 2009/01/16 21:15:34 dsl Exp $ */ + +/*- + * Copyright (c) 2008 - 2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David Laight. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce 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 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 _STRLIST_H +#define _STRLIST_H + +typedef struct { + char *si_str; + unsigned int si_info; +} strlist_item_t; + +typedef struct { + unsigned int sl_num; + unsigned int sl_max; + strlist_item_t *sl_items; +} strlist_t; + +void strlist_init(strlist_t *); +void strlist_clean(strlist_t *); +void strlist_add_str(strlist_t *, char *, unsigned int); + +#define strlist_num(sl) ((sl)->sl_num) +#define strlist_str(sl, n) ((sl)->sl_items[n].si_str) +#define strlist_info(sl, n) ((sl)->sl_items[n].si_info) +#define strlist_set_info(sl, n, v) ((void)((sl)->sl_items[n].si_info = (v))) + +#define STRLIST_FOREACH(v, sl, index) \ + if ((sl)->sl_items != NULL) \ + for (index = 0; (v = strlist_str(sl, index)) != NULL; index++) + +#endif /* _STRLIST_H */ diff --git a/20120704/suff.c b/20120704/suff.c new file mode 100644 index 0000000..6abdeb0 --- /dev/null +++ b/20120704/suff.c @@ -0,0 +1,2653 @@ +/* $NetBSD: suff.c,v 1.69 2011/09/29 23:38:04 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.69 2011/09/29 23:38:04 sjg Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)suff.c 8.4 (Berkeley) 3/21/94"; +#else +__RCSID("$NetBSD: suff.c,v 1.69 2011/09/29 23:38:04 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<suffix>: 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 NULL + * if the target had no implicit sources. + * + * Suff_FindPath Return the appropriate path to search in + * order to find the node. + */ + +#include <stdio.h> +#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 const char *SuffStrIsPrefix(const char *, const char *); +static char *SuffSuffIsSuffix(const Suff *, const SuffixCmpData *); +static int SuffSuffIsSuffixP(const void *, const void *); +static int SuffSuffHasNameP(const void *, const void *); +static int SuffSuffIsPrefix(const void *, const void *); +static int SuffGNHasNameP(const void *, const void *); +static void SuffUnRef(void *, void *); +static void SuffFree(void *); +static void SuffInsert(Lst, Suff *); +static void SuffRemove(Lst, Suff *); +static Boolean SuffParseTransform(char *, Suff **, Suff **); +static int SuffRebuildGraph(void *, void *); +static int SuffScanTargets(void *, void *); +static int SuffAddSrc(void *, void *); +static int SuffRemoveSrc(Lst); +static void SuffAddLevel(Lst, Src *); +static Src *SuffFindThem(Lst, Lst); +static Src *SuffFindCmds(Src *, Lst); +static void SuffExpandChildren(LstNode, GNode *); +static void SuffExpandWildcards(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(void *, void *); +static int SuffPrintSuff(void *, void *); +static int SuffPrintTrans(void *, void *); + + /*************** 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 const char * +SuffStrIsPrefix(const char *pref, const 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(const Suff *s, const 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(const void *s, const void *sd) +{ + return(!SuffSuffIsSuffix(s, 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(const void *s, const void *sname) +{ + return (strcmp(sname, ((const 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(const void *s, const void *str) +{ + return SuffStrIsPrefix(((const Suff *)s)->name, str) == NULL; +} + +/*- + *----------------------------------------------------------------------- + * 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(const void *gn, const void *name) +{ + return (strcmp(name, ((const GNode *)gn)->name)); +} + + /*********** Maintenance Functions ************/ + +static void +SuffUnRef(void *lp, void *sp) +{ + Lst l = (Lst) lp; + + LstNode ln = Lst_Member(l, sp); + if (ln != NULL) { + 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(void *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, NULL); + Lst_Destroy(s->children, NULL); + Lst_Destroy(s->parents, NULL); + 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(l, s); + if (s->refCount == 0) { + SuffUnRef(sufflist, s); + SuffFree(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)) != NULL) { + s2 = (Suff *)Lst_Datum(ln); + if (s2->sNum >= s->sNum) { + break; + } + } + + Lst_Close(l); + if (DEBUG(SUFF)) { + fprintf(debug_file, "inserting %s(%d)...", s->name, s->sNum); + } + if (ln == NULL) { + if (DEBUG(SUFF)) { + fprintf(debug_file, "at end of list\n"); + } + (void)Lst_AtEnd(l, s); + s->refCount++; + (void)Lst_AtEnd(s->ref, l); + } else if (s2->sNum != s->sNum) { + if (DEBUG(SUFF)) { + fprintf(debug_file, "before %s(%d)\n", s2->name, s2->sNum); + } + (void)Lst_InsertBefore(l, ln, s); + s->refCount++; + (void)Lst_AtEnd(s->ref, l); + } else if (DEBUG(SUFF)) { + fprintf(debug_file, "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 = NULL; + singleLn = NULL; + + /* + * 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 == NULL) { + srcLn = Lst_Find(sufflist, str, SuffSuffIsPrefix); + } else { + srcLn = Lst_FindFrom(sufflist, Lst_Succ(srcLn), str, + SuffSuffIsPrefix); + } + if (srcLn == NULL) { + /* + * Ran out of source suffixes -- no such rule + */ + if (singleLn != NULL) { + /* + * 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, str2, SuffSuffHasNameP); + if (targLn != NULL) { + *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, line, SuffGNHasNameP); + if (ln == NULL) { + /* + * 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, 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, NULL); + Lst_Destroy(gn->children, NULL); + 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)) { + fprintf(debug_file, "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(void *gnp, void *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)) { + fprintf(debug_file, "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)) { + fprintf(debug_file, "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(void *transformp, void *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 = UNCONST(SuffStrIsPrefix(s->name, transform->name)); + if (cp != NULL) { + ln = Lst_Find(sufflist, cp, SuffSuffHasNameP); + if (ln != NULL) { + /* + * 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, transform->name, SuffSuffHasNameP); + /* + * Replace the start of the target suffix + */ + cp[1] = s->name[0]; + if (ln != NULL) { + /* + * 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(void *targetp, void *gsp) +{ + GNode *target = (GNode *)targetp; + GNodeSuff *gs = (GNodeSuff *)gsp; + Suff *s, *t; + char *ptr; + + if (*gs->gn == NULL && gs->r && (target->type & OP_NOTARGET) == 0) { + *gs->gn = target; + Targ_SetMain(target); + return 1; + } + + if ((unsigned int)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 = NULL; + Targ_SetMain(NULL); + } + Lst_Destroy(target->children, NULL); + target->children = Lst_Init(FALSE); + target->type = OP_TRANSFORM; + /* + * link the two together in the proper relationship and order + */ + if (DEBUG(SUFF)) { + fprintf(debug_file, "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, str, SuffSuffHasNameP); + if (ln == NULL) { + s = bmake_malloc(sizeof(Suff)); + + s->name = bmake_strdup(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, 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, &gs); + /* + * Look for any existing transformations from or to this suffix. + * XXX: Only do this after a Suff_ClearSuffixes? + */ + Lst_ForEach(transforms, SuffRebuildGraph, s); + } +} + +/*- + *----------------------------------------------------------------------- + * Suff_GetPath -- + * Return the search path for the given suffix, if it's defined. + * + * Results: + * The searchPath for the desired suffix or NULL if the suffix isn't + * defined. + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +Lst +Suff_GetPath(char *sname) +{ + LstNode ln; + Suff *s; + + ln = Lst_Find(sufflist, sname, SuffSuffHasNameP); + if (ln == NULL) { + return NULL; + } 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)) != NULL) { + 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, sname, SuffSuffHasNameP); + if (ln != NULL) { + 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, sname, SuffSuffHasNameP); + if (ln != NULL) { + 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(void *sp, void *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 = bmake_malloc(sizeof(Src)); + s2->file = bmake_strdup(targ->pref); + s2->pref = targ->pref; + s2->parent = targ; + s2->node = NULL; + s2->suff = s; + s->refCount++; + s2->children = 0; + targ->children += 1; + (void)Lst_AtEnd(ls->l, s2); +#ifdef DEBUG_SRC + s2->cp = Lst_Init(FALSE); + Lst_AtEnd(targ->cp, s2); + fprintf(debug_file, "1 add %x %x to %x:", targ, s2, ls->l); + Lst_ForEach(ls->l, PrintAddr, NULL); + fprintf(debug_file, "\n"); +#endif + } + s2 = bmake_malloc(sizeof(Src)); + s2->file = str_concat(targ->pref, s->name, 0); + s2->pref = targ->pref; + s2->parent = targ; + s2->node = NULL; + s2->suff = s; + s->refCount++; + s2->children = 0; + targ->children += 1; + (void)Lst_AtEnd(ls->l, s2); +#ifdef DEBUG_SRC + s2->cp = Lst_Init(FALSE); + Lst_AtEnd(targ->cp, s2); + fprintf(debug_file, "2 add %x %x to %x:", targ, s2, ls->l); + Lst_ForEach(ls->l, PrintAddr, NULL); + fprintf(debug_file, "\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, &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 + fprintf(debug_file, "cleaning %lx: ", (unsigned long) l); + Lst_ForEach(l, PrintAddr, NULL); + fprintf(debug_file, "\n"); +#endif + + + while ((ln = Lst_Next(l)) != NULL) { + 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, s); + if (ln != NULL) + Lst_Remove(s->parent->cp, ln); +#endif + --s->parent->children; + } +#ifdef DEBUG_SRC + fprintf(debug_file, "free: [l=%x] p=%x %d\n", l, s, s->children); + Lst_Destroy(s->cp, NULL); +#endif + Lst_Remove(l, ln); + free(s); + t |= 1; + Lst_Close(l); + return TRUE; + } +#ifdef DEBUG_SRC + else { + fprintf(debug_file, "keep: [l=%x] p=%x %d: ", l, s, s->children); + Lst_ForEach(s->cp, PrintAddr, NULL); + fprintf(debug_file, "\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)) { + fprintf(debug_file, "\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) != NULL) { +#ifdef DEBUG_SRC + fprintf(debug_file, "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 + fprintf(debug_file, "remove %x from %x\n", s, srcs); +#endif + free(ptr); + break; + } + + if (DEBUG(SUFF)) { + fprintf(debug_file, "not there\n"); + } + + SuffAddLevel(srcs, s); + Lst_AtEnd(slst, s); + } + + if (DEBUG(SUFF) && rs) { + fprintf(debug_file, "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 NULL 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 == NULL) { + Lst_Close(t->children); + return NULL; + } + s = (GNode *)Lst_Datum(ln); + + if (s->type & OP_OPTIONAL && Lst_IsEmpty(t->commands)) { + /* + * We haven't looked to see if .OPTIONAL files exist yet, so + * don't use one as the implicit source. + * This allows us to use .OPTIONAL in .depend files so make won't + * complain "don't know how to make xxx.h' when a dependent file + * has been moved/deleted. + */ + continue; + } + + 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, &cp[prefLen], SuffSuffHasNameP); + if (ln == NULL) + 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, targ->suff) != NULL) + 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 = bmake_malloc(sizeof(Src)); + ret->file = bmake_strdup(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); + fprintf(debug_file, "3 add %x %x\n", targ, ret); + Lst_AtEnd(targ->cp, ret); +#endif + Lst_AtEnd(slst, ret); + if (DEBUG(SUFF)) { + fprintf(debug_file, "\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: + * cln 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 void +SuffExpandChildren(LstNode cln, GNode *pgn) +{ + GNode *cgn = (GNode *)Lst_Datum(cln); + GNode *gn; /* New source 8) */ + char *cp; /* Expanded value */ + + if (!Lst_IsEmpty(cgn->order_pred) || !Lst_IsEmpty(cgn->order_succ)) + /* It is all too hard to process the result of .ORDER */ + return; + + if (cgn->type & OP_WAIT) + /* Ignore these (& OP_PHONY ?) */ + return; + + /* + * 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) { + SuffExpandWildcards(cln, pgn); + return; + } + + if (DEBUG(SUFF)) { + fprintf(debug_file, "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, 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; + void *freeIt; + + junk = Var_Parse(cp, pgn, TRUE, &len, &freeIt); + if (junk != var_Error) { + cp += len - 1; + } + + if (freeIt) + free(freeIt); + } 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, 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)) { + fprintf(debug_file, "%s...", gn->name); + } + /* Add gn to the parents child list before the original child */ + (void)Lst_InsertBefore(pgn->children, cln, gn); + (void)Lst_AtEnd(gn->parents, pgn); + pgn->unmade++; + /* Expand wildcards on new node */ + SuffExpandWildcards(Lst_Prev(cln), pgn); + } + Lst_Destroy(members, NULL); + + /* + * Free the result + */ + free(cp); + } + if (DEBUG(SUFF)) { + fprintf(debug_file, "\n"); + } + + /* + * Now the source is expanded, remove it from the list of children to + * keep it from being processed. + */ + pgn->unmade--; + Lst_Remove(pgn->children, cln); + Lst_Remove(cgn->parents, Lst_Member(cgn->parents, pgn)); +} + +static void +SuffExpandWildcards(LstNode cln, GNode *pgn) +{ + GNode *cgn = (GNode *)Lst_Datum(cln); + GNode *gn; /* New source 8) */ + char *cp; /* Expanded value */ + Lst explist; /* List of expansions */ + + if (!Dir_HasWildcards(cgn->name)) + return; + + /* + * 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)) { + fprintf(debug_file, "%s...", cp); + } + gn = Targ_FindNode(cp, TARG_CREATE); + + /* Add gn to the parents child list before the original child */ + (void)Lst_InsertBefore(pgn->children, cln, gn); + (void)Lst_AtEnd(gn->parents, pgn); + pgn->unmade++; + } + + /* + * Nuke what's left of the list + */ + Lst_Destroy(explist, NULL); + + if (DEBUG(SUFF)) { + fprintf(debug_file, "\n"); + } + + /* + * Now the source is expanded, remove it from the list of children to + * keep it from being processed. + */ + pgn->unmade--; + Lst_Remove(pgn->children, cln); + Lst_Remove(cgn->parents, Lst_Member(cgn->parents, pgn)); +} + +/*- + *----------------------------------------------------------------------- + * 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, &sd, SuffSuffIsSuffixP); + + if (DEBUG(SUFF)) { + fprintf(debug_file, "Wildcard expanding \"%s\"...", gn->name); + } + if (ln != NULL) + 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)) { + fprintf(debug_file, "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, sGn); + (void)Lst_AtEnd(sGn->parents, tGn); + tGn->unmade += 1; + + /* + * Locate the transformation rule itself + */ + tname = str_concat(s->name, t->name, 0); + ln = Lst_Find(transforms, tname, SuffGNHasNameP); + free(tname); + + if (ln == NULL) { + /* + * 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)) { + fprintf(debug_file, "\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 + */ + for (ln = Lst_Succ(ln); ln != NULL; ln = nln) { + nln = Lst_Succ(ln); + SuffExpandChildren(ln, tGn); + } + + /* + * 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, 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, mem); + (void)Lst_AtEnd(mem->parents, 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)) { + fprintf(debug_file, "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 != NULL) { + /* + * Got one -- apply it + */ + if (!SuffApplyTransform(gn, mem, (Suff *)Lst_Datum(ln), ms) && + DEBUG(SUFF)) + { + fprintf(debug_file, "\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 != NULL) { + /* + * Look for next possible suffix... + */ + ln = Lst_FindFrom(sufflist, ln, &sd, SuffSuffIsSuffixP); + + if (ln != NULL) { + int prefLen; /* Length of the prefix */ + + /* + * Allocate a Src structure to which things can be transformed + */ + targ = bmake_malloc(sizeof(Src)); + targ->file = bmake_strdup(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 = bmake_malloc(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, 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)) { + fprintf(debug_file, "\tNo known suffix on %s. Using .NULL suffix\n", gn->name); + } + + targ = bmake_malloc(sizeof(Src)); + targ->file = bmake_strdup(gn->name); + targ->suff = suffNull; + targ->suff->refCount++; + targ->node = gn; + targ->parent = NULL; + targ->children = 0; + targ->pref = bmake_strdup(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)) + fprintf(debug_file, "not "); + } + + if (DEBUG(SUFF)) + fprintf(debug_file, "adding suffix rules\n"); + + (void)Lst_AtEnd(targs, 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. + */ + for (ln = Lst_First(gn->children); ln != NULL; ln = nln) { + nln = Lst_Succ(ln); + SuffExpandChildren(ln, gn); + } + + if (targ == NULL) { + if (DEBUG(SUFF)) { + fprintf(debug_file, "\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, bottom) == NULL) { + Lst_AtEnd(slst, 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 == NULL) { + 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 == NULL) { + 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, bottom) == NULL) + Lst_AtEnd(slst, 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) { + /* + * If dependencies already found, no need to do it again... + */ + return; + } else { + gn->type |= OP_DEPS_FOUND; + } + /* + * Make sure we have these set, may get revised below. + */ + Var_Set(TARGET, gn->path ? gn->path : gn->name, gn, 0); + Var_Set(PREFIX, gn->name, gn, 0); + if (gn->type & OP_PHONY) { + /* + * If this is a .PHONY target, we do not apply suffix rules. + */ + return; + } + if (DEBUG(SUFF)) { + fprintf(debug_file, "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, LIBSUFF, SuffSuffHasNameP); + if (gn->suffix) + gn->suffix->refCount--; + if (ln != NULL) { + 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, name, SuffSuffHasNameP); + if (ln != NULL) { + 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 = bmake_malloc(sizeof(Suff)); + + suffNull->name = bmake_strdup(""); + 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, NULL); + Lst_Destroy(transforms, NULL); +#endif +} + + +/********************* DEBUGGING FUNCTIONS **********************/ + +static int SuffPrintName(void *s, void *dummy) +{ + fprintf(debug_file, "%s ", ((Suff *)s)->name); + return (dummy ? 0 : 0); +} + +static int +SuffPrintSuff(void *sp, void *dummy) +{ + Suff *s = (Suff *)sp; + int flags; + int flag; + + fprintf(debug_file, "# `%s' [%d] ", s->name, s->refCount); + + flags = s->flags; + if (flags) { + fputs(" (", debug_file); + while (flags) { + flag = 1 << (ffs(flags) - 1); + flags &= ~flag; + switch (flag) { + case SUFF_NULL: + fprintf(debug_file, "NULL"); + break; + case SUFF_INCLUDE: + fprintf(debug_file, "INCLUDE"); + break; + case SUFF_LIBRARY: + fprintf(debug_file, "LIBRARY"); + break; + } + fputc(flags ? '|' : ')', debug_file); + } + } + fputc('\n', debug_file); + fprintf(debug_file, "#\tTo: "); + Lst_ForEach(s->parents, SuffPrintName, NULL); + fputc('\n', debug_file); + fprintf(debug_file, "#\tFrom: "); + Lst_ForEach(s->children, SuffPrintName, NULL); + fputc('\n', debug_file); + fprintf(debug_file, "#\tSearch Path: "); + Dir_PrintPath(s->searchPath); + fputc('\n', debug_file); + return (dummy ? 0 : 0); +} + +static int +SuffPrintTrans(void *tp, void *dummy) +{ + GNode *t = (GNode *)tp; + + fprintf(debug_file, "%-16s: ", t->name); + Targ_PrintType(t->type); + fputc('\n', debug_file); + Lst_ForEach(t->commands, Targ_PrintCmd, NULL); + fputc('\n', debug_file); + return(dummy ? 0 : 0); +} + +void +Suff_PrintAll(void) +{ + fprintf(debug_file, "#*** Suffixes:\n"); + Lst_ForEach(sufflist, SuffPrintSuff, NULL); + + fprintf(debug_file, "#*** Transformations:\n"); + Lst_ForEach(transforms, SuffPrintTrans, NULL); +} diff --git a/20120704/targ.c b/20120704/targ.c new file mode 100644 index 0000000..d26b845 --- /dev/null +++ b/20120704/targ.c @@ -0,0 +1,848 @@ +/* $NetBSD: targ.c,v 1.57 2012/06/12 19:21:51 joerg 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.57 2012/06/12 19:21:51 joerg Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)targ.c 8.2 (Berkeley) 3/19/94"; +#else +__RCSID("$NetBSD: targ.c,v 1.57 2012/06/12 19:21:51 joerg 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. + * + * Targ_Propagate Propagate information between related + * nodes. Should be called after the + * makefiles are parsed but before any + * action is taken. + * + * Debugging: + * Targ_PrintGraph Print out the entire graphm all variables + * and statistics for the directory cache. Should + * print something for suffixes, too, but... + */ + +#include <stdio.h> +#include <time.h> + +#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(void *, void *); +static int TargPrintName(void *, void *); +#ifdef CLEANUP +static void TargFreeGN(void *); +#endif +static int TargPropagateCohort(void *, void *); +static int TargPropagateNode(void *, void *); + +/*- + *----------------------------------------------------------------------- + * 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, NULL); + 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 = bmake_malloc(sizeof(GNode)); + gn->name = bmake_strdup(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->cohort_num[0] = 0; + gn->centurion = NULL; + gn->made = UNMADE; + gn->flags = 0; + gn->checked = 0; + gn->mtime = 0; + gn->cmgn = NULL; + gn->iParents = Lst_Init(FALSE); + gn->cohorts = Lst_Init(FALSE); + gn->parents = Lst_Init(FALSE); + gn->children = Lst_Init(FALSE); + gn->order_pred = Lst_Init(FALSE); + gn->order_succ = 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, gn); +#endif + + return (gn); +} + +#ifdef CLEANUP +/*- + *----------------------------------------------------------------------- + * TargFreeGN -- + * Destroy a GNode + * + * Results: + * None. + * + * Side Effects: + * None. + *----------------------------------------------------------------------- + */ +static void +TargFreeGN(void *gnp) +{ + GNode *gn = (GNode *)gnp; + + + free(gn->name); + if (gn->uname) + free(gn->uname); + if (gn->path) + free(gn->path); + /* gn->fname points to name allocated when file was opened, don't free */ + + Lst_Destroy(gn->iParents, NULL); + Lst_Destroy(gn->cohorts, NULL); + Lst_Destroy(gn->parents, NULL); + Lst_Destroy(gn->children, NULL); + Lst_Destroy(gn->order_succ, NULL); + Lst_Destroy(gn->order_pred, NULL); + Hash_DeleteTable(&gn->context); + Lst_Destroy(gn->commands, NULL); + 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 NULL 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 = NULL; /* 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 | TARG_NOHASH))) { + he = Hash_FindEntry(&targets, name); + if (he == NULL) + return NULL; + return (GNode *)Hash_GetValue(he); + } + + if (!(flags & TARG_NOHASH)) { + he = Hash_CreateEntry(&targets, name, &isNew); + if (!isNew) + return (GNode *)Hash_GetValue(he); + } + + gn = Targ_NewGN(name); + if (!(flags & TARG_NOHASH)) + Hash_SetValue(he, gn); + Var_Append(".ALLTARGETS", name, VAR_GLOBAL); + (void)Lst_AtEnd(allTargets, gn); + if (doing_depend) + gn->flags |= FROM_DEPEND; + return gn; +} + +/*- + *----------------------------------------------------------------------- + * 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)) != NULL) { + name = (char *)Lst_Datum(ln); + gn = Targ_FindNode(name, flags); + if (gn != NULL) { + /* + * 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, 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(void *gnp, void *pflags MAKE_ATTR_UNUSED) +{ + GNode *gn = (GNode *)gnp; + + fprintf(debug_file, "%s%s ", gn->name, gn->cohort_num); + + return 0; +} + + +int +Targ_PrintCmd(void *cmd, void *dummy) +{ + fprintf(debug_file, "\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): fprintf(debug_file, "." #attr " "); break +#define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG))fprintf(debug_file, "." #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))fprintf(debug_file, ".MEMBER "); break; + PRINTDBIT(ARCHV); + PRINTDBIT(MADE); + PRINTDBIT(PHONY); + } + } +} + +static const char * +made_name(enum enum_made made) +{ + switch (made) { + case UNMADE: return "unmade"; + case DEFERRED: return "deferred"; + case REQUESTED: return "requested"; + case BEINGMADE: return "being made"; + case MADE: return "made"; + case UPTODATE: return "up-to-date"; + case ERROR: return "error when made"; + case ABORTED: return "aborted"; + default: return "unknown enum_made value"; + } +} + +/*- + *----------------------------------------------------------------------- + * TargPrintNode -- + * print the contents of a node + *----------------------------------------------------------------------- + */ +int +Targ_PrintNode(void *gnp, void *passp) +{ + GNode *gn = (GNode *)gnp; + int pass = passp ? *(int *)passp : 0; + + fprintf(debug_file, "# %s%s, flags %x, type %x, made %d\n", + gn->name, gn->cohort_num, gn->flags, gn->type, gn->made); + if (gn->flags == 0) + return 0; + + if (!OP_NOP(gn->type)) { + fprintf(debug_file, "#\n"); + if (gn == mainTarg) { + fprintf(debug_file, "# *** MAIN TARGET ***\n"); + } + if (pass >= 2) { + if (gn->unmade) { + fprintf(debug_file, "# %d unmade children\n", gn->unmade); + } else { + fprintf(debug_file, "# No unmade children\n"); + } + if (! (gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC))) { + if (gn->mtime != 0) { + fprintf(debug_file, "# last modified %s: %s\n", + Targ_FmtTime(gn->mtime), + made_name(gn->made)); + } else if (gn->made != UNMADE) { + fprintf(debug_file, "# non-existent (maybe): %s\n", + made_name(gn->made)); + } else { + fprintf(debug_file, "# unmade\n"); + } + } + if (!Lst_IsEmpty (gn->iParents)) { + fprintf(debug_file, "# implicit parents: "); + Lst_ForEach(gn->iParents, TargPrintName, NULL); + fprintf(debug_file, "\n"); + } + } else { + if (gn->unmade) + fprintf(debug_file, "# %d unmade children\n", gn->unmade); + } + if (!Lst_IsEmpty (gn->parents)) { + fprintf(debug_file, "# parents: "); + Lst_ForEach(gn->parents, TargPrintName, NULL); + fprintf(debug_file, "\n"); + } + if (!Lst_IsEmpty (gn->order_pred)) { + fprintf(debug_file, "# order_pred: "); + Lst_ForEach(gn->order_pred, TargPrintName, NULL); + fprintf(debug_file, "\n"); + } + if (!Lst_IsEmpty (gn->order_succ)) { + fprintf(debug_file, "# order_succ: "); + Lst_ForEach(gn->order_succ, TargPrintName, NULL); + fprintf(debug_file, "\n"); + } + + fprintf(debug_file, "%-16s", gn->name); + switch (gn->type & OP_OPMASK) { + case OP_DEPENDS: + fprintf(debug_file, ": "); break; + case OP_FORCE: + fprintf(debug_file, "! "); break; + case OP_DOUBLEDEP: + fprintf(debug_file, ":: "); break; + } + Targ_PrintType(gn->type); + Lst_ForEach(gn->children, TargPrintName, NULL); + fprintf(debug_file, "\n"); + Lst_ForEach(gn->commands, Targ_PrintCmd, NULL); + fprintf(debug_file, "\n\n"); + if (gn->type & OP_DOUBLEDEP) { + Lst_ForEach(gn->cohorts, Targ_PrintNode, &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(void *gnp, void *dummy MAKE_ATTR_UNUSED) +{ + GNode *gn = (GNode *)gnp; + if (!OP_NOP(gn->type)) + return 0; + + fprintf(debug_file, "#\t%s [%s] ", + gn->name, gn->path ? gn->path : gn->name); + Targ_PrintType(gn->type); + fprintf(debug_file, "\n"); + + return 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) +{ + fprintf(debug_file, "#*** Input graph:\n"); + Lst_ForEach(allTargets, Targ_PrintNode, &pass); + fprintf(debug_file, "\n\n"); + fprintf(debug_file, "#\n# Files that are only sources:\n"); + Lst_ForEach(allTargets, TargPrintOnlySrc, NULL); + fprintf(debug_file, "#*** Global Variables:\n"); + Var_Dump(VAR_GLOBAL); + fprintf(debug_file, "#*** Command-line Variables:\n"); + Var_Dump(VAR_CMD); + fprintf(debug_file, "\n"); + Dir_PrintDirectories(); + fprintf(debug_file, "\n"); + Suff_PrintAll(); +} + +/*- + *----------------------------------------------------------------------- + * TargPropagateNode -- + * Propagate information from a single node to related nodes if + * appropriate. + * + * Input: + * gnp The node that we are processing. + * + * Results: + * Always returns 0, for the benefit of Lst_ForEach(). + * + * Side Effects: + * Information is propagated from this node to cohort or child + * nodes. + * + * If the node was defined with "::", then TargPropagateCohort() + * will be called for each cohort node. + * + * If the node has recursive predecessors, then + * TargPropagateRecpred() will be called for each recursive + * predecessor. + *----------------------------------------------------------------------- + */ +static int +TargPropagateNode(void *gnp, void *junk MAKE_ATTR_UNUSED) +{ + GNode *gn = (GNode *)gnp; + + if (gn->type & OP_DOUBLEDEP) + Lst_ForEach(gn->cohorts, TargPropagateCohort, gnp); + return (0); +} + +/*- + *----------------------------------------------------------------------- + * TargPropagateCohort -- + * Propagate some bits in the type mask from a node to + * a related cohort node. + * + * Input: + * cnp The node that we are processing. + * gnp Another node that has cnp as a cohort. + * + * Results: + * Always returns 0, for the benefit of Lst_ForEach(). + * + * Side Effects: + * cnp's type bitmask is modified to incorporate some of the + * bits from gnp's type bitmask. (XXX need a better explanation.) + *----------------------------------------------------------------------- + */ +static int +TargPropagateCohort(void *cgnp, void *pgnp) +{ + GNode *cgn = (GNode *)cgnp; + GNode *pgn = (GNode *)pgnp; + + cgn->type |= pgn->type & ~OP_OPMASK; + return (0); +} + +/*- + *----------------------------------------------------------------------- + * Targ_Propagate -- + * Propagate information between related nodes. Should be called + * after the makefiles are parsed but before any action is taken. + * + * Results: + * none + * + * Side Effects: + * Information is propagated between related nodes throughout the + * graph. + *----------------------------------------------------------------------- + */ +void +Targ_Propagate(void) +{ + Lst_ForEach(allTargets, TargPropagateNode, NULL); +} diff --git a/20120704/trace.c b/20120704/trace.c new file mode 100644 index 0000000..267177f --- /dev/null +++ b/20120704/trace.c @@ -0,0 +1,116 @@ +/* $NetBSD: trace.c,v 1.11 2008/12/28 18:31:51 christos 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. + * + * 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.11 2008/12/28 18:31:51 christos Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: trace.c,v 1.11 2008/12/28 18:31:51 christos 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 <sys/time.h> + +#include <stdio.h> +#include <unistd.h> + +#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, "%lld.%06ld %d %s %d %s", + (long long)rightnow.tv_sec, (long)rightnow.tv_usec, + jobTokensRunning, + 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/20120704/trace.h b/20120704/trace.h new file mode 100644 index 0000000..dc0fc6c --- /dev/null +++ b/20120704/trace.h @@ -0,0 +1,49 @@ +/* $NetBSD: trace.h,v 1.3 2008/04/28 20:24:14 martin 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. + * + * 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/20120704/unit-tests/Makefile.in b/20120704/unit-tests/Makefile.in new file mode 100644 index 0000000..4e3592d --- /dev/null +++ b/20120704/unit-tests/Makefile.in @@ -0,0 +1,96 @@ +# $Id: Makefile.in,v 1.38 2012/06/19 23:38:48 sjg Exp $ +# +# $NetBSD: Makefile,v 1.34 2012/06/19 23:25:53 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 \ + error \ + export \ + export-all \ + doterror \ + dotwait \ + forloop \ + forsubst \ + hash \ + misc \ + moderrs \ + modmatch \ + modmisc \ + modorder \ + modts \ + modword \ + phony-end \ + posix \ + qequals \ + sysv \ + ternary \ + unexport \ + unexport-env \ + varcmd + +all: ${SUBFILES} + +flags.doterror= + +# the tests are actually done with sub-makes. +.PHONY: ${SUBFILES} +.PRECIOUS: ${SUBFILES} +${SUBFILES}: + -@${.MAKE} ${flags.$@:U-k} -f ${UNIT_TESTS}/$@ + +clean: + rm -f *.out *.fail *.core + +.-include <bsd.obj.mk> + +TEST_MAKE?= ${.MAKE} +TOOL_SED?= sed +TOOL_TR?= tr +TOOL_DIFF?= diff +DIFF_FLAGS?= @diff_u@ + +# ensure consistent results from sort(1) +LC_ALL= C +LANG= C +.export LANG LC_ALL + +# 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 | \ + ${TOOL_TR} -d '\015' | \ + ${TOOL_SED} -e 's,^${TEST_MAKE:T:C/\./\\\./g}:,make:,' \ + -e '/stopped/s, /.*, unit-tests,' \ + -e 's,${.CURDIR:C/\./\\\./g}/,,g' \ + -e 's,${UNIT_TESTS:C/\./\\\./g}/,,g' > ${.TARGET}.out || { \ + tail ${.TARGET}.out; mv ${.TARGET}.out ${.TARGET}.fail; exit 1; } + ${TOOL_DIFF} ${DIFF_FLAGS} ${UNIT_TESTS}/${.TARGET}.exp ${.TARGET}.out + +accept: + mv test.out ${srcdir}/test.exp + diff --git a/20120704/unit-tests/comment b/20120704/unit-tests/comment new file mode 100644 index 0000000..7dd7dbb --- /dev/null +++ b/20120704/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/20120704/unit-tests/cond1 b/20120704/unit-tests/cond1 new file mode 100644 index 0000000..c877c3d --- /dev/null +++ b/20120704/unit-tests/cond1 @@ -0,0 +1,109 @@ +# $Id: cond1,v 1.1.1.3 2011/03/06 00:04:58 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 + +.if defined(nosuch) && ${nosuch:Mx} != "" +# this should not happen +.info nosuch is x +.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}@}" + @echo "${"${DoNotQuoteHere:U0}" > 0:?OK:No}" + @echo "${${NoSuchNumber:U42} > 0:?OK:No}" diff --git a/20120704/unit-tests/doterror b/20120704/unit-tests/doterror new file mode 100644 index 0000000..75d8920 --- /dev/null +++ b/20120704/unit-tests/doterror @@ -0,0 +1,20 @@ +# $Id: doterror,v 1.1.1.1 2010/04/08 17:43:00 sjg Exp $ + + +.BEGIN: + @echo At first, I am + +.END: + @echo not reached + +.ERROR: + @echo "$@: Looks like '${.ERROR_TARGET}' is upset." + +all: happy sad + +happy: + @echo $@ + +sad: + @echo and now: $@; exit 1 + diff --git a/20120704/unit-tests/dotwait b/20120704/unit-tests/dotwait new file mode 100644 index 0000000..43706af --- /dev/null +++ b/20120704/unit-tests/dotwait @@ -0,0 +1,61 @@ +# $NetBSD: dotwait,v 1.1 2006/02/26 22:45:46 apb Exp $ + +THISMAKEFILE:= ${.PARSEDIR}/${.PARSEFILE} + +TESTS= simple recursive shared cycle +PAUSE= sleep 1 + +# Use a .for loop rather than dependencies here, to ensure +# that the tests are run one by one, with parallelism +# only within tests. +# Ignore "--- target ---" lines printed by parallel make. +all: +.for t in ${TESTS} + @${.MAKE} -f ${THISMAKEFILE} -j4 $t | grep -v "^--- " +.endfor + +# +# Within each test, the names of the sub-targets follow these +# conventions: +# * If it's expected that two or more targets may be made in parallel, +# then the target names will differ only in an alphabetic component +# such as ".a" or ".b". +# * If it's expected that two or more targets should be made in sequence +# then the target names will differ in numeric components, such that +# lexical ordering of the target names matches the expected order +# in which the targets should be made. +# +# Targets may echo ${PARALLEL_TARG} to print a modified version +# of their own name, in which alphabetic components like ".a" or ".b" +# are converted to ".*". Two targets that are expected to +# be made in parallel will thus print the same strings, so that the +# output is independent of the order in which these targets are made. +# +PARALLEL_TARG= ${.TARGET:C/\.[a-z]/.*/g:Q} +.DEFAULT: + @echo ${PARALLEL_TARG}; ${PAUSE}; echo ${PARALLEL_TARG} +_ECHOUSE: .USE + @echo ${PARALLEL_TARG}; ${PAUSE}; echo ${PARALLEL_TARG} + +# simple: no recursion, no cycles +simple: simple.1 .WAIT simple.2 + +# recursive: all children of the left hand side of the .WAIT +# must be made before any child of the right hand side. +recursive: recursive.1.99 .WAIT recursive.2.99 +recursive.1.99: recursive.1.1.a recursive.1.1.b _ECHOUSE +recursive.2.99: recursive.2.1.a recursive.2.1.b _ECHOUSE + +# shared: both shared.1.99 and shared.2.99 depend on shared.0. +# shared.0 must be made first, even though it is a child of +# the right hand side of the .WAIT. +shared: shared.1.99 .WAIT shared.2.99 +shared.1.99: shared.0 _ECHOUSE +shared.2.99: shared.2.1 shared.0 _ECHOUSE + +# cycle: the cyclic dependency must not cause infinite recursion +# leading to stack overflow and a crash. +cycle: cycle.1.99 .WAIT cycle.2.99 +cycle.2.99: cycle.2.98 _ECHOUSE +cycle.2.98: cycle.2.97 _ECHOUSE +cycle.2.97: cycle.2.99 _ECHOUSE diff --git a/20120704/unit-tests/error b/20120704/unit-tests/error new file mode 100644 index 0000000..c0a1403 --- /dev/null +++ b/20120704/unit-tests/error @@ -0,0 +1,10 @@ +# $Id: error,v 1.1.1.2 2010/05/24 23:36:03 sjg Exp $ + +.info just FYI +.warning this could be serious +.error this is fatal + +all: + +.info.html: + @echo this should be ignored diff --git a/20120704/unit-tests/export b/20120704/unit-tests/export new file mode 100644 index 0000000..3e2ad95 --- /dev/null +++ b/20120704/unit-tests/export @@ -0,0 +1,22 @@ +# $Id: export,v 1.1.1.1 2007/10/08 20:30:12 sjg Exp $ + +UT_TEST=export +UT_FOO=foo${BAR} +UT_FU=fubar +UT_ZOO=hoopie +UT_NO=all +# belive it or not, we expect this one to come out with $UT_FU unexpanded. +UT_DOLLAR= This is $$UT_FU + +.export UT_FU UT_FOO +.export UT_DOLLAR +# this one will be ignored +.export .MAKE.PID + +BAR=bar is ${UT_FU} + +.MAKE.EXPORTED+= UT_ZOO UT_TEST + +all: + @env | grep '^UT_' | sort + diff --git a/20120704/unit-tests/export-all b/20120704/unit-tests/export-all new file mode 100644 index 0000000..a243fe3 --- /dev/null +++ b/20120704/unit-tests/export-all @@ -0,0 +1,23 @@ +# $Id: export-all,v 1.1.1.2 2010/04/21 04:26:14 sjg Exp $ + +UT_OK=good +UT_F=fine + +# the old way to do :tA +M_tAbad = C,.*,cd & \&\& 'pwd',:sh +# the new +M_tA = tA + +here := ${.PARSEDIR} + +# this will cause trouble (recursing if we let it) +UT_BADDIR = ${${here}/../${here:T}:L:${M_tAbad}:T} +# this will be ok +UT_OKDIR = ${${here}/../${here:T}:L:${M_tA}:T} + +.export + +.include "export" + +UT_TEST=export-all +UT_ALL=even this gets exported diff --git a/20120704/unit-tests/forloop b/20120704/unit-tests/forloop new file mode 100644 index 0000000..0b50e66 --- /dev/null +++ b/20120704/unit-tests/forloop @@ -0,0 +1,45 @@ +# $Id: forloop,v 1.1.1.1 2012/06/19 23:30:49 sjg Exp $ + +all: for-loop + +LIST = one "two and three" four "five" + +.if make(for-fail) +for-fail: + +XTRA_LIST = xtra +.else + +.for x in ${LIST} +X!= echo 'x=$x' >&2; echo +.endfor + +CFL = -I/this -I"This or that" -Ithat "-DTHIS=\"this and that\"" +cfl= +.for x in ${CFL} +X!= echo 'x=$x' >&2; echo +.if empty(cfl) +cfl= $x +.else +cfl+= $x +.endif +.endfor +X!= echo 'cfl=${cfl}' >&2; echo + +.if ${cfl} != ${CFL} +.error ${.newline}'${cfl}' != ${.newline}'${CFL}' +.endif + +.for a b in ${EMPTY} +X!= echo 'a=$a b=$b' >&2; echo +.endfor +.endif + +.for a b in ${LIST} ${LIST:tu} ${XTRA_LIST} +X!= echo 'a=$a b=$b' >&2; echo +.endfor + +for-loop: + @echo We expect an error next: + @(cd ${.CURDIR} && ${.MAKE} -f ${MAKEFILE} for-fail) && \ + { echo "Oops that should have failed!"; exit 1; } || echo OK diff --git a/20120704/unit-tests/forsubst b/20120704/unit-tests/forsubst new file mode 100644 index 0000000..d3a7de1 --- /dev/null +++ b/20120704/unit-tests/forsubst @@ -0,0 +1,10 @@ +# $Id: forsubst,v 1.1.1.1 2009/10/07 18:53:35 sjg Exp $ + +all: for-subst + +here := ${.PARSEDIR} +# this should not run foul of the parser +.for file in ${.PARSEFILE} +for-subst: ${file:S;^;${here}/;g} + @echo ".for with :S;... OK" +.endfor diff --git a/20120704/unit-tests/hash b/20120704/unit-tests/hash new file mode 100644 index 0000000..1ed84e7 --- /dev/null +++ b/20120704/unit-tests/hash @@ -0,0 +1,18 @@ +STR1= +STR2= a +STR3= ab +STR4= abc +STR5= abcd +STR6= abcde +STR7= abcdef +STR8= abcdefghijklmnopqrstuvwxyz + +all: + @echo ${STR1:hash} + @echo ${STR2:hash} + @echo ${STR3:hash} + @echo ${STR4:hash} + @echo ${STR5:hash} + @echo ${STR6:hash} + @echo ${STR7:hash} + @echo ${STR8:hash} diff --git a/20120704/unit-tests/misc b/20120704/unit-tests/misc new file mode 100644 index 0000000..4ba3655 --- /dev/null +++ b/20120704/unit-tests/misc @@ -0,0 +1,16 @@ +# $Id: misc,v 1.1.1.1 2011/03/06 00:04:58 sjg Exp $ + +.if !exists(${.CURDIR}/) +.warning ${.CURDIR}/ doesn't exist ? +.endif + +.if !exists(${.CURDIR}/.) +.warning ${.CURDIR}/. doesn't exist ? +.endif + +.if !exists(${.CURDIR}/..) +.warning ${.CURDIR}/.. doesn't exist ? +.endif + +all: + @: all is well diff --git a/20120704/unit-tests/moderrs b/20120704/unit-tests/moderrs new file mode 100644 index 0000000..b8f78ce --- /dev/null +++ b/20120704/unit-tests/moderrs @@ -0,0 +1,31 @@ +# $Id: moderrs,v 1.2 2006/05/11 18:53:39 sjg Exp $ +# +# various modifier error tests + +VAR=TheVariable +# incase we have to change it ;-) +MOD_UNKN=Z +MOD_TERM=S,V,v +MOD_S:= ${MOD_TERM}, + +all: modunkn modunknV varterm vartermV modtermV + +modunkn: + @echo "Expect: Unknown modifier 'Z'" + @echo "VAR:Z=${VAR:Z}" + +modunknV: + @echo "Expect: Unknown modifier 'Z'" + @echo "VAR:${MOD_UNKN}=${VAR:${MOD_UNKN}}" + +varterm: + @echo "Expect: Unclosed variable specification for VAR" + @echo VAR:S,V,v,=${VAR:S,V,v, + +vartermV: + @echo "Expect: Unclosed variable specification for VAR" + @echo VAR:${MOD_TERM},=${VAR:${MOD_S} + +modtermV: + @echo "Expect: Unclosed substitution for VAR (, missing)" + -@echo "VAR:${MOD_TERM}=${VAR:${MOD_TERM}}" diff --git a/20120704/unit-tests/modmatch b/20120704/unit-tests/modmatch new file mode 100644 index 0000000..48a1bef --- /dev/null +++ b/20120704/unit-tests/modmatch @@ -0,0 +1,25 @@ + +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 + +var = head +res = no +.if !empty(var:M${:Uhead\:tail:C/:.*//}) +res = OK +.endif + +all: + @for x in $X; do ${.MAKE} -f ${MAKEFILE} show LIB=$$x; done + @echo "Mscanner=${res}" + +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/20120704/unit-tests/modmisc b/20120704/unit-tests/modmisc new file mode 100644 index 0000000..d562e46 --- /dev/null +++ b/20120704/unit-tests/modmisc @@ -0,0 +1,38 @@ +# $Id: modmisc,v 1.1.1.5 2011/04/11 15:10:32 sjg Exp $ +# +# miscellaneous modifier tests + +# do not put any dirs in this list which exist on some +# but not all target systems - an exists() check is below. +path=:/bin:/tmp::/:.:/no/such/dir:. +# strip cwd from path. +MOD_NODOT=S/:/ /g:N.:ts: +# and decorate, note that $'s need to be doubled. Also note that +# the modifier_variable can be used with other modifiers. +MOD_NODOTX=S/:/ /g:N.:@d@'$$d'@ +# another mod - pretend it is more interesting +MOD_HOMES=S,/home/,/homes/, +MOD_OPT=@d@$${exists($$d):?$$d:$${d:S,/usr,/opt,}}@ +MOD_SEP=S,:, ,g + +all: modvar modvarloop modsysv + +modsysv: + @echo "The answer is ${libfoo.a:L:libfoo.a=42}" + +modvar: + @echo "path='${path}'" + @echo "path='${path:${MOD_NODOT}}'" + @echo "path='${path:S,home,homes,:${MOD_NODOT}}'" + @echo "path=${path:${MOD_NODOTX}:ts:}" + @echo "path=${path:${MOD_HOMES}:${MOD_NODOTX}:ts:}" + +.for d in ${path:${MOD_SEP}:N.} /usr/xbin +path_$d?= ${d:${MOD_OPT}:${MOD_HOMES}}/ +paths+= ${d:${MOD_OPT}:${MOD_HOMES}} +.endfor + +modvarloop: + @echo "path_/usr/xbin=${path_/usr/xbin}" + @echo "paths=${paths}" + @echo "PATHS=${paths:tu}" diff --git a/20120704/unit-tests/modorder b/20120704/unit-tests/modorder new file mode 100644 index 0000000..68b66fb --- /dev/null +++ b/20120704/unit-tests/modorder @@ -0,0 +1,22 @@ +# $NetBSD: modorder,v 1.2 2007/10/05 15:27:46 sjg Exp $ + +LIST= one two three four five six seven eight 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/20120704/unit-tests/modts b/20120704/unit-tests/modts new file mode 100644 index 0000000..616bd89 --- /dev/null +++ b/20120704/unit-tests/modts @@ -0,0 +1,43 @@ + +LIST= one two three +LIST+= four five six + +FU_mod-ts = a / b / cool + +AAA= a a a +B.aaa= Baaa + +all: mod-ts + +# Use print or printf iff they are builtin. +# XXX note that this causes problems, when make decides +# there is no need to use a shell, so avoid where possible. +.if ${type print 2> /dev/null || echo:L:sh:Mbuiltin} != "" +PRINT= print -r -- +.elif ${type printf 2> /dev/null || echo:L:sh:Mbuiltin} != "" +PRINT= printf '%s\n' +.else +PRINT= echo +.endif + +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." + @${PRINT} 'LIST:ts/n="${LIST:ts\n}"' + @${PRINT} 'LIST:ts/t="${LIST:ts\t}"' + @${PRINT} 'LIST:ts/012:tu="${LIST:ts\012:tu}"' + @${PRINT} 'LIST:tx="${LIST:tx}"' + @${PRINT} 'LIST:ts/x:tu="${LIST:ts\x:tu}"' + @${PRINT} 'FU_$@="${FU_${@:ts}:ts}"' + @${PRINT} 'FU_$@:ts:T="${FU_${@:ts}:ts:T}" == cool?' + @${PRINT} 'B.$${AAA:ts}="${B.${AAA:ts}}" == Baaa?' diff --git a/20120704/unit-tests/modword b/20120704/unit-tests/modword new file mode 100644 index 0000000..39355d7 --- /dev/null +++ b/20120704/unit-tests/modword @@ -0,0 +1,151 @@ +# $Id: modword,v 1.1.1.1 2003/09/28 17:01:48 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/20120704/unit-tests/phony-end b/20120704/unit-tests/phony-end new file mode 100644 index 0000000..d61884c --- /dev/null +++ b/20120704/unit-tests/phony-end @@ -0,0 +1,9 @@ +# $Id: phony-end,v 1.1.1.1 2011/10/01 17:19:39 sjg Exp $ + +all ok also.ok bug phony: + @echo '${.TARGET .PREFIX .IMPSRC:L:@v@$v="${$v}"@}' + +.END: ok also.ok bug + +phony bug: .PHONY +all: phony diff --git a/20120704/unit-tests/posix b/20120704/unit-tests/posix new file mode 100644 index 0000000..48ed7a3 --- /dev/null +++ b/20120704/unit-tests/posix @@ -0,0 +1,24 @@ +# $Id: posix,v 1.1.1.1 2004/05/08 16:45:39 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/20120704/unit-tests/qequals b/20120704/unit-tests/qequals new file mode 100644 index 0000000..e23078e --- /dev/null +++ b/20120704/unit-tests/qequals @@ -0,0 +1,8 @@ +# $Id: qequals,v 1.1.1.1 2008/03/31 00:13:05 sjg Exp $ + +M= i386 +V.i386= OK +V.$M ?= bug + +all: + @echo 'V.$M ?= ${V.$M}' diff --git a/20120704/unit-tests/sysv b/20120704/unit-tests/sysv new file mode 100644 index 0000000..9eedacb --- /dev/null +++ b/20120704/unit-tests/sysv @@ -0,0 +1,26 @@ +# $Id: sysv,v 1.1.1.2 2011/06/05 04:23:49 sjg Exp $ + +FOO ?= +FOOBAR = $(FOO:=bar) + +_this := ${.PARSEDIR}/${.PARSEFILE} + +B = /b +S = / +FUN = ${B}${S}fun +SUN = the Sun + +# we expect nothing when FOO is empty +all: foo fun + +foo: + @echo FOOBAR = $(FOOBAR) +.if empty(FOO) + @FOO="foo fu" ${.MAKE} -f ${_this} foo +.endif + +fun: + @echo ${FUN:T} + @echo ${FUN:${B}${S}fun=fun} + @echo ${FUN:${B}${S}%=%} + @echo ${In:L:%=% ${SUN}} diff --git a/20120704/unit-tests/ternary b/20120704/unit-tests/ternary new file mode 100644 index 0000000..77f8349 --- /dev/null +++ b/20120704/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/20120704/unit-tests/test.exp b/20120704/unit-tests/test.exp new file mode 100644 index 0000000..932d84e --- /dev/null +++ b/20120704/unit-tests/test.exp @@ -0,0 +1,369 @@ +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 + +make: warning: String comparison operator should be either == or != +make: Bad conditional expression `"0" > 0' in "0" > 0?OK:No + +OK +make: "error" line 3: just FYI +make: "error" line 4: warning: this could be serious +make: "error" line 5: this is fatal +UT_DOLLAR=This is $UT_FU +UT_FOO=foobar is fubar +UT_FU=fubar +UT_TEST=export +UT_ZOO=hoopie +UT_ALL=even this gets exported +UT_BADDIR=unit-tests +UT_DOLLAR=This is $UT_FU +UT_F=fine +UT_FOO=foobar is fubar +UT_FU=fubar +UT_NO=all +UT_OK=good +UT_OKDIR=unit-tests +UT_TEST=export-all +UT_ZOO=hoopie +At first, I am +happy +and now: sad +.ERROR: Looks like 'sad' is upset. +*** Error code 1 + +Stop. +make: stopped in unit-tests +simple.1 +simple.1 +simple.2 +simple.2 +recursive.1.1.* +recursive.1.1.* +recursive.1.1.* +recursive.1.1.* +recursive.1.99 +recursive.1.99 +recursive.2.1.* +recursive.2.1.* +recursive.2.1.* +recursive.2.1.* +recursive.2.99 +recursive.2.99 +shared.0 +shared.0 +shared.1.99 +shared.1.99 +shared.2.1 +shared.2.1 +shared.2.99 +shared.2.99 +make: Graph cycles through `cycle.2.99' +make: Graph cycles through `cycle.2.98' +make: Graph cycles through `cycle.2.97' +cycle.1.99 +cycle.1.99 +x=one +x="two and three" +x=four +x="five" +x=-I/this +x=-I"This or that" +x=-Ithat +x="-DTHIS=\"this and that\"" +cfl=-I/this -I"This or that" -Ithat "-DTHIS=\"this and that\"" +a=one b="two and three" +a=four b="five" +a=ONE b="TWO AND THREE" +a=FOUR b="FIVE" +We expect an error next: +make: "forloop" line 38: Wrong number of words (9) in .for substitution list with 2 vars +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +OK +.for with :S;... OK +b2af338b +3360ac65 +7747f046 +9ca87054 +880fe816 +208fcbd3 +d5d376eb +de41416c +Expect: Unknown modifier 'Z' +make: Unknown modifier 'Z' +VAR:Z= +Expect: Unknown modifier 'Z' +make: Unknown modifier 'Z' +VAR:Z= +Expect: Unclosed variable specification for VAR +make: Unclosed variable specification (expecting '}') for "VAR" (value "Thevariable") modifier S +VAR:S,V,v,=Thevariable +Expect: Unclosed variable specification for VAR +make: Unclosed variable specification after complex modifier (expecting '}') for VAR +VAR:S,V,v,=Thevariable +Expect: Unclosed substitution for VAR (, missing) +make: Unclosed substitution for VAR (, missing) +VAR:S,V,v= +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" +Mscanner=OK +path=':/bin:/tmp::/:.:/no/such/dir:.' +path='/bin:/tmp:/:/no/such/dir' +path='/bin:/tmp:/:/no/such/dir' +path='/bin':'/tmp':'/':'/no/such/dir' +path='/bin':'/tmp':'/':'/no/such/dir' +path_/usr/xbin=/opt/xbin/ +paths=/bin /tmp / /no/such/dir /opt/xbin +PATHS=/BIN /TMP / /NO/SUCH/DIR /OPT/XBIN +The answer is 42 +LIST = one two three four five six seven eight nine ten +LIST:O = eight five four nine one seven six ten three two +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" +.TARGET="phony" .PREFIX="phony" .IMPSRC="" +.TARGET="all" .PREFIX="all" .IMPSRC="" +.TARGET="ok" .PREFIX="ok" .IMPSRC="" +.TARGET="also.ok" .PREFIX="also.ok" .IMPSRC="" +.TARGET="bug" .PREFIX="bug" .IMPSRC="" +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. +V.i386 ?= OK +FOOBAR = +FOOBAR = foobar fubar +fun +fun +fun +In the Sun +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 +UT_DOLLAR=This is $UT_FU +UT_FU=fubar +UT_TEST=unexport +UT_TEST=unexport-env +default FU=<v>fu</v> FOO=<v>foo</v> VAR=<v></v> +two FU=<v>bar</v> FOO=<v>goo</v> VAR=<v></v> +three FU=<v>bar</v> FOO=<v>goo</v> VAR=<v></v> +four FU=<v>bar</v> FOO=<v>goo</v> VAR=<v>Internal</v> +five FU=<v>bar</v> FOO=<v>goo</v> VAR=<v>Internal</v> +five v=is x k=is x +six v=is y k=is y +show-v v=override k=override +*** Error code 1 (ignored) +*** Error code 1 (ignored) diff --git a/20120704/unit-tests/unexport b/20120704/unit-tests/unexport new file mode 100644 index 0000000..fb40d0c --- /dev/null +++ b/20120704/unit-tests/unexport @@ -0,0 +1,8 @@ +# $Id: unexport,v 1.1.1.1 2009/11/19 00:31:11 sjg Exp $ + +# pick up a bunch of exported vars +.include "export" + +.unexport UT_ZOO UT_FOO + +UT_TEST = unexport diff --git a/20120704/unit-tests/unexport-env b/20120704/unit-tests/unexport-env new file mode 100644 index 0000000..f6a2ff9 --- /dev/null +++ b/20120704/unit-tests/unexport-env @@ -0,0 +1,14 @@ +# $Id: unexport-env,v 1.1.1.1 2009/11/19 00:31:11 sjg Exp $ + +# pick up a bunch of exported vars +.include "export" + +# an example of setting up a minimal environment. +PATH = /bin:/usr/bin:/sbin:/usr/sbin + +# now clobber the environment to just PATH and UT_TEST +UT_TEST = unexport-env + +# this removes everything +.unexport-env +.export PATH UT_TEST diff --git a/20120704/unit-tests/varcmd b/20120704/unit-tests/varcmd new file mode 100644 index 0000000..a58e014 --- /dev/null +++ b/20120704/unit-tests/varcmd @@ -0,0 +1,49 @@ +# $Id: varcmd,v 1.3 2008/05/15 04:30:47 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=<v>${FU}</v> FOO=<v>${FOO}</v> VAR=<v>${VAR}</v>" + +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} five + +M = x +V.y = is y +V.x = is x +V := ${V.$M} +K := ${V} + +show-v: + @echo '${TAG} v=${V} k=${K}' + +five: show show-v + @${.MAKE} -f ${MAKEFILE} M=y six + +six: show-v + @${.MAKE} -f ${MAKEFILE} V=override show-v + diff --git a/20120704/util.c b/20120704/util.c new file mode 100644 index 0000000..a63fd33 --- /dev/null +++ b/20120704/util.c @@ -0,0 +1,619 @@ +/* $NetBSD: util.c,v 1.53 2012/06/04 22:45:05 sjg Exp $ */ + +/* + * Missing stuff from OS's + * + * $Id: util.c,v 1.32 2012/06/06 20:08:44 sjg Exp $ + */ + +#include "make.h" + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: util.c,v 1.53 2012/06/04 22:45:05 sjg Exp $"; +#else +#ifndef lint +__RCSID("$NetBSD: util.c,v 1.53 2012/06/04 22:45:05 sjg Exp $"); +#endif +#endif + +#include <errno.h> +#include <time.h> +#include <signal.h> + +#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_GETENV) || !defined(HAVE_SETENV) || !defined(HAVE_UNSETENV) +extern char **environ; + +static char * +findenv(const char *name, int *offset) +{ + size_t i, len; + char *p, *q; + + len = strlen(name); + for (i = 0; (q = environ[i]); i++) { + p = strchr(q, '='); + if (p == NULL || p - q != len) + continue; + if (strncmp(name, q, len) == 0) { + *offset = i; + return q + len + 1; + } + } + *offset = i; + return NULL; +} + +char * +getenv(const char *name) +{ + int offset; + + return(findenv(name, &offset)); +} + +int +unsetenv(const char *name) +{ + char **p; + int offset; + + if (name == NULL || *name == '\0' || strchr(name, '=') != NULL) { + errno = EINVAL; + return -1; + } + + while (findenv(name, &offset)) { /* if set multiple times */ + for (p = &environ[offset];; ++p) + if (!(*p = *(p + 1))) + break; + } + return 0; +} + +int +setenv(const char *name, const char *value, int rewrite) +{ + char *c, **newenv; + const char *cc; + size_t l_value, size; + int offset; + + if (name == NULL || value == NULL) { + errno = EINVAL; + return -1; + } + + if (*value == '=') /* no `=' in value */ + ++value; + l_value = strlen(value); + + /* find if already exists */ + if ((c = findenv(name, &offset))) { + if (!rewrite) + return 0; + if (strlen(c) >= l_value) /* old larger; copy over */ + goto copy; + } else { /* create new slot */ + size = sizeof(char *) * (offset + 2); + if (savedEnv == environ) { /* just increase size */ + if ((newenv = realloc(savedEnv, size)) == NULL) + return -1; + savedEnv = newenv; + } else { /* get new space */ + /* + * We don't free here because we don't know if + * the first allocation is valid on all OS's + */ + if ((savedEnv = malloc(size)) == NULL) + return -1; + (void)memcpy(savedEnv, environ, size - sizeof(char *)); + } + environ = savedEnv; + environ[offset + 1] = NULL; + } + for (cc = name; *cc && *cc != '='; ++cc) /* no `=' in name */ + continue; + size = cc - name; + /* name + `=' + value */ + if ((environ[offset] = malloc(size + l_value + 2)) == NULL) + return -1; + c = environ[offset]; + (void)memcpy(c, name, size); + c += size; + *c++ = '='; +copy: + (void)memcpy(c, value, l_value + 1); + return 0; +} + +#ifdef TEST +int +main(int argc, char *argv[]) +{ + setenv(argv[1], argv[2], 0); + printf("%s\n", getenv(argv[1])); + unsetenv(argv[1]); + printf("%s\n", getenv(argv[1])); + return 0; +} +#endif + +#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 <sys/types.h> +#include <sys/syscall.h> +#include <sys/signal.h> +#include <sys/stat.h> +#include <dirent.h> +#include <sys/time.h> +#include <unistd.h> + +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 + +#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 + +/* force posix signals */ +void (* +bmake_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; +} + +#if !defined(HAVE_VSNPRINTF) || !defined(HAVE_VASPRINTF) +#include <stdarg.h> +#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_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, "%02d", tm->tm_mday); + break; + case 'Y': + s = snprintf(buf, len, "%d", 1900 + tm->tm_year); + break; + default: + s = snprintf(buf, len, "Unsupported format %c", + fmt[-1]); + break; + } + buf += s; + len -= s; + } +} +#endif + +#if !defined(HAVE_KILLPG) +#if !defined(__hpux__) && !defined(__hpux) +int +killpg(int pid, int sig) +{ + return kill(-pid, sig); +} +#endif +#endif + +#if !defined(HAVE_WARNX) +static void +vwarnx(const char *fmt, va_list args) +{ + fprintf(stderr, "%s: ", progname); + if ((fmt)) { + vfprintf(stderr, fmt, args); + fprintf(stderr, ": "); + } +} +#endif + +#if !defined(HAVE_WARN) +static void +vwarn(const char *fmt, va_list args) +{ + vwarnx(fmt, args); + fprintf(stderr, "%s\n", strerror(errno)); +} +#endif + +#if !defined(HAVE_ERR) +static void +verr(int eval, const char *fmt, va_list args) +{ + vwarn(fmt, args); + exit(eval); +} +#endif + +#if !defined(HAVE_ERRX) +static void +verrx(int eval, const char *fmt, va_list args) +{ + vwarnx(fmt, args); + exit(eval); +} +#endif + +#if !defined(HAVE_ERR) +void +err(int eval, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + verr(eval, fmt, ap); + va_end(ap); +} +#endif + +#if !defined(HAVE_ERRX) +void +errx(int eval, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + verrx(eval, fmt, ap); + va_end(ap); +} +#endif + +#if !defined(HAVE_WARN) +void +warn(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vwarn(fmt, ap); + va_end(ap); +} +#endif + +#if !defined(HAVE_WARNX) +void +warnx(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vwarnx(fmt, ap); + va_end(ap); +} +#endif diff --git a/20120704/var.c b/20120704/var.c new file mode 100644 index 0000000..e958a36 --- /dev/null +++ b/20120704/var.c @@ -0,0 +1,4196 @@ +/* $NetBSD: var.c,v 1.171 2012/06/12 19:21:51 joerg 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.171 2012/06/12 19:21:51 joerg Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 3/19/94"; +#else +__RCSID("$NetBSD: var.c,v 1.171 2012/06/12 19:21:51 joerg 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. + */ + +#include <sys/stat.h> +#ifndef NO_REGEX +#include <sys/types.h> +#include <regex.h> +#endif +#include <ctype.h> +#include <inttypes.h> +#include <stdlib.h> +#include <limits.h> +#include <time.h> + +#include "make.h" +#include "buf.h" +#include "dir.h" +#include "job.h" + +/* + * This lets us tell if we have replaced the original environ + * (which we cannot free). + */ +char **savedEnv = NULL; + +/* + * 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 'errnum' 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 */ +#define VAR_EXPORTED 16 /* Variable is exported */ +#define VAR_REEXPORT 32 /* Indicate if var needs re-export. + * This would be true if it contains $'s + */ +#define VAR_FROM_CMD 64 /* Variable came from command line */ +} Var; + +/* + * Exporting vars is expensive so skip it if we can + */ +#define VAR_EXPORTED_NONE 0 +#define VAR_EXPORTED_YES 1 +#define VAR_EXPORTED_ALL 2 +static int var_exportedVars = VAR_EXPORTED_NONE; +/* + * We pass this to Var_Export when doing the initial export + * or after updating an exported var. + */ +#define VAR_EXPORT_PARENT 1 + +/* 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 'void *' 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 'void *' 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 errnum; /* errnum for not defined */ +} VarLoop_t; + +#ifndef NO_REGEX +/* struct passed as 'void *' 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 *, void *); +static Boolean VarTail(GNode *, Var_Parse_State *, + char *, Boolean, Buffer *, void *); +static Boolean VarSuffix(GNode *, Var_Parse_State *, + char *, Boolean, Buffer *, void *); +static Boolean VarRoot(GNode *, Var_Parse_State *, + char *, Boolean, Buffer *, void *); +static Boolean VarMatch(GNode *, Var_Parse_State *, + char *, Boolean, Buffer *, void *); +#ifdef SYSVVARSUB +static Boolean VarSYSVMatch(GNode *, Var_Parse_State *, + char *, Boolean, Buffer *, void *); +#endif +static Boolean VarNoMatch(GNode *, Var_Parse_State *, + char *, Boolean, Buffer *, void *); +#ifndef NO_REGEX +static void VarREError(int, regex_t *, const char *); +static Boolean VarRESubstitute(GNode *, Var_Parse_State *, + char *, Boolean, Buffer *, void *); +#endif +static Boolean VarSubstitute(GNode *, Var_Parse_State *, + char *, Boolean, Buffer *, void *); +static Boolean VarLoopExpand(GNode *, Var_Parse_State *, + char *, Boolean, Buffer *, void *); +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 *VarHash(char *); +static char *VarModify(GNode *, Var_Parse_State *, + const char *, + Boolean (*)(GNode *, Var_Parse_State *, char *, Boolean, Buffer *, void *), + void *); +static char *VarOrder(const char *, const char); +static char *VarUniq(const char *); +static int VarWordCompare(const void *, const void *); +static void VarPrintVar(void *); + +#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 + * NULL 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; + } +#ifdef notyet + /* for compatibility with gmake */ + if (name[0] == '^' && name[1] == '\0') + name = ALLSRC; +#endif + + /* + * 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 = bmake_malloc(sizeof(Var)); + v->name = bmake_strdup(name); + + len = strlen(env); + + Buf_Init(&v->val, len + 1); + Buf_AddBytes(&v->val, len, 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 NULL; + } else { + return ((Var *)Hash_GetValue(var)); + } + } else { + return NULL; + } + } else if (var == NULL) { + return NULL; + } else { + return ((Var *)Hash_GetValue(var)); + } +} + +/*- + *----------------------------------------------------------------------- + * VarFreeEnv -- + * If the variable is an environment variable, free it + * + * Input: + * v the variable + * destroy true if the value buffer should be destroyed. + * + * Results: + * 1 if it is an environment variable 0 ow. + * + * Side Effects: + * The variable is free'ed if it is an environent variable. + *----------------------------------------------------------------------- + */ +static Boolean +VarFreeEnv(Var *v, Boolean destroy) +{ + if ((v->flags & VAR_FROM_ENV) == 0) + return FALSE; + free(v->name); + Buf_Destroy(&v->val, destroy); + free(v); + return TRUE; +} + +/*- + *----------------------------------------------------------------------- + * 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 = bmake_malloc(sizeof(Var)); + + len = val ? strlen(val) : 0; + Buf_Init(&v->val, len+1); + Buf_AddBytes(&v->val, len, val); + + v->flags = 0; + + h = Hash_CreateEntry(&ctxt->context, name, NULL); + Hash_SetValue(h, v); + v->name = h->name; + if (DEBUG(VAR)) { + fprintf(debug_file, "%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; + + ln = Hash_FindEntry(&ctxt->context, name); + if (DEBUG(VAR)) { + fprintf(debug_file, "%s:delete %s%s\n", + ctxt->name, name, ln ? "" : " (not found)"); + } + if (ln != NULL) { + Var *v; + + v = (Var *)Hash_GetValue(ln); + if ((v->flags & VAR_EXPORTED)) { + unsetenv(v->name); + } + if (strcmp(MAKE_EXPORTED, v->name) == 0) { + var_exportedVars = VAR_EXPORTED_NONE; + } + if (v->name != ln->name) + free(v->name); + Hash_DeleteEntry(&ctxt->context, ln); + Buf_Destroy(&v->val, TRUE); + free(v); + } +} + + +/* + * Export a var. + * We ignore make internal variables (those which start with '.') + * Also we jump through some hoops to avoid calling setenv + * more than necessary since it can leak. + * We only manipulate flags of vars if 'parent' is set. + */ +static int +Var_Export1(const char *name, int parent) +{ + char tmp[BUFSIZ]; + Var *v; + char *val = NULL; + int n; + + if (*name == '.') + return 0; /* skip internals */ + if (!name[1]) { + /* + * A single char. + * If it is one of the vars that should only appear in + * local context, skip it, else we can get Var_Subst + * into a loop. + */ + switch (name[0]) { + case '@': + case '%': + case '*': + case '!': + return 0; + } + } + v = VarFind(name, VAR_GLOBAL, 0); + if (v == NULL) { + return 0; + } + if (!parent && + (v->flags & (VAR_EXPORTED|VAR_REEXPORT)) == VAR_EXPORTED) { + return 0; /* nothing to do */ + } + val = Buf_GetAll(&v->val, NULL); + if (strchr(val, '$')) { + if (parent) { + /* + * Flag this as something we need to re-export. + * No point actually exporting it now though, + * the child can do it at the last minute. + */ + v->flags |= (VAR_EXPORTED|VAR_REEXPORT); + return 1; + } + if (v->flags & VAR_IN_USE) { + /* + * We recursed while exporting in a child. + * This isn't going to end well, just skip it. + */ + return 0; + } + n = snprintf(tmp, sizeof(tmp), "${%s}", name); + if (n < (int)sizeof(tmp)) { + val = Var_Subst(NULL, tmp, VAR_GLOBAL, 0); + setenv(name, val, 1); + free(val); + } + } else { + if (parent) { + v->flags &= ~VAR_REEXPORT; /* once will do */ + } + if (parent || !(v->flags & VAR_EXPORTED)) { + setenv(name, val, 1); + } + } + /* + * This is so Var_Set knows to call Var_Export again... + */ + if (parent) { + v->flags |= VAR_EXPORTED; + } + return 1; +} + +/* + * This gets called from our children. + */ +void +Var_ExportVars(void) +{ + char tmp[BUFSIZ]; + Hash_Entry *var; + Hash_Search state; + Var *v; + char *val; + int n; + + if (VAR_EXPORTED_NONE == var_exportedVars) + return; + + if (VAR_EXPORTED_ALL == var_exportedVars) { + /* + * Ouch! This is crazy... + */ + for (var = Hash_EnumFirst(&VAR_GLOBAL->context, &state); + var != NULL; + var = Hash_EnumNext(&state)) { + v = (Var *)Hash_GetValue(var); + Var_Export1(v->name, 0); + } + return; + } + /* + * We have a number of exported vars, + */ + n = snprintf(tmp, sizeof(tmp), "${" MAKE_EXPORTED ":O:u}"); + if (n < (int)sizeof(tmp)) { + char **av; + char *as; + int ac; + int i; + + val = Var_Subst(NULL, tmp, VAR_GLOBAL, 0); + av = brk_string(val, &ac, FALSE, &as); + for (i = 0; i < ac; i++) { + Var_Export1(av[i], 0); + } + free(val); + free(as); + free(av); + } +} + +/* + * This is called when .export is seen or + * .MAKE.EXPORTED is modified. + * It is also called when any exported var is modified. + */ +void +Var_Export(char *str, int isExport) +{ + char *name; + char *val; + char **av; + char *as; + int track; + int ac; + int i; + + if (isExport && (!str || !str[0])) { + var_exportedVars = VAR_EXPORTED_ALL; /* use with caution! */ + return; + } + + if (strncmp(str, "-env", 4) == 0) { + track = 0; + str += 4; + } else { + track = VAR_EXPORT_PARENT; + } + val = Var_Subst(NULL, str, VAR_GLOBAL, 0); + av = brk_string(val, &ac, FALSE, &as); + for (i = 0; i < ac; i++) { + name = av[i]; + if (!name[1]) { + /* + * A single char. + * If it is one of the vars that should only appear in + * local context, skip it, else we can get Var_Subst + * into a loop. + */ + switch (name[0]) { + case '@': + case '%': + case '*': + case '!': + continue; + } + } + if (Var_Export1(name, track)) { + if (VAR_EXPORTED_ALL != var_exportedVars) + var_exportedVars = VAR_EXPORTED_YES; + if (isExport && track) { + Var_Append(MAKE_EXPORTED, name, VAR_GLOBAL); + } + } + } + free(val); + free(as); + free(av); +} + + +/* + * This is called when .unexport[-env] is seen. + */ +extern char **environ; + +void +Var_UnExport(char *str) +{ + char tmp[BUFSIZ]; + char *vlist; + char *cp; + Boolean unexport_env; + int n; + + if (!str || !str[0]) { + return; /* assert? */ + } + + vlist = NULL; + + str += 8; + unexport_env = (strncmp(str, "-env", 4) == 0); + if (unexport_env) { + char **newenv; + + cp = getenv(MAKE_LEVEL); /* we should preserve this */ + if (environ == savedEnv) { + /* we have been here before! */ + newenv = bmake_realloc(environ, 2 * sizeof(char *)); + } else { + if (savedEnv) { + free(savedEnv); + savedEnv = NULL; + } + newenv = bmake_malloc(2 * sizeof(char *)); + } + if (!newenv) + return; + /* Note: we cannot safely free() the original environ. */ + environ = savedEnv = newenv; + newenv[0] = NULL; + newenv[1] = NULL; + setenv(MAKE_LEVEL, cp, 1); +#ifdef MAKE_LEVEL_SAFE + setenv(MAKE_LEVEL_SAFE, cp, 1); +#endif + } else { + for (; *str != '\n' && isspace((unsigned char) *str); str++) + continue; + if (str[0] && str[0] != '\n') { + vlist = str; + } + } + + if (!vlist) { + /* Using .MAKE.EXPORTED */ + n = snprintf(tmp, sizeof(tmp), "${" MAKE_EXPORTED ":O:u}"); + if (n < (int)sizeof(tmp)) { + vlist = Var_Subst(NULL, tmp, VAR_GLOBAL, 0); + } + } + if (vlist) { + Var *v; + char **av; + char *as; + int ac; + int i; + + av = brk_string(vlist, &ac, FALSE, &as); + for (i = 0; i < ac; i++) { + v = VarFind(av[i], VAR_GLOBAL, 0); + if (!v) + continue; + if (!unexport_env && + (v->flags & (VAR_EXPORTED|VAR_REEXPORT)) == VAR_EXPORTED) { + unsetenv(v->name); + } + v->flags &= ~(VAR_EXPORTED|VAR_REEXPORT); + /* + * If we are unexporting a list, + * remove each one from .MAKE.EXPORTED. + * If we are removing them all, + * just delete .MAKE.EXPORTED below. + */ + if (vlist == str) { + n = snprintf(tmp, sizeof(tmp), + "${" MAKE_EXPORTED ":N%s}", v->name); + if (n < (int)sizeof(tmp)) { + cp = Var_Subst(NULL, tmp, VAR_GLOBAL, 0); + Var_Set(MAKE_EXPORTED, cp, VAR_GLOBAL, 0); + free(cp); + } + } + } + free(as); + free(av); + if (vlist != str) { + Var_Delete(MAKE_EXPORTED, VAR_GLOBAL); + free(vlist); + } + } +} + +/*- + *----------------------------------------------------------------------- + * 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 $(<). + * If the context is VAR_GLOBAL though, we check if the variable + * was set in VAR_CMD from the command line and skip it if so. + *----------------------------------------------------------------------- + */ +void +Var_Set(const char *name, const char *val, GNode *ctxt, int flags) +{ + Var *v; + char *expanded_name = NULL; + + /* + * 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 (strchr(name, '$') != NULL) { + expanded_name = Var_Subst(NULL, name, ctxt, 0); + if (expanded_name[0] == 0) { + if (DEBUG(VAR)) { + fprintf(debug_file, "Var_Set(\"%s\", \"%s\", ...) " + "name expands to empty string - ignored\n", + name, val); + } + free(expanded_name); + return; + } + name = expanded_name; + } + if (ctxt == VAR_GLOBAL) { + v = VarFind(name, VAR_CMD, 0); + if (v != NULL) { + if ((v->flags & VAR_FROM_CMD)) { + if (DEBUG(VAR)) { + fprintf(debug_file, "%s:%s = %s ignored!\n", ctxt->name, name, val); + } + goto out; + } + VarFreeEnv(v, TRUE); + } + } + v = VarFind(name, ctxt, 0); + if (v == NULL) { + VarAdd(name, val, ctxt); + } else { + Buf_Empty(&v->val); + Buf_AddBytes(&v->val, strlen(val), val); + + if (DEBUG(VAR)) { + fprintf(debug_file, "%s:%s = %s\n", ctxt->name, name, val); + } + if ((v->flags & VAR_EXPORTED)) { + Var_Export1(name, VAR_EXPORT_PARENT); + } + } + /* + * 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 (v == NULL) { + /* we just added it */ + v = VarFind(name, ctxt, 0); + } + if (v != NULL) + v->flags |= VAR_FROM_CMD; + /* + * 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); + } + /* + * Another special case. + * Several make's support this sort of mechanism for tracking + * recursion - but each uses a different name. + * We allow the makefiles to update .MAKE.LEVEL and ensure + * children see a correctly incremented value. + */ + if (ctxt == VAR_GLOBAL && strcmp(MAKE_LEVEL, name) == 0) { + char tmp[64]; + int level; + + level = atoi(val); + snprintf(tmp, sizeof(tmp), "%u", level + 1); + setenv(MAKE_LEVEL, tmp, 1); +#ifdef MAKE_LEVEL_SAFE + setenv(MAKE_LEVEL_SAFE, tmp, 1); +#endif + } + + + out: + if (expanded_name != NULL) + free(expanded_name); + if (v != NULL) + VarFreeEnv(v, TRUE); +} + +/*- + *----------------------------------------------------------------------- + * 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; + char *expanded_name = NULL; + + if (strchr(name, '$') != NULL) { + expanded_name = Var_Subst(NULL, name, ctxt, 0); + if (expanded_name[0] == 0) { + if (DEBUG(VAR)) { + fprintf(debug_file, "Var_Append(\"%s\", \"%s\", ...) " + "name expands to empty string - ignored\n", + name, val); + } + free(expanded_name); + return; + } + name = expanded_name; + } + + v = VarFind(name, ctxt, (ctxt == VAR_GLOBAL) ? FIND_ENV : 0); + + if (v == NULL) { + VarAdd(name, val, ctxt); + } else { + Buf_AddByte(&v->val, ' '); + Buf_AddBytes(&v->val, strlen(val), val); + + if (DEBUG(VAR)) { + fprintf(debug_file, "%s:%s = %s\n", ctxt->name, name, + 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 (expanded_name != NULL) + free(expanded_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; + char *cp; + + if ((cp = strchr(name, '$')) != NULL) { + cp = Var_Subst(NULL, name, ctxt, FALSE); + } + v = VarFind(cp ? cp : name, ctxt, FIND_CMD|FIND_GLOBAL|FIND_ENV); + if (cp != NULL) { + free(cp); + } + if (v == NULL) { + return(FALSE); + } else { + (void)VarFreeEnv(v, TRUE); + } + 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 != NULL) { + char *p = (Buf_GetAll(&v->val, NULL)); + if (VarFreeEnv(v, FALSE)) + *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 MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, + char *word, Boolean addSpace, Buffer *buf, + void *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), 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, '.'); + } + 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 MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, + char *word, Boolean addSpace, Buffer *buf, + void *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), slash); + slash[-1] = '/'; + } else { + Buf_AddBytes(buf, strlen(word), 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 MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, + char *word, Boolean addSpace, Buffer *buf, + void *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), 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 MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, + char *word, Boolean addSpace, Buffer *buf, + void *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), word); + *dot = '.'; + } else { + Buf_AddBytes(buf, strlen(word), 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 MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, + char *word, Boolean addSpace, Buffer *buf, + void *pattern) +{ + if (DEBUG(VAR)) + fprintf(debug_file, "VarMatch [%s] [%s]\n", word, (char *)pattern); + if (Str_Match(word, (char *)pattern)) { + if (addSpace && vpstate->varSpace) { + Buf_AddByte(buf, vpstate->varSpace); + } + addSpace = TRUE; + Buf_AddBytes(buf, strlen(word), 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, + void *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), 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 MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, + char *word, Boolean addSpace, Buffer *buf, + void *pattern) +{ + if (!Str_Match(word, (char *)pattern)) { + if (addSpace && vpstate->varSpace) { + Buf_AddByte(buf, vpstate->varSpace); + } + addSpace = TRUE; + Buf_AddBytes(buf, strlen(word), 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 MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, + char *word, Boolean addSpace, Buffer *buf, + void *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, 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, pattern->rhs); + Buf_AddBytes(buf, wordLen - pattern->leftLen, + (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, word); + Buf_AddBytes(buf, pattern->rightLen, 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, word); + Buf_AddBytes(buf, pattern->rightLen, 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, 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, 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 errnum, regex_t *pat, const char *str) +{ + char *errbuf; + int errlen; + + errlen = regerror(errnum, pat, 0, 0); + errbuf = bmake_malloc(errlen); + regerror(errnum, 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 MAKE_ATTR_UNUSED, + Var_Parse_State *vpstate MAKE_ATTR_UNUSED, + char *word, Boolean addSpace, Buffer *buf, + void *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 :@<temp>@<string>@ 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 MAKE_ATTR_UNUSED, + Var_Parse_State *vpstate MAKE_ATTR_UNUSED, + char *word, Boolean addSpace, Buffer *buf, + void *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->errnum); + if (s != NULL && *s != '\0') { + if (addSpace && *s != '\n') + Buf_AddByte(buf, ' '); + Buf_AddBytes(buf, (slen = strlen(s)), 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 MAKE_ATTR_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_Init(&buf, 0); + addSpace = FALSE; + + if (vpstate->oneBigWord) { + /* fake what brk_string() would do if there were only one word */ + ac = 1; + av = bmake_malloc((ac + 1) * sizeof(char *)); + as = bmake_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]), av[i]); + addSpace = TRUE; + } + } + + free(as); + free(av); + + return Buf_Destroy(&buf, FALSE); +} + + +/*- + * VarRealpath -- + * Replace each word with the result of realpath() + * if successful. + */ +static Boolean +VarRealpath(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, + char *word, Boolean addSpace, Buffer *buf, + void *patternp MAKE_ATTR_UNUSED) +{ + struct stat st; + char rbuf[MAXPATHLEN]; + char *rp; + + if (addSpace && vpstate->varSpace) { + Buf_AddByte(buf, vpstate->varSpace); + } + addSpace = TRUE; + rp = realpath(word, rbuf); + if (rp && *rp == '/' && stat(rp, &st) == 0) + word = rp; + + Buf_AddBytes(buf, strlen(word), word); + return(addSpace); +} + +/*- + *----------------------------------------------------------------------- + * 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 *, void *), + void *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_Init(&buf, 0); + addSpace = FALSE; + + if (vpstate->oneBigWord) { + /* fake what brk_string() would do if there were only one word */ + ac = 1; + av = bmake_malloc((ac + 1) * sizeof(char *)); + as = bmake_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); + + return Buf_Destroy(&buf, FALSE); +} + + +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_Init(&buf, 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]), av[i]); + if (i != ac - 1) + Buf_AddByte(&buf, ' '); + } + + free(as); + free(av); + + return Buf_Destroy(&buf, FALSE); +} + + +/*- + *----------------------------------------------------------------------- + * 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_Init(&buf, 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]), av[i]); + if (i != ac - 1) + Buf_AddByte(&buf, ' '); + } + + free(as); + free(av); + + return Buf_Destroy(&buf, FALSE); +} + + +/*- + *----------------------------------------------------------------------- + * 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 MAKE_ATTR_UNUSED, + int errnum, const char **tstr, int delim, int *flags, + int *length, VarPattern *pattern) +{ + const char *cp; + char *rstr; + Buffer buf; + int junk; + + Buf_Init(&buf, 0); + 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, cp[1]); + cp++; + } else if (*cp == '$') { + if (cp[1] == delim) { + if (flags == NULL) + Buf_AddByte(&buf, *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; + void *freeIt; + + /* + * If unescaped dollar sign not before the + * delimiter, assume it's a variable + * substitution and recurse. + */ + cp2 = Var_Parse(cp, ctxt, errnum, &len, &freeIt); + Buf_AddBytes(&buf, strlen(cp2), cp2); + if (freeIt) + free(freeIt); + 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, cp); + cp = --cp2; + } else + Buf_AddByte(&buf, *cp); + } + } + } + else if (pattern && *cp == '&') + Buf_AddBytes(&buf, pattern->leftLen, pattern->lhs); + else + Buf_AddByte(&buf, *cp); + } + + if (*cp != delim) { + *tstr = cp; + *length = 0; + return NULL; + } + + *tstr = ++cp; + *length = Buf_Size(&buf); + rstr = Buf_Destroy(&buf, FALSE); + if (DEBUG(VAR)) + fprintf(debug_file, "Modifier pattern: \"%s\"\n", rstr); + 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 const char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~"; + const char *newline; + size_t len, nlen; + + if ((newline = Shell_GetNewline()) == NULL) + newline = "\\\n"; + nlen = strlen(newline); + + Buf_Init(&buf, 0); + while (*str != '\0') { + if ((len = strcspn(str, meta)) != 0) { + Buf_AddBytes(&buf, len, str); + str += len; + } else if (*str == '\n') { + Buf_AddBytes(&buf, nlen, newline); + ++str; + } else { + Buf_AddByte(&buf, '\\'); + Buf_AddByte(&buf, *str); + ++str; + } + } + str = Buf_Destroy(&buf, FALSE); + if (DEBUG(VAR)) + fprintf(debug_file, "QuoteMeta: [%s]\n", str); + return str; +} + +/*- + *----------------------------------------------------------------------- + * VarHash -- + * Hash the string using the MurmurHash3 algorithm. + * Output is computed using 32bit Little Endian arithmetic. + * + * Input: + * str String to modify + * + * Results: + * Hash value of str, encoded as 8 hex digits. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static char * +VarHash(char *str) +{ + static const char hexdigits[16] = "0123456789abcdef"; + Buffer buf; + size_t len, len2; + unsigned char *ustr = (unsigned char *)str; + uint32_t h, k, c1, c2; + int done; + + done = 1; + h = 0x971e137bU; + c1 = 0x95543787U; + c2 = 0x2ad7eb25U; + len2 = strlen(str); + + for (len = len2; len; ) { + k = 0; + switch (len) { + default: + k = (ustr[3] << 24) | (ustr[2] << 16) | (ustr[1] << 8) | ustr[0]; + len -= 4; + ustr += 4; + break; + case 3: + k |= (ustr[2] << 16); + case 2: + k |= (ustr[1] << 8); + case 1: + k |= ustr[0]; + len = 0; + } + c1 = c1 * 5 + 0x7b7d159cU; + c2 = c2 * 5 + 0x6bce6396U; + k *= c1; + k = (k << 11) ^ (k >> 21); + k *= c2; + h = (h << 13) ^ (h >> 19); + h = h * 5 + 0x52dce729U; + h ^= k; + } while (!done); + h ^= len2; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + + Buf_Init(&buf, 0); + for (len = 0; len < 8; ++len) { + Buf_AddByte(&buf, hexdigits[h & 15]); + h >>= 4; + } + + return Buf_Destroy(&buf, FALSE); +} + +/*- + *----------------------------------------------------------------------- + * 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_Init(&buf, 0); + for (; *str ; str++) { + Buf_AddByte(&buf, modProc(*str)); + } + return Buf_Destroy(&buf, FALSE); +} + +static char * +VarStrftime(const char *fmt, int zulu) +{ + char buf[BUFSIZ]; + time_t utc; + + time(&utc); + if (!*fmt) + fmt = "%c"; + strftime(buf, sizeof(buf), fmt, zulu ? gmtime(&utc) : localtime(&utc)); + + buf[sizeof(buf) - 1] = '\0'; + return bmake_strdup(buf); +} + +/* + * Now we need to apply any modifiers the user wants applied. + * These are: + * :M<pattern> words which match the given <pattern>. + * <pattern> is of the standard file + * wildcarding form. + * :N<pattern> words which do not match the given <pattern>. + * :S<d><pat1><d><pat2><d>[1gW] + * Substitute <pat2> for <pat1> in the value + * :C<d><pat1><d><pat2><d>[1gW] + * Substitute <pat2> for regex <pat1> 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. + * + * :?<true-value>:<false-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. + * :@<tmpvar>@<newval>@ + * Assign a temporary local variable <tmpvar> + * to the current value of each word in turn + * and replace each word with the result of + * evaluating <newval> + * :D<newval> Use <newval> as value if variable defined + * :U<newval> Use <newval> 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}'. + * :!<cmd>! 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 + * + * ::=<str> Assigns <str> as the new value of variable. + * ::?=<str> Assigns <str> as value of variable if + * it was not already set. + * ::+=<str> Appends <str> to variable. + * ::!=<cmd> Assigns output of <cmd> as the new value of + * variable. + */ + +/* we now have some modifiers with long names */ +#define STRMOD_MATCH(s, want, n) \ + (strncmp(s, want, n) == 0 && (s[n] == endc || s[n] == ':')) + +static char * +ApplyModifiers(char *nstr, const char *tstr, + int startc, int endc, + Var *v, GNode *ctxt, Boolean errnum, + int *lengthPtr, void **freePtr) +{ + const char *start; + const char *cp; /* Secondary pointer into str (place marker + * for tstr) */ + char *newStr; /* New value to return */ + char termc; /* Character which terminated scan */ + int cnt; /* Used to count brace pairs when variable in + * in parens or braces */ + char delim; + int modifier; /* that we are processing */ + Var_Parse_State parsestate; /* Flags passed to helper functions */ + + delim = '\0'; + parsestate.oneBigWord = FALSE; + parsestate.varSpace = ' '; /* word separator */ + + start = cp = tstr; + + while (*tstr && *tstr != endc) { + + if (*tstr == '$') { + /* + * We may have some complex modifiers in a variable. + */ + void *freeIt; + char *rval; + int rlen; + int c; + + rval = Var_Parse(tstr, ctxt, errnum, &rlen, &freeIt); + + /* + * If we have not parsed up to endc or ':', + * we are not interested. + */ + if (rval != NULL && *rval && + (c = tstr[rlen]) != '\0' && + c != ':' && + c != endc) { + if (freeIt) + free(freeIt); + goto apply_mods; + } + + if (DEBUG(VAR)) { + fprintf(debug_file, "Got '%s' from '%.*s'%.*s\n", + rval, rlen, tstr, rlen, tstr + rlen); + } + + tstr += rlen; + + if (rval != NULL && *rval) { + int used; + + nstr = ApplyModifiers(nstr, rval, + 0, 0, + v, ctxt, errnum, &used, freePtr); + if (nstr == var_Error + || (nstr == varNoError && errnum == 0) + || strlen(rval) != (size_t) used) { + if (freeIt) + free(freeIt); + goto out; /* error already reported */ + } + } + if (freeIt) + free(freeIt); + if (*tstr == ':') + tstr++; + else if (!*tstr && endc) { + Error("Unclosed variable specification after complex modifier (expecting '%c') for %s", endc, v->name); + goto out; + } + continue; + } + apply_mods: + if (DEBUG(VAR)) { + fprintf(debug_file, "Applying :%c to \"%s\"\n", *tstr, nstr); + } + newStr = var_Error; + switch ((modifier = *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; + + if (v->name[0] == 0) + goto bad_modifier; + + v_ctxt = ctxt; + sv_name = NULL; + ++tstr; + if (v->flags & VAR_JUNK) { + /* + * We need to bmake_strdup() it incase + * VarGetPattern() recurses. + */ + sv_name = v->name; + v->name = bmake_strdup(v->name); + } else if (ctxt != VAR_GLOBAL) { + Var *gv = VarFind(v->name, ctxt, 0); + if (gv == NULL) + v_ctxt = VAR_GLOBAL; + else + VarFreeEnv(gv, TRUE); + } + + switch ((how = *tstr)) { + case '+': + case '?': + case '!': + cp = &tstr[2]; + break; + default: + cp = ++tstr; + break; + } + delim = startc == PROPEN ? PRCLOSE : BRCLOSE; + pattern.flags = 0; + + pattern.rhs = VarGetPattern(ctxt, &parsestate, errnum, + &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; /* "::<unrecognised>" */ + } + case '@': + { + VarLoop_t loop; + int flags = VAR_NOSUBST; + + cp = ++tstr; + delim = '@'; + if ((loop.tvar = VarGetPattern(ctxt, &parsestate, errnum, + &cp, delim, + &flags, &loop.tvarLen, + NULL)) == NULL) + goto cleanup; + + if ((loop.str = VarGetPattern(ctxt, &parsestate, errnum, + &cp, delim, + &flags, &loop.strLen, + NULL)) == NULL) + goto cleanup; + + termc = *cp; + delim = '\0'; + + loop.errnum = errnum; + loop.ctxt = ctxt; + newStr = VarModify(ctxt, &parsestate, nstr, VarLoopExpand, + &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_Init(&buf, 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, cp[1]); + cp++; + } else if (*cp == '$') { + /* + * If unescaped dollar sign, assume it's a + * variable substitution and recurse. + */ + char *cp2; + int len; + void *freeIt; + + cp2 = Var_Parse(cp, ctxt, errnum, &len, &freeIt); + Buf_AddBytes(&buf, strlen(cp2), cp2); + if (freeIt) + free(freeIt); + cp += len - 1; + } else { + Buf_AddByte(&buf, *cp); + } + } + + 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 = 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 = bmake_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 == NULL || gn->type & OP_NOPATH) { + newStr = NULL; + } else if (gn->path) { + newStr = bmake_strdup(gn->path); + } else { + newStr = Dir_FindFile(v->name, Suff_FindPath(gn)); + } + if (!newStr) { + newStr = bmake_strdup(v->name); + } + cp = ++tstr; + termc = *tstr; + break; + } + case '!': + { + const char *emsg; + VarPattern pattern; + pattern.flags = 0; + + delim = '!'; + + cp = ++tstr; + if ((pattern.rhs = VarGetPattern(ctxt, &parsestate, errnum, + &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, + errnum, &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 = bmake_malloc(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 'g': + cp = tstr + 1; /* make sure it is set */ + if (STRMOD_MATCH(tstr, "gmtime", 6)) { + newStr = VarStrftime(nstr, 1); + cp = tstr + 6; + termc = *cp; + } else { + goto default_case; + } + break; + case 'h': + cp = tstr + 1; /* make sure it is set */ + if (STRMOD_MATCH(tstr, "hash", 4)) { + newStr = VarHash(nstr); + cp = tstr + 4; + termc = *cp; + } else { + goto default_case; + } + break; + case 'l': + cp = tstr + 1; /* make sure it is set */ + if (STRMOD_MATCH(tstr, "localtime", 9)) { + newStr = VarStrftime(nstr, 0); + cp = tstr + 9; + termc = *cp; + } else { + goto default_case; + } + 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<unrecognised><endc>" or + * ":ts<unrecognised>:" */ + parsestate.varSpace = tstr[2]; + cp = tstr + 3; + } else if (tstr[2] == endc || tstr[2] == ':') { + /* ":ts<endc>" 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<backslash><unrecognised>". + */ + goto bad_modifier; + } + break; + } + } else { + /* + * Found ":ts<unrecognised><unrecognised>". + */ + 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, + &pattern); + } else if (tstr[2] == endc || tstr[2] == ':') { + /* + * Check for two-character options: + * ":tu", ":tl" + */ + if (tstr[1] == 'A') { /* absolute path */ + newStr = VarModify(ctxt, &parsestate, nstr, + VarRealpath, NULL); + cp = tstr + 2; + termc = *cp; + } else 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<unrecognised>:" or + * ":t<unrecognised><endc>". */ + goto bad_modifier; + } + } else { + /* + * Found ":t<unrecognised><unrecognised>". + */ + goto bad_modifier; + } + } else { + /* + * Found ":t<endc>" or ":t:". + */ + goto bad_modifier; + } + break; + } + case 'N': + case 'M': + { + char *pattern; + const char *endpat; /* points just after end of pattern */ + char *cp2; + Boolean copy; /* pattern should be, or has been, copied */ + Boolean needSubst; + int nest; + + copy = FALSE; + needSubst = 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)) { + if (!needSubst) { + copy = TRUE; + } + cp++; + continue; + } + if (*cp == '$') { + needSubst = TRUE; + } + if (*cp == '(' || *cp == '{') + ++nest; + if (*cp == ')' || *cp == '}') { + --nest; + if (nest == 0) + break; + } + } + termc = *cp; + endpat = cp; + 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 = bmake_malloc(cp - tstr); + for (cp2 = pattern, cp = tstr + 1; + cp < endpat; + cp++, cp2++) + { + if ((*cp == '\\') && (cp+1 < endpat) && + (cp[1] == ':' || cp[1] == endc)) { + cp++; + } + *cp2 = *cp; + } + *cp2 = '\0'; + endpat = cp2; + } else { + /* + * Either Var_Subst or VarModify will need a + * nul-terminated string soon, so construct one now. + */ + pattern = bmake_strndup(tstr+1, endpat - (tstr + 1)); + } + if (needSubst) { + /* + * pattern contains embedded '$', so use Var_Subst to + * expand it. + */ + cp2 = pattern; + pattern = Var_Subst(NULL, cp2, ctxt, errnum); + free(cp2); + } + if (DEBUG(VAR)) + fprintf(debug_file, "Pattern for [%s] is [%s]\n", nstr, + pattern); + if (*tstr == 'M') { + newStr = VarModify(ctxt, &parsestate, nstr, VarMatch, + pattern); + } else { + newStr = VarModify(ctxt, &parsestate, nstr, VarNoMatch, + pattern); + } + 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, errnum, + &cp, delim, + &pattern.flags, + &pattern.leftLen, + NULL)) == NULL) + goto cleanup; + + if ((pattern.rhs = VarGetPattern(ctxt, &parsestate, errnum, + &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, + &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, errnum, + &cp, delim, NULL, + &pattern.leftLen, + NULL)) == NULL) + goto cleanup; + + /* BROPEN or PROPEN */ + delim = endc; + if ((pattern.rhs = VarGetPattern(ctxt, &parsestate, errnum, + &cp, delim, NULL, + &pattern.rightLen, + NULL)) == NULL) + goto cleanup; + + termc = *--cp; + delim = '\0'; + if (Cond_EvalExpression(NULL, 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, errnum, &cp, delim, + NULL, NULL, NULL)) == NULL) + goto cleanup; + + if ((pattern.replace = VarGetPattern(ctxt, &parsestate, + errnum, &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); + goto cleanup; + } + + pattern.nsub = pattern.re.re_nsub + 1; + if (pattern.nsub < 1) + pattern.nsub = 1; + if (pattern.nsub > 10) + pattern.nsub = 10; + pattern.matches = bmake_malloc(pattern.nsub * + sizeof(regmatch_t)); + newStr = VarModify(ctxt, &tmpparsestate, nstr, + VarRESubstitute, + &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, + NULL); + cp = tstr + 1; + termc = *cp; + break; + } + goto default_case; + case 'H': + if (tstr[1] == endc || tstr[1] == ':') { + newStr = VarModify(ctxt, &parsestate, nstr, VarHead, + NULL); + cp = tstr + 1; + termc = *cp; + break; + } + goto default_case; + case 'E': + if (tstr[1] == endc || tstr[1] == ':') { + newStr = VarModify(ctxt, &parsestate, nstr, VarSuffix, + NULL); + cp = tstr + 1; + termc = *cp; + break; + } + goto default_case; + case 'R': + if (tstr[1] == endc || tstr[1] == ':') { + newStr = VarModify(ctxt, &parsestate, nstr, VarRoot, + NULL); + 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: <string1>=<string2>) + */ + 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, + errnum, &cp, delim, &pattern.flags, + &pattern.leftLen, NULL)) == NULL) + goto cleanup; + delim = endc; + if ((pattern.rhs = VarGetPattern(ctxt, &parsestate, + errnum, &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'; + if (pattern.leftLen == 0 && *nstr == '\0') { + newStr = nstr; /* special case */ + } else { + newStr = VarModify(ctxt, &parsestate, nstr, + VarSYSVMatch, + &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)) { + fprintf(debug_file, "Result of :%c is \"%s\"\n", modifier, newStr); + } + + if (newStr != nstr) { + if (*freePtr) { + free(nstr); + *freePtr = NULL; + } + nstr = newStr; + if (nstr != var_Error && nstr != varNoError) { + *freePtr = nstr; + } + } + if (termc == '\0' && endc != '\0') { + Error("Unclosed variable specification (expecting '%c') for \"%s\" (value \"%s\") modifier %c", endc, v->name, nstr, modifier); + } else if (termc == ':') { + cp++; + } + tstr = cp; + } + out: + *lengthPtr = tstr - start; + return (nstr); + + bad_modifier: + /* "{(" */ + Error("Bad modifier `:%.*s' for %s", (int)strcspn(tstr, ":)}"), tstr, + v->name); + + cleanup: + *lengthPtr = cp - start; + if (delim != '\0') + Error("Unclosed substitution for %s (%c missing)", + v->name, delim); + if (*freePtr) { + free(*freePtr); + *freePtr = NULL; + } + return (var_Error); +} + +/*- + *----------------------------------------------------------------------- + * 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 + * errnum TRUE if undefined variables are an error + * lengthPtr OUT: The length of the specification + * freePtr OUT: Non-NULL if caller should free *freePtr + * + * 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...?). + * If *freePtr is non-NULL then it's a pointer that the caller + * should pass to free() to free memory used by the result. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +/* coverity[+alloc : arg-*4] */ +char * +Var_Parse(const char *str, GNode *ctxt, Boolean errnum, int *lengthPtr, + void **freePtr) +{ + const char *tstr; /* Pointer into str */ + Var *v; /* Variable in invocation */ + Boolean haveModifier;/* TRUE if have modifiers for the variable */ + char endc; /* Ending character when variable in parens + * or braces */ + char startc; /* Starting character when variable in parens + * or braces */ + int vlen; /* Length of variable name */ + const char *start; /* Points to original start of str */ + char *nstr; /* New string, used during expansion */ + 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 */ + char name[2]; + + *freePtr = NULL; + dynamic = FALSE; + start = str; + parsestate.oneBigWord = FALSE; + parsestate.varSpace = ' '; /* word separator */ + + startc = str[1]; + if (startc != PROPEN && startc != 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. + */ + + /* Error out some really stupid names */ + if (startc == '\0' || strchr(")}:$", startc)) { + *lengthPtr = 1; + return var_Error; + } + name[0] = startc; + name[1] = '\0'; + + v = VarFind(name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); + if (v == NULL) { + *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 (errnum ? var_Error : varNoError); + } else { + haveModifier = FALSE; + tstr = &str[1]; + endc = str[1]; + } + } else { + Buffer buf; /* Holds the variable name */ + + endc = startc == PROPEN ? PRCLOSE : BRCLOSE; + Buf_Init(&buf, 0); + + /* + * 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; + void *freeIt; + char *rval = Var_Parse(tstr, ctxt, errnum, &rlen, &freeIt); + if (rval != NULL) { + Buf_AddBytes(&buf, strlen(rval), rval); + } + if (freeIt) + free(freeIt); + tstr += rlen - 1; + } + else + Buf_AddByte(&buf, *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; + Buf_Destroy(&buf, TRUE); + return (var_Error); + } + str = Buf_GetAll(&buf, &vlen); + + /* + * At this point, str points into newly allocated memory from + * buf, containing only the name of the variable. + * + * start and tstr point into the const string that was pointed + * to by the original value of the str parameter. start points + * to the '$' at the beginning of the string, while tstr points + * to the char just after the end of the variable name -- this + * will be '\0', ':', PRCLOSE, or BRCLOSE. + */ + + v = VarFind(str, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); + /* + * Check also for bogus D and F forms of local variables since we're + * in a local context and the name is the right length. + */ + if ((v == NULL) && (ctxt != VAR_CMD) && (ctxt != VAR_GLOBAL) && + (vlen == 2) && (str[1] == 'F' || str[1] == 'D') && + strchr("@%*!<>", str[0]) != NULL) { + /* + * Well, it's local -- go look for it. + */ + name[0] = *str; + name[1] = '\0'; + v = VarFind(name, ctxt, 0); + + if (v != NULL) { + /* + * 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... + */ + nstr = Buf_GetAll(&v->val, NULL); + + if (str[1] == 'D') { + nstr = VarModify(ctxt, &parsestate, nstr, VarHead, + NULL); + } else { + nstr = VarModify(ctxt, &parsestate, nstr, VarTail, + NULL); + } + /* + * Resulting string is dynamically allocated, so + * tell caller to free it. + */ + *freePtr = nstr; + *lengthPtr = tstr-start+1; + Buf_Destroy(&buf, TRUE); + VarFreeEnv(v, TRUE); + return nstr; + } + } + + if (v == NULL) { + 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; + if (dynamic) { + char *pstr = bmake_strndup(start, *lengthPtr); + *freePtr = pstr; + Buf_Destroy(&buf, TRUE); + return(pstr); + } else { + Buf_Destroy(&buf, TRUE); + return (errnum ? 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 = bmake_malloc(sizeof(Var)); + v->name = UNCONST(str); + Buf_Init(&v->val, 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 = Buf_GetAll(&v->val, NULL); + if (strchr(nstr, '$') != NULL) { + nstr = Var_Subst(NULL, nstr, ctxt, errnum); + *freePtr = nstr; + } + + v->flags &= ~VAR_IN_USE; + + if ((nstr != NULL) && haveModifier) { + int used; + /* + * Skip initial colon. + */ + tstr++; + + nstr = ApplyModifiers(nstr, tstr, startc, endc, + v, ctxt, errnum, &used, freePtr); + tstr += used; + } + if (*tstr) { + *lengthPtr = tstr - start + 1; + } else { + *lengthPtr = tstr - start; + } + + if (v->flags & VAR_FROM_ENV) { + Boolean destroy = FALSE; + + if (nstr != Buf_GetAll(&v->val, NULL)) { + destroy = TRUE; + } else { + /* + * Returning the value unmodified, so tell the caller to free + * the thing. + */ + *freePtr = nstr; + } + VarFreeEnv(v, destroy); + } else if (v->flags & VAR_JUNK) { + /* + * Perform any free'ing needed and set *freePtr to NULL 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 = NULL; + } + if (dynamic) { + nstr = bmake_strndup(start, *lengthPtr); + *freePtr = nstr; + } else { + nstr = errnum ? var_Error : varNoError; + } + } + if (nstr != Buf_GetAll(&v->val, NULL)) + Buf_Destroy(&v->val, TRUE); + free(v->name); + free(v); + } + return (nstr); +} + +/*- + *----------------------------------------------------------------------- + * 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 \ */ + void *freeIt = NULL; /* Set if it should be freed */ + static Boolean errorReported; /* Set true if an error has already + * been reported to prevent a plethora + * of messages when recursing */ + + Buf_Init(&buf, 0); + 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, *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, 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, 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, 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, str); + str = p; + expand = FALSE; + } + else + expand = TRUE; + break; + } + } + if (!expand) + continue; + } + + val = Var_Parse(str, ctxt, undefErr, &length, &freeIt); + + /* + * 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, *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, val); + trailingBslash = length > 0 && val[length - 1] == '\\'; + } + if (freeIt) { + free(freeIt); + freeIt = NULL; + } + } + } + + return Buf_DestroyCompact(&buf); +} + +/*- + *----------------------------------------------------------------------- + * 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, NULL)); +} + +/*- + *----------------------------------------------------------------------- + * 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, NULL)); +} +#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(void *vp) +{ + Var *v = (Var *)vp; + fprintf(debug_file, "%-16s = %s\n", v->name, 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/20120704/wait.h b/20120704/wait.h new file mode 100644 index 0000000..7408d15 --- /dev/null +++ b/20120704/wait.h @@ -0,0 +1,81 @@ +/* NAME: + * wait.h - compensate for what vendors leave out + * + * AUTHOR: + * Simon J. Gerraty <sjg@crufty.net> + */ +/* + * RCSid: + * $Id: wait.h,v 1.6 2002/11/26 07:53:06 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 <sys/wait.h> + +#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 |