From b379932fc0f836f9f3f5ac89ef7f6cd38377dff8 Mon Sep 17 00:00:00 2001 From: "David E. O'Brien" Date: Fri, 8 Jun 2012 21:57:36 +0000 Subject: [PATCH] Import the 6-May-2012 release of the "Portable" BSD make tool (from NetBSD). Submitted by: sjg@juniper.net --- ChangeLog | 1346 ++++ FILES | 120 + Makefile.in | 181 + {usr.bin/make/PSD.doc => PSD.doc}/Makefile | 1 + {usr.bin/make/PSD.doc => PSD.doc}/tutorial.ms | 105 +- README | 47 + aclocal.m4 | 77 + usr.bin/make/arch.c => arch.c | 802 +- bmake.1 | 2043 +++++ bmake.cat1 | 1305 +++ boot-strap | 388 + bsd.after-import.mk | 87 + buf.c | 291 + buf.h | 119 + usr.bin/make/compat.c => compat.c | 592 +- cond.c | 1410 ++++ config.h.in | 314 + configure | 7134 +++++++++++++++++ configure.in | 370 + usr.bin/make/dir.c => dir.c | 1300 ++- dir.h | 108 + dirname.c | 95 + find_lib.sh | 13 + for.c | 496 ++ getopt.c | 179 + usr.bin/make/hash.c => hash.c | 185 +- usr.bin/make/hash.h => hash.h | 86 +- install-sh | 201 + job.c | 2988 +++++++ job.h | 272 + usr.bin/make/lst.h => lst.h | 126 +- lst.lib/Makefile | 0 {usr.bin/make/lst.lib => lst.lib}/lstAppend.c | 65 +- {usr.bin/make/lst.lib => lst.lib}/lstAtEnd.c | 37 +- .../make/lst.lib => lst.lib}/lstAtFront.c | 31 +- {usr.bin/make/lst.lib => lst.lib}/lstClose.c | 29 +- {usr.bin/make/lst.lib => lst.lib}/lstConcat.c | 67 +- {usr.bin/make/lst.lib => lst.lib}/lstDatum.c | 32 +- .../make/lst.lib => lst.lib}/lstDeQueue.c | 44 +- .../make/lst.lib => lst.lib}/lstDestroy.c | 57 +- {usr.bin/make/lst.lib => lst.lib}/lstDupl.c | 64 +- .../make/lst.lib => lst.lib}/lstEnQueue.c | 29 +- {usr.bin/make/lst.lib => lst.lib}/lstFind.c | 28 +- .../make/lst.lib => lst.lib}/lstFindFrom.c | 60 +- {usr.bin/make/lst.lib => lst.lib}/lstFirst.c | 28 +- .../make/lst.lib => lst.lib}/lstForEach.c | 28 +- .../make/lst.lib => lst.lib}/lstForEachFrom.c | 74 +- {usr.bin/make/lst.lib => lst.lib}/lstInit.c | 39 +- {usr.bin/make/lst.lib => lst.lib}/lstInsert.c | 61 +- {usr.bin/make/lst.lib => lst.lib}/lstInt.h | 35 +- .../make/lst.lib => lst.lib}/lstIsAtEnd.c | 24 +- .../make/lst.lib => lst.lib}/lstIsEmpty.c | 26 +- {usr.bin/make/lst.lib => lst.lib}/lstLast.c | 28 +- {usr.bin/make/lst.lib => lst.lib}/lstMember.c | 39 +- {usr.bin/make/lst.lib => lst.lib}/lstNext.c | 48 +- {usr.bin/make/lst.lib => lst.lib}/lstOpen.c | 30 +- usr.bin/make/dir.h => lst.lib/lstPrev.c | 85 +- {usr.bin/make/lst.lib => lst.lib}/lstRemove.c | 49 +- .../make/lst.lib => lst.lib}/lstReplace.c | 27 +- {usr.bin/make/lst.lib => lst.lib}/lstSucc.c | 30 +- machine.sh | 96 + main.c | 2078 +++++ make-bootstrap.sh.in | 84 + usr.bin/make/config.h => make-conf.h | 94 +- make.1 | 2061 +++++ make.c | 1561 ++++ usr.bin/make/make.h => make.h | 347 +- make_malloc.c | 119 + make_malloc.h | 41 + meta.c | 1346 ++++ meta.h | 54 + mkdeps.sh | 314 + nonints.h | 206 + os.sh | 228 + parse.c | 3122 ++++++++ usr.bin/make/pathnames.h => pathnames.h | 37 +- ranlib.h | 32 + realpath.c | 196 + setenv.c | 154 + sigcompat.c | 325 + usr.bin/make/sprite.h => sprite.h | 71 +- usr.bin/make/str.c => str.c | 231 +- stresep.c | 89 + strlcpy.c | 63 + strlist.c | 93 + strlist.h | 62 + usr.bin/make/suff.c => suff.c | 1772 ++-- targ.c | 848 ++ trace.c | 116 + trace.h | 49 + unit-tests/Makefile.in | 95 + unit-tests/comment | 31 + unit-tests/cond1 | 109 + unit-tests/doterror | 20 + unit-tests/dotwait | 61 + unit-tests/error | 10 + unit-tests/export | 22 + unit-tests/export-all | 23 + unit-tests/forsubst | 10 + unit-tests/hash | 18 + unit-tests/misc | 16 + unit-tests/moderrs | 31 + unit-tests/modmatch | 25 + unit-tests/modmisc | 38 + unit-tests/modorder | 22 + unit-tests/modts | 43 + unit-tests/modword | 151 + unit-tests/phony-end | 9 + unit-tests/posix | 24 + unit-tests/qequals | 8 + unit-tests/sysv | 26 + unit-tests/ternary | 8 + unit-tests/test.exp | 351 + unit-tests/unexport | 8 + unit-tests/unexport-env | 14 + unit-tests/varcmd | 49 + usr.bin/make/Makefile | 19 - usr.bin/make/Makefile.boot | 36 - usr.bin/make/bit.h | 100 - usr.bin/make/buf.c | 436 - usr.bin/make/buf.h | 80 - usr.bin/make/cond.c | 1252 --- usr.bin/make/for.c | 302 - usr.bin/make/job.c | 2668 ------ usr.bin/make/job.h | 233 - usr.bin/make/list.h | 298 - usr.bin/make/lst.lib/Makefile | 10 - usr.bin/make/main.c | 954 --- usr.bin/make/make.1 | 890 -- usr.bin/make/make.c | 908 --- usr.bin/make/nonints.h | 140 - usr.bin/make/parse.c | 2661 ------ usr.bin/make/targ.c | 657 -- usr.bin/make/util.c | 351 - usr.bin/make/var.c | 2033 ----- util.c | 619 ++ var.c | 4194 ++++++++++ wait.h | 81 + 138 files changed, 43381 insertions(+), 16627 deletions(-) create mode 100644 ChangeLog create mode 100644 FILES create mode 100644 Makefile.in rename {usr.bin/make/PSD.doc => PSD.doc}/Makefile (64%) rename {usr.bin/make/PSD.doc => PSD.doc}/tutorial.ms (97%) create mode 100644 README create mode 100644 aclocal.m4 rename usr.bin/make/arch.c => arch.c (58%) create mode 100644 bmake.1 create mode 100644 bmake.cat1 create mode 100755 boot-strap create mode 100644 bsd.after-import.mk create mode 100644 buf.c create mode 100644 buf.h rename usr.bin/make/compat.c => compat.c (52%) create mode 100644 cond.c create mode 100644 config.h.in create mode 100755 configure create mode 100644 configure.in rename usr.bin/make/dir.c => dir.c (50%) create mode 100644 dir.h create mode 100644 dirname.c create mode 100755 find_lib.sh create mode 100644 for.c create mode 100644 getopt.c rename usr.bin/make/hash.c => hash.c (68%) rename usr.bin/make/hash.h => hash.h (54%) create mode 100755 install-sh create mode 100644 job.c create mode 100644 job.h rename usr.bin/make/lst.h => lst.h (53%) create mode 100644 lst.lib/Makefile rename {usr.bin/make/lst.lib => lst.lib}/lstAppend.c (76%) rename {usr.bin/make/lst.lib => lst.lib}/lstAtEnd.c (77%) rename {usr.bin/make/lst.lib => lst.lib}/lstAtFront.c (80%) rename {usr.bin/make/lst.lib => lst.lib}/lstClose.c (83%) rename {usr.bin/make/lst.lib => lst.lib}/lstConcat.c (80%) rename {usr.bin/make/lst.lib => lst.lib}/lstDatum.c (78%) rename {usr.bin/make/lst.lib => lst.lib}/lstDeQueue.c (75%) rename {usr.bin/make/lst.lib => lst.lib}/lstDestroy.c (69%) rename {usr.bin/make/lst.lib => lst.lib}/lstDupl.c (67%) rename {usr.bin/make/lst.lib => lst.lib}/lstEnQueue.c (80%) rename {usr.bin/make/lst.lib => lst.lib}/lstFind.c (77%) rename {usr.bin/make/lst.lib => lst.lib}/lstFindFrom.c (71%) rename {usr.bin/make/lst.lib => lst.lib}/lstFirst.c (80%) rename {usr.bin/make/lst.lib => lst.lib}/lstForEach.c (80%) rename {usr.bin/make/lst.lib => lst.lib}/lstForEachFrom.c (72%) rename {usr.bin/make/lst.lib => lst.lib}/lstInit.c (78%) rename {usr.bin/make/lst.lib => lst.lib}/lstInsert.c (75%) rename {usr.bin/make/lst.lib => lst.lib}/lstInt.h (76%) rename {usr.bin/make/lst.lib => lst.lib}/lstIsAtEnd.c (85%) rename {usr.bin/make/lst.lib => lst.lib}/lstIsEmpty.c (80%) rename {usr.bin/make/lst.lib => lst.lib}/lstLast.c (80%) rename {usr.bin/make/lst.lib => lst.lib}/lstMember.c (74%) rename {usr.bin/make/lst.lib => lst.lib}/lstNext.c (79%) rename {usr.bin/make/lst.lib => lst.lib}/lstOpen.c (82%) rename usr.bin/make/dir.h => lst.lib/lstPrev.c (56%) rename {usr.bin/make/lst.lib => lst.lib}/lstRemove.c (80%) rename {usr.bin/make/lst.lib => lst.lib}/lstReplace.c (81%) rename {usr.bin/make/lst.lib => lst.lib}/lstSucc.c (79%) create mode 100755 machine.sh create mode 100644 main.c create mode 100755 make-bootstrap.sh.in rename usr.bin/make/config.h => make-conf.h (54%) create mode 100644 make.1 create mode 100644 make.c rename usr.bin/make/make.h => make.h (55%) create mode 100644 make_malloc.c create mode 100644 make_malloc.h create mode 100644 meta.c create mode 100644 meta.h create mode 100755 mkdeps.sh create mode 100644 nonints.h create mode 100755 os.sh create mode 100644 parse.c rename usr.bin/make/pathnames.h => pathnames.h (66%) create mode 100644 ranlib.h create mode 100644 realpath.c create mode 100644 setenv.c create mode 100644 sigcompat.c rename usr.bin/make/sprite.h => sprite.h (60%) rename usr.bin/make/str.c => str.c (68%) create mode 100644 stresep.c create mode 100644 strlcpy.c create mode 100644 strlist.c create mode 100644 strlist.h rename usr.bin/make/suff.c => suff.c (59%) create mode 100644 targ.c create mode 100644 trace.c create mode 100644 trace.h create mode 100644 unit-tests/Makefile.in create mode 100644 unit-tests/comment create mode 100644 unit-tests/cond1 create mode 100644 unit-tests/doterror create mode 100644 unit-tests/dotwait create mode 100644 unit-tests/error create mode 100644 unit-tests/export create mode 100644 unit-tests/export-all create mode 100644 unit-tests/forsubst create mode 100644 unit-tests/hash create mode 100644 unit-tests/misc create mode 100644 unit-tests/moderrs create mode 100644 unit-tests/modmatch create mode 100644 unit-tests/modmisc create mode 100644 unit-tests/modorder create mode 100644 unit-tests/modts create mode 100644 unit-tests/modword create mode 100644 unit-tests/phony-end create mode 100644 unit-tests/posix create mode 100644 unit-tests/qequals create mode 100644 unit-tests/sysv create mode 100644 unit-tests/ternary create mode 100644 unit-tests/test.exp create mode 100644 unit-tests/unexport create mode 100644 unit-tests/unexport-env create mode 100644 unit-tests/varcmd delete mode 100644 usr.bin/make/Makefile delete mode 100644 usr.bin/make/Makefile.boot delete mode 100644 usr.bin/make/bit.h delete mode 100644 usr.bin/make/buf.c delete mode 100644 usr.bin/make/buf.h delete mode 100644 usr.bin/make/cond.c delete mode 100644 usr.bin/make/for.c delete mode 100644 usr.bin/make/job.c delete mode 100644 usr.bin/make/job.h delete mode 100644 usr.bin/make/list.h delete mode 100644 usr.bin/make/lst.lib/Makefile delete mode 100644 usr.bin/make/main.c delete mode 100644 usr.bin/make/make.1 delete mode 100644 usr.bin/make/make.c delete mode 100644 usr.bin/make/nonints.h delete mode 100644 usr.bin/make/parse.c delete mode 100644 usr.bin/make/targ.c delete mode 100644 usr.bin/make/util.c delete mode 100644 usr.bin/make/var.c create mode 100644 util.c create mode 100644 var.c create mode 100644 wait.h diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 000000000000..7c4875ebeefc --- /dev/null +++ b/ChangeLog @@ -0,0 +1,1346 @@ +2012-06-06 Simon J. Gerraty + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * Makefile.in: replace use of .Nx in bmake.1 with NetBSD + since some systems cannot cope with .Nx + +2011-11-14 Simon J. Gerraty + + * 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 + + * Makefile.in (MAKE_VERSION): bump version to 20111010 + +2011-10-09 Simon J. Gerraty + + * 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 + + * 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 + + * Makefile.in (MAKE_VERSION): 20110909 is a better number ;-) + +2011-09-05 Simon J. Gerraty + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * Merge with NetBSD make, pick up + o unit-tests/modts now works on MirBSD + +2011-06-04 Simon J. Gerraty + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * parse.c: SunOS 5.8 at least does not have MAP_FILE + +2011-01-01 Simon J. Gerraty + + * 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 + + * 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 + + * 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 + + * 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 + + * Makefile.in (MAKE_VERSION): bump version to 20101101 + +2010-10-16 Simon J. Gerraty + + * machine.sh: like os.sh, + allow for uname -p producing useless drivel + +2010-09-13 Simon J. Gerraty + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * Makefile.in (MAKE_VERSION): bump version to 20100606 + Merge with NetBSD make, pick up + o man page update + +2010-06-05 Simon J. Gerraty + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * boot-strap: add FindHereOrAbove so we can use -m .../mk + +2010-04-20 Simon J. Gerraty + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * Makefile.in (MAKE_VERSION): bump version to 20100102 + * Merge with NetBSD make, pick up: + o fix for -m .../ + +2009-11-18 Simon J. Gerraty + + * 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 + + * configure.in: Ensure that srcdir and mksrc are absolute paths. + +2009-10-09 Simon J. Gerraty + + * Makefile.in (MAKE_VERSION): fix version to 20091007 + +2009-10-07 Simon J. Gerraty + + * 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 + + * 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 + + * 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 + + * configure.in: Don't -D__EXTENSIONS__ if + AC_USE_SYSTEM_EXTENSIONS says "no". + +2009-08-26 Simon J. Gerraty + + * 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 + + * Appy some patches for MiNT from David Brownlee + +2009-02-26 Simon J. Gerraty + + * 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 + + * 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 + + * 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 + + * 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 + + * Makefile.in (BMAKE_VERSION): bump version to 20081030 + pick up man page tweaks. + +2008-10-29 Simon J. Gerraty + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * Makefile.in (BMAKE_VERSION): bump version to 20071022 + * Merge with NetBSD make, pick up: + o Allow .PATH to be used for .include "" + + * boot-strap: source default settings from .bmake-boot-strap.rc + +2007-10-16 Simon J. Gerraty + + * 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 + + * boot-strap: make bmake.1 too, so maninstall works. + +2007-10-14 Simon J. Gerraty + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * Makefile.in (BMAKE_VERSION): bump to 20051105 + * configure.in: always set default_sys_path + default is ${prefix}/share/mk + - remove prefix_sys_path, anyone wanting more than above + needs to set it manually. + +2005-11-04 Simon J. Gerraty + + * boot-strap: make this a bit easier for pkgsrc folk. + bootstrap still fails on IRIX64 since MACHINE_ARCH gets set to + 'mips' while pkgsrc wants 'mipseb' or 'mipsel' + +2005-11-02 Simon J. Gerraty + + * Makefile.in (BMAKE_VERSION): bump to 20051102 + * job.c (JobFinish): fix likely ancient merge lossage + fix from Todd Vierling. + * boot-strap (srcdir): allow setting mksrc=none + +2005-10-31 Simon J. Gerraty + + * Makefile.in (BMAKE_VERSION): bump to 20051031 + * ranlib.h: skip on OSF too. + (NetBSD PR 31864) + +2005-10-10 Simon J. Gerraty + + * Makefile.in (BMAKE_VERSION): bump to 20051002 + fix a silly typo + +2005-10-09 Simon J. Gerraty + + * Makefile.in (BMAKE_VERSION): bump to 20051001 + support for UnixWare and some other systems, + based on patches from pkgsrc/bootstrap + +2005-09-03 Simon J. Gerraty + + * Makefile.in (BMAKE_VERSION): bump to 20050901 + * Merge with NetBSD make, pick up: + o possible parse error causing us to wander off. + +2005-06-06 Simon J. Gerraty + + * Makefile.in (BMAKE_VERSION): bump to 20050606 + * Merge with NetBSD make, pick up: + o :0x modifier for randomizing a list + o fixes for a number of -Wuninitialized issues. + +2005-05-30 Simon J. Gerraty + + * Makefile.in (BMAKE_VERSION): bump to 20050530 + * Merge with NetBSD make, pick up: + o Handle dependencies for .BEGIN, .END and .INTERRUPT + + * README: was seriously out of date. + +2005-03-22 Simon J. Gerraty + + * Important to use .MAKE rather than MAKE. + +2005-03-15 Simon J. Gerraty + + * Makefile.in (BMAKE_VERSION): bump to 20050315 + * Merge with NetBSD make, pick up: + o don't mistake .elsefoo for .else + o use suffix-specific search path correctly + o bunch of style nits + +2004-05-11 Simon J. Gerraty + + * boot-strap: + o ensure that args to --src and --with-mksrc + are resolved before giving them to configure. + o add -o "objdir" so that builder can control it, + default is $OS as determined by os.sh + o add -q to suppress all the install instructions. + +2004-05-08 Simon J. Gerraty + + * Remove __IDSTRING() + + * Makefile.in (BMAKE_VERSION): bump to 20040508 + * Merge with NetBSD make, pick up: + o posix fixes + - remove '-e' from compat mode + - add support for '+' command-line prefix. + o fix for handling '--' on command-line. + o fix include in lst.lib/lstInt.h to simplify '-I's + o we also picked up replacement of MAKE_BOOTSTRAP + with !MAKE_NATIVE which is a noop, but possibly confusing. + +2004-04-14 Simon J. Gerraty + + * Makefile.in (BMAKE_VERSION): bump to 20040414 + * Merge with NetBSD make, pick up: + o allow quoted strings on lhs of conditionals + o issue warning when extra .else is seen + o print line numer when errors encountered during parsing from + string. + +2004-02-20 Simon J. Gerraty + + * Makefile.in (BMAKE_VERSION): bump to 20040220 + * Merge with NetBSD make, pick up: + o fix for old :M parsing bug. + o re-jigged unit-tests + +2004-02-15 Simon J. Gerraty + + * Makefile.in (accept test): use ${.MAKE:S,^./,${.CURDIR}/,} + so that './bmake -f Makefile test' works. + +2004-02-14 Simon J. Gerraty + + * Makefile.in: (BMAKE_VERSION): bump to 20040214 + * Merge with NetBSD make, pick up: + o search upwards for *.mk + o fix for double free of var substitution buffers + o use of getopt replaced with custom code, since the usage + (re-scanning) isn't posix compatible. + +2004-02-12 Simon J. Gerraty + + * arch.c: don't include ranlib.h on ELF systems + (thanks to Chuck Cranor ). + +2004-01-18 Simon J. Gerraty + + * Makefile.in (BMAKE_VERSION): bump to 20040118 + + * boot-strap (while): export vars we assign to on cmdline + * unit-test/Makefile.in: ternary is .PHONY + +2004-01-08 Simon J. Gerraty + + * Makefile.in (BMAKE_VERSION): bump version to 20040108 + * Merge with NetBSD make, pick up: + o fix for ternary modifier + +2004-01-06 Simon J. Gerraty + + * Makefile.in (BMAKE_VERSION): bump version to 20040105 + * Merge with NetBSD make, pick up: + o fix for cond.c to handle compound expressions better + o variable expansion within sysV style replacements + +2003-12-22 Simon J. Gerraty + + * Make portable snprintf safer - output to /dev/null first to + check space needed. + + * Makefile.in (BMAKE_VERSION): bump version to 20031222 + * Merge with NetBSD make, pick up: + o -dg3 to show input graph when things go wrong. + o explicitly look for makefiles in objdir if not found in curdir so + that errors in .depend etc will be reported accurarely. + o avoid use of -e in shell scripts in jobs mode, use '|| exit $?' + instead as it more accurately reflects the expected behavior and + is more consistently implemented. + o avoid use of asprintf. + +2003-09-28 Simon J. Gerraty + + * util.c: Add asprintf and vasprintf. + + * Makefile.in (BMAKE_VERSION): bump version to 20030928 + * Merge with NetBSD make, pick up: + :[] modifier - allows picking words from a variable. + :tW modifier - allows treating value as one big word. + W flag for :C and :S - allows treating value as one big word. + +2003-09-12 Simon J. Gerraty + + * Merge with NetBSD make + pick up -de flag to enable printing failed command. + don't skip 1st two dir entries (normally . and ..) since + coda does not have them. + +2003-09-09 Simon J. Gerraty + + * Makefile.in (BMAKE_VERSION): bump version to 20030909 + * Merge with NetBSD make, pick up: + - changes for -V '${VAR}' to print fully expanded value + cf. -V VAR + - CompatRunCommand now prints the command that failed. + - several files got updated 3 clause Berkeley license. + +2003-08-02 Simon J. Gerraty + + * boot-strap: Allow setting configure args on command line. + +2003-07-31 Simon J. Gerraty + + * configure.in: add --with-defshell to allow sh or ksh + to be selected as default shell. + + * Makefile.in: bump version to 20030731 + + * Merge with NetBSD make + Pick up .SHELL spec for ksh and associate man page changes. + Also compat mode now uses the same shell specs. + +2003-07-29 Simon J. Gerraty + + * var.c (Var_Parse): ensure delim is initialized. + + * unit-tests/Makefile.in: use single quotes to avoid problems from + some shells. + + * makefile.boot.in: + Run the unit-tests as part of the bootstrap procedure. + +2003-07-28 Simon J. Gerraty + + * unit-tests/Makefile.in: always force complaints from + ${TEST_MAKE} to be from 'make'. + + * configure.in: add check for 'diff -u' + also fix some old autoconf'isms + + * Makefile.in (BMAKE_VERSION): bump version to 20030728. + if using GCC add -Wno-cast-qual to CFLAGS for var.o + + * Merge with NetBSD make + Pick up fix for :ts parsing error in some cases. + Pick unit-tests. + +2003-07-23 Simon J. Gerraty + + * Makefile.in (BMAKE_VERSION): bump version to 20030723. + + * var.c (Var_Parse): fix bug in :ts modifier, after const + correctness fixes, must pass nstr to VarModify. + +2003-07-14 Simon J. Gerraty + + * Makefile.in: BMAKE_VERSION switch to a date based version. + We'll generally use the date of last import from NetBSD. + + * Merge with NetBSD make + Pick up fixes for const-correctness, now passes WARNS=3 on + NetBSD. + Pick up :ts modifier, allows controlling the separator used + between words in variable expansion. + +2003-07-11 Simon J. Gerraty + + * FILES: include boot-strap and os.sh + + * Makefile.in: only set WARNS if we are NetBSD, the effect on + FreeBSD is known to be bad. + + * makefile.boot.in (bootstrap): make this the default target. + + * Makefile.in: bump version to 3.1.19 + + * machine.sh: avoid A-Z with tr as it is bound to lose. + +2003-07-10 Simon J. Gerraty + + * Merge with NetBSD make + Pick up fix for PR/19781 - unhelpful error msg on unclosed ${var:foo + Plus some doc fixes. + +2003-04-27 Simon J. Gerraty + + * Merge with NetBSD make + Pick up fix for PR/1523 - don't count a library as built, if there + is no way to build it + + * Bump version to 3.1.18 + +2003-03-23 Simon J. Gerraty + + * Merge with NetBSD make + Pick up fix for ParseDoSpecialSrc - we only use it if .WAIT + appears in src list. + +2003-03-21 Simon J. Gerraty + + * Merge with NetBSD make (mmm 10th anniversary!) + pick up fix for .WAIT in srcs that refer to $@ or $* (PR#20828) + pick up -X which tells us to not export VAR=val via setenv if + we are already doing so via MAKEFLAGS. This saves valuable env + space on systems like Darwin. + set MAKE_VERSION to 3.1.17 + + * parse.c: pix up fix for suffix rules + +2003-03-06 Simon J. Gerraty + + * Merge with NetBSD make. + pick up fix for propagating -B via MAKEFLAGS. + set MAKE_VERSION to 3.1.16 + + * Apply some patches from pkgsrc-bootstrap/bmake + Originally by Grant Beattie + I may have missed some - since they are based on bmake-3.1.12 + +2002-12-03 Simon J. Gerraty + + * makefile.boot.in (bmake): update install targets for those that + use them, also clear MAKEFLAGS when invoking bmake.boot to avoid + havoc from gmake -w. Thanks to Harlan Stenn . + + * bmake.cat1: update the pre-formatted man page! + +2002-11-30 Simon J. Gerraty + + * Merge with NetBSD make. + pick up fix for premature free of pointer used in call + to Dir_InitCur(). + set MAKE_VERSION to 3.1.15 + +2002-11-26 Simon J. Gerraty + + * configure.in: determine suitable value for MKSRC. + override using --with-mksrc=PATH. + + * machine.sh: use `uname -p` for MACHINE_ARCH on modern SunOS systems. + configs(8) will use 'sun4' as an alias for 'sparc'. + +2002-11-25 Simon J. Gerraty + + * Merge with NetBSD make. + pick up ${.PATH} + pick up fix for finding ../cat.c via .PATH when .CURDIR=.. + set MAKE_VERSION to 3.1.14 + add configure checks for killpg and sys/socket.h + +2002-09-16 Simon J. Gerraty + + * tag bmake-3-1-13 + + * makefile.boot.in (bmake): use install-mk + Also setup ./mk before trying to invoke bmake.boot incase we + needed install-mk to create a sys.mk for us. + + * configure.in: If we need to add -I${srcdir}/missing, make it an + absolute path so that it works for lst.lib too. + + * make.h: always include sys/cdefs.h since we provide one if the + host does not. + + * Makefile.in (install-mk): + use MKSRC/install-mk which will do the right thing. + use uname -p for ARCH if possible. + since install-mk will setup links bsd.prog.mk -> prog.mk if + needed, just .include bsd.prog.mk + + * Merge with NetBSD make (NetBSD-1.6) + Code is ansi-C only now. + Bug in handling of dotLast is fixed. + Can now assign .OBJDIR and make will reset its notions of life. + New modifiers :tu :tl for toUpper and toLower. + +Tue Oct 16 12:18:42 2001 Simon J. Gerraty + + * Merge with NetBSD make + pick up fix for .END failure in compat mode. + pick up fix for extra va_end() in ParseVErrorInternal. + +Thu Oct 11 13:20:06 2001 Simon J. Gerraty + + * configure.in: for systems that have sys/cdefs.h check if it is + compatible. If not, include the one under missing, but tell it to + include the native one too - necessary on Linux. + + * missing/sys/cdefs.h: if NEED_HOST_CDEFS_H is defined, use + include_next (for gcc) to get the native sys/cdefs.h + +Tue Aug 21 02:29:34 2001 Simon J. Gerraty + + * job.c (JobFinish): Fix an earlier merge bug that resulted in + leaking descriptors when using -jN. + + * job.c (JobPrintCommand): See if "curdir" exists before + attempting to chdir(). Doing the chdir directly in make (when in + compat mode) fails silently, so let the -jN version do the same. + This can happen when building kernels in an object tree and + playing clever games to reset .CURDIR. + + * Merged with NetBSD make + pick up .USEBEFORE + +Tue Jun 26 23:45:11 2001 Simon J. Gerraty + + * makefile.boot.in: Give bmake.boot a MAKESYSPATH that might work. + +Tue Jun 12 16:48:57 2001 Simon J. Gerraty + + * var.c (Var_Set): Add 4th (flags) arg so VarLoopExpand can tell + us not to export the iterator variable when using VAR_CMD context. + +Sun Jun 10 21:55:21 2001 Simon J. Gerraty + + * job.c (Job_CatchChildren): don't call Job_CatchOutput() here, + its the wrong "fix". + +Sat Jun 9 00:11:24 2001 Simon J. Gerraty + + * Redesigned export of VAR_CMD's via MAKEFLAGS. + We now simply append the variable names to .MAKEOVERRIDES, and + handle duplicate suppression and quoting in ExportMAKEFLAGS using: + ${.MAKEOVERRIDES:O:u:@v@$v=${$v:Q}@} + Apart from fixing quoting bugs in previous version, this allows us + to export vars to the environment by simply doing: + .MAKEOVERRIDES+= PATH + Merged again with NetBSD make, but the above is the only change. + + * configure.in: added + --disable-pwd-override disable $PWD overriding getcwd() + --disable-check-make-chdir disable make trying to guess + when it should automatically cd ${.CURDIR} + + * Merge with NetBSD make, changes include: + parse.c (ParseDoDependency): Spot that the syntax error is + caused by an unresolved cvs/rcs conflict and say so. + var.c: most of Var* functions now take a ctxt as 1st arg. + now does variable substituion on rhs of sysv style modifiers. + + * var.c (Var_Set): exporting of command line variables (VAR_CMD) + is now done here. We append the name='value' to .MAKEOVERRIDES + rather than directly into MAKEFLAGS as this allows a Makefile to + use .MAKEOVERRIDES= to disable this behaviour. GNU make uses a + very similar mechanism. Note that in adding name='value' to + .MAKEOVERRIDES we do the moral equivalent of: + .MAKEOVERRIDES:= ${.MAKEOVERRIDES:Nname=*} name='val' + +Fri Jun 1 14:08:02 2001 Simon J. Gerraty + + * make-conf.h (USE_IOVEC): make it conditional on HAVE_SYS_UIO_H + + * Merged with NetBSD make + make -dx can now be used to run commands via sh -x + better error messages on exec failures. + +Thu May 31 01:44:54 2001 Simon J. Gerraty + + * Makefile.in (main.o): depends on ${SRCS} ${MAKEFILE} so that + MAKE_VERSION gets updated. Also don't use ?= for MAKE_VERSION, + MACHINE etc otherwise they propagate from the previous bmake. + + * configure.in (machine): allow --with-machine=generic to make + configure use machine.sh to set MACHINE. + + * job.c (JobInterrupt): convert to using WAIT_T and friends. + + * Makefile.in: mention in bmake.1 that we use autoconf. + + * make.1: mention MAKE_PRINT_VAR_ON_ERROR. + +Wed May 30 23:17:18 2001 Simon J. Gerraty + + * main.c (ReadMakefile): don't set MAKEFILE if reading ".depend" + as that rather defeats the usefulness of ${MAKEFILE}. + + * main.c (MainParseArgs): append command line variable assignments + to MAKEFLAGS so that they get propagated to child make's. + Apparently this is required POSIX behaviour? Its useful anyway. + +Tue May 29 02:20:07 2001 Simon J. Gerraty + + * compat.c (CompatRunCommand): don't use perror() since stdio may + cause problems in child of vfork(). + + * compat.c, main.c: Call PrintOnError() when we are going to bail. + This routine prints out the .curdir where we stopped and will also + display any vars listed in ${MAKE_PRINT_VAR_ON_ERROR}. + + * main.c: add ${.newline} to hold a "\n" - sometimes handy in + :@ expansion. + + * var.c: VarLoopExpand: ignore addSpace if a \n is present. + + * Added RCSid's for the files we've touched. + +Thu May 24 15:41:37 2001 Simon J. Gerraty + + * configure.in: Thanks to some clues from mdb@juniper.net, + added autoconf magic to control setting of MACHINE, MACHINE_ARCH + as well as what ends up in _PATH_DEFSYSPATH. We now have: + + --with-machine=MACHINE explicitly set MACHINE + --with-force-machine=MACHINE set FORCE_MACHINE + --with-machine_arch=MACHINE_ARCH explicitly set MACHINE_ARCH + --with-default-sys-path=PATH:DIR:LIST use an explicit _PATH_DEFSYSPATH + --with-prefix-sys-path=PATH:DIR:LIST prefix _PATH_PREFIX_SYSPATH + --with-path-objdirprefix=PATH override _PATH_OBJDIRPREFIX + + If _PATH_OBJDIRPREFIX is set to "no" we won't define it. + + * makefile: added a pathetically simple makefile to drive + bootstrapping. Running configure by hand is more useful. + + * Makefile.in: added MAKE_VERSION, and reworked things to be less + dependent on NetBSD bsd.*.mk + + * pathnames.h: allow NO_PATH_OBJDIRPREFIX to stop us defining + _PATH_OBJDIRPREFIX for those that don't want a default. + construct _PATH_DEFSYSPATH from the info we get from configure. + + * main.c: allow for no _PATH_OBJDIRPREFIX, set ${MAKE_VERSION} + if MAKE_VERSION is defined. + + * compat.c: when we bail, print out the .CURDIR we were in. + +Sat May 12 00:34:12 2001 Simon J. Gerraty + + * Merged with NetBSD make + + * var.c: fixed a bug in the handling of the modifier :P + if the node as found but the path was null, we segfault trying to + duplicate it. + +Mon Mar 5 16:20:33 2001 Simon J. Gerraty + + * Merged with NetBSD make + + * make.c: Make_OODate's test for a library out of date was using + cmtime where it should have used mtime (my bug). + + * compat.c: Use perror() to tell us what really went wrong when we + cannot exec a command. + +Fri Dec 15 10:11:08 2000 Simon J. Gerraty + + * Merged with NetBSD make + +Sat Jun 10 10:11:08 2000 Simon J. Gerraty + + * Merged with NetBSD make + +Thu Jun 1 10:11:08 2000 Simon J. Gerraty + + * Merged with NetBSD make + +Tue May 30 10:11:08 2000 Simon J. Gerraty + + * Merged with NetBSD make + +Thu Apr 27 00:07:47 2000 Simon J. Gerraty + + * util.c: don't provide signal() since we use sigcompat.c + + * Makefile.in: added a build target. + + * var.c (Var_Parse): added ODE modifiers :U, :D, :L, :P, :@ and :! + These allow some quite clever magic. + + * main.c (main): added support for getenv(MAKESYSPATH). + +Mon Apr 2 16:25:13 2000 Simon J. Gerraty + + * Disable $PWD overriding getcwd() if MAKEOBJDIRPREFIX is set. + This avoids objdir having a different value depending on how a + directory was reached (via command line, or subdir.mk). + + * If FORCE_MACHINE is defined, ignore getenv("MACHINE"). + +Mon Apr 2 23:15:31 2000 Simon J. Gerraty + + * Do a chdir(${.CURDIR}) before invoking ${.MAKE} or ${.MAKE:T} if + MAKEOBJDIRPREFIX is set and NOCHECKMAKECHDIR is not. + I've been testing this in NetBSD's make for some weeks. + + * Turn Makefile into Makefile.in and make it useful. + +Tue Feb 29 22:08:00 2000 Simon J. Gerraty + + * Imported NetBSD's -current make(1) and resolve conflicts. + + * Applied autoconf patches from bmake v2 + + * Imported clean code base from NetBSD-1.0 diff --git a/FILES b/FILES new file mode 100644 index 000000000000..6dcb22952a0e --- /dev/null +++ b/FILES @@ -0,0 +1,120 @@ +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/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/Makefile.in b/Makefile.in new file mode 100644 index 000000000000..560752267ab3 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,181 @@ +# $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.161 2012/06/06 20:02:32 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= 20120606 +MACHINE=@machine@ +MACHINE_ARCH=@machine_arch@ +DEFAULT_SYS_PATH = @default_sys_path@ + +CFLAGS+= -D_PATH_DEFSYSPATH=\"${DEFAULT_SYS_PATH}\" +CFLAGS+= -I. -I${srcdir} @DEFS@ @CPPFLAGS@ ${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:tl} != "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 make(obj) || make(clean) +SUBDIR+= unit-tests +.endif + +# many systems use gcc these days +CC_IS_GCC=@GCC@ +.if ${CC_IS_GCC} == "yes" +# problem with gcc3 +CFLAGS_var.o+= -Wno-cast-qual +.endif + +CFLAGS_main.o+= "-D@force_machine@MACHINE=\"${MACHINE}\"" "-DMACHINE_ARCH=\"${MACHINE_ARCH}\"" + +EXTRACT_MAN=no + +MAN=${PROG}.1 +.if (${PROG} != "make") +${MAN}: make.1 + @echo making ${PROG}.1 + @sed -e '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 +# sigh, FreeBSD at least includes bsd.subdir.mk via bsd.obj.mk +# so the inclusion below, results in complaints about re-defined +# targets. For NetBSD though we need to explicitly include it. +.if defined(SUBDIR) && !target(${SUBDIR:[1]}) +.-include +.endif + +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/usr.bin/make/PSD.doc/Makefile b/PSD.doc/Makefile similarity index 64% rename from usr.bin/make/PSD.doc/Makefile rename to PSD.doc/Makefile index acac84f3edc6..8e1f1fa0705b 100644 --- a/usr.bin/make/PSD.doc/Makefile +++ b/PSD.doc/Makefile @@ -1,3 +1,4 @@ +# $NetBSD: Makefile,v 1.2 1995/06/14 15:20:23 christos Exp $ # @(#)Makefile 8.1 (Berkeley) 8/14/93 DIR= psd/12.make diff --git a/usr.bin/make/PSD.doc/tutorial.ms b/PSD.doc/tutorial.ms similarity index 97% rename from usr.bin/make/PSD.doc/tutorial.ms rename to PSD.doc/tutorial.ms index 3cd869d05289..c1a6444d48c6 100644 --- a/usr.bin/make/PSD.doc/tutorial.ms +++ b/PSD.doc/tutorial.ms @@ -1,5 +1,4 @@ -.\" Copyright (c) 1988, 1989 by Adam de Boor -.\" Copyright (c) 1989 by Berkeley Softworks +.\" $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. .\" @@ -14,6 +13,36 @@ .\" 2. Redistributions in binary form must reproduce 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 @@ -34,7 +63,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)tutorial.ms 8.4 (Berkeley) 4/28/95 +.\" @(#)tutorial.ms 8.1 (Berkeley) 8/18/93 .\" .EH 'PSD:12-%''PMake \*- A Tutorial' .OH 'PMake \*- A Tutorial''PSD:12-%' @@ -43,8 +72,6 @@ .\" is numeric, it is taken as the depth for numbering (as for .NH), else .\" the default (1) is assumed. .\" -.\" $Id: tutorial.ms,v 1.4 89/01/08 20:20:22 adam Exp Locker: adam $ -.\" .\" @P The initial paragraph distance. .\" @Q The piece of section number to increment (or 0 if none given) .\" @R Section header. @@ -87,14 +114,15 @@ .nr g4 \\n(.s .sp -1 .\" .st cf -\D's -1u'\D't 5u' +\D't 5u' .sp -1 -\h'50u'\D'l 71u 0u'\D'l 50u 50u'\D'l 0u 71u'\D'l -50u 50u'\D'l -71u 0u'\D'l -50u -50u'\D'l 0u -71u'\D'l 50u -50u' +\h'50u' .sp -1 \D't 3u' .sp -1 .sp 7u -\h'53u'\D'p 14 68u 0u 46u 46u 0u 68u -46u 46u -68u 0u -47u -46u 0u -68u 47u -46u' +\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 @@ -104,7 +132,7 @@ \h'85u'\v'0.85n'\h-\w\\*(g9u/2u\&\\*(g9 .sp |\\n(g8u .sp 166u -\D't 3u'\D's -1u' +\D't 3u' .br .po .rt @@ -150,7 +178,7 @@ 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 +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: @@ -599,7 +627,7 @@ 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 preceeding the whole thing +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 @@ -1272,6 +1300,15 @@ administrator. If locking is on, 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 @@ -1417,7 +1454,7 @@ the screen from being filled with garbage even more indecipherable than you usually see. PMake has two ways of doing this, one of which provides for much cleaner output and a clear separation between the output of different jobs, the other of which provides a more immediate -response so one can tell what is really happpening. The former is done +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 @@ -1521,7 +1558,7 @@ using the .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, prceeded by a dollar sign. +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: @@ -1587,7 +1624,7 @@ suffix is usually a text file to be processed by Troff with the \-ms macro package, and so on. One of the best aspects of both Make and PMake comes from their understanding of how the suffix of a file pertains to its contents and -their ability to do things with a file based soley on its suffix. This +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. @@ -1912,11 +1949,15 @@ or this .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 (to find out what that -directory is, give PMake the +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) @@ -2588,7 +2629,7 @@ in the variable to be replaced by .I replacement-string , unless the .CW g -flag is given at the end, in which case all occurences of the string +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 @@ -2599,7 +2640,7 @@ the string must match starting at the beginning of the word. If 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 preceeds these two +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 @@ -2624,7 +2665,7 @@ character is replaced by the 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 preceeding it +delimiter character may be placed in either string by preceding it with a backslash. .IP T .Ix 0 def :T @@ -2927,10 +2968,10 @@ looks like this: .DS .SM # -# Rules for making libraries. The object files that make up the library are -# removed once they are archived. +# Rules for making libraries. The object files that make up the library +# are removed once they are archived. # -# To make several libararies in parallel, you should define the variable +# 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: @@ -2954,19 +2995,19 @@ 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. +# 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 +# endif /* many_libraries */ ranlib $(.TARGET) -#endif no_ranlib +#endif /* no_ranlib */ -#endif _MAKELIB_MK +#endif /* _MAKELIB_MK */ .DE .xH 2 On the Condition... .Rd 1 @@ -3054,8 +3095,8 @@ identify the system on which PMake is being run. 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 +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 @@ -3168,7 +3209,7 @@ FORMATTER = ditroff -Plaser_printer FORMATTER = nroff -Pdot_matrix_printer #endif .DE -would wreak havok if you tried +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. @@ -3289,7 +3330,7 @@ within that loop will be printed, etc. The specification runs like this: .DS # -# This is a shell specification to have the bourne shell echo +# 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. # @@ -3325,7 +3366,7 @@ When the echo off command is executed, the shell will print .CW \-x flag (rather than the .CW \-v -flag PMake usually uses)). PMake will remove all occurences of this +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 diff --git a/README b/README new file mode 100644 index 000000000000..fb688a33bc65 --- /dev/null +++ b/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/aclocal.m4 b/aclocal.m4 new file mode 100644 index 000000000000..2adafba3dac5 --- /dev/null +++ b/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 +], +[ +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/usr.bin/make/arch.c b/arch.c similarity index 58% rename from usr.bin/make/arch.c rename to arch.c index 64071dc6c535..ee09a5fcc6c1 100644 --- a/usr.bin/make/arch.c +++ b/arch.c @@ -1,6 +1,38 @@ +/* $NetBSD: arch.c,v 1.62 2010/11/27 16:00:09 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. + */ + +/* * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * @@ -36,9 +68,18 @@ * SUCH DAMAGE. */ +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: arch.c,v 1.62 2010/11/27 16:00:09 christos Exp $"; +#else +#include #ifndef lint -static char sccsid[] = "@(#)arch.c 8.3 (Berkeley) 4/28/95"; +#if 0 +static char sccsid[] = "@(#)arch.c 8.2 (Berkeley) 1/2/94"; +#else +__RCSID("$NetBSD: arch.c,v 1.62 2010/11/27 16:00:09 christos Exp $"); +#endif #endif /* not lint */ +#endif /*- * arch.c -- @@ -88,19 +129,52 @@ static char sccsid[] = "@(#)arch.c 8.3 (Berkeley) 4/28/95"; * Arch_End Cleanup this module. */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif #include #include #include #include #include +#ifdef HAVE_AR_H #include +#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 +#endif +#include #include #include +#ifdef HAVE_UTIME_H +#include +#endif + #include "make.h" #include "hash.h" #include "dir.h" -#include "config.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 */ @@ -108,13 +182,51 @@ typedef struct Arch { char *name; /* Name of archive */ Hash_Table members; /* All the members of the archive described * by key/value pairs */ + char *fnametab; /* Extended name table strings */ + size_t fnamesize; /* Size of the string table */ } Arch; -static int ArchFindArchive __P((ClientData, ClientData)); -static void ArchFree __P((ClientData)); -static struct ar_hdr *ArchStatMember __P((char *, char *, Boolean)); -static FILE *ArchFindMember __P((char *, char *, struct ar_hdr *, char *)); +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 "!\n" +#endif +#ifndef SARMAG +# define SARMAG 8 +#endif + +#define AR_MAX_NAME_LEN (sizeof(arh.AR_NAME)-1) + +#ifdef CLEANUP /*- *----------------------------------------------------------------------- * ArchFree -- @@ -129,24 +241,25 @@ static FILE *ArchFindMember __P((char *, char *, struct ar_hdr *, char *)); *----------------------------------------------------------------------- */ static void -ArchFree(ap) - ClientData ap; +ArchFree(void *ap) { - Arch *a = (Arch *) ap; + Arch *a = (Arch *)ap; Hash_Search search; Hash_Entry *entry; - - /* Free memory from hash entries */ + + /* Free memory from hash entries */ for (entry = Hash_EnumFirst(&a->members, &search); - entry != (Hash_Entry *)NULL; + entry != NULL; entry = Hash_EnumNext(&search)) - free((Address) Hash_GetValue (entry)); + free(Hash_GetValue(entry)); free(a->name); + if (a->fnametab) + free(a->fnametab); Hash_DeleteTable(&a->members); - free((Address) a); + free(a); } - +#endif /*- @@ -156,6 +269,11 @@ ArchFree(ap) * 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 @@ -167,22 +285,19 @@ ArchFree(ap) *----------------------------------------------------------------------- */ ReturnStatus -Arch_ParseArchive (linePtr, nodeLst, ctxt) - char **linePtr; /* Pointer to start of specification */ - Lst nodeLst; /* Lst on which to place the nodes */ - GNode *ctxt; /* Context in which to expand variables */ +Arch_ParseArchive(char **linePtr, Lst nodeLst, GNode *ctxt) { - register char *cp; /* Pointer into line */ + char *cp; /* Pointer into line */ GNode *gn; /* New node */ char *libName; /* Library-part of specification */ char *memName; /* Member-part of specification */ - char nameBuf[MAKE_BSIZE]; /* temporary place for node name */ + 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++) { @@ -192,19 +307,18 @@ Arch_ParseArchive (linePtr, nodeLst, ctxt) * so we can safely advance beyond it... */ int length; - Boolean freeIt; + void *freeIt; char *result; - - result=Var_Parse(cp, ctxt, TRUE, &length, &freeIt); + + result = Var_Parse(cp, ctxt, TRUE, &length, &freeIt); + if (freeIt) + free(freeIt); if (result == var_Error) { return(FAILURE); } else { subLibName = TRUE; } - - if (freeIt) { - free(result); - } + cp += length-1; } } @@ -213,7 +327,7 @@ Arch_ParseArchive (linePtr, nodeLst, ctxt) if (subLibName) { libName = Var_Subst(NULL, libName, ctxt, TRUE); } - + for (;;) { /* @@ -223,30 +337,29 @@ Arch_ParseArchive (linePtr, nodeLst, ctxt) */ Boolean doSubst = FALSE; /* TRUE if need to substitute in memName */ - while (*cp != '\0' && *cp != ')' && isspace (*cp)) { + while (*cp != '\0' && *cp != ')' && isspace ((unsigned char)*cp)) { cp++; } memName = cp; - while (*cp != '\0' && *cp != ')' && !isspace (*cp)) { + while (*cp != '\0' && *cp != ')' && !isspace ((unsigned char)*cp)) { if (*cp == '$') { /* * Variable spec, so call the Var module to parse the puppy * so we can safely advance beyond it... */ int length; - Boolean freeIt; + void *freeIt; char *result; - result=Var_Parse(cp, ctxt, TRUE, &length, &freeIt); + result = Var_Parse(cp, ctxt, TRUE, &length, &freeIt); + if (freeIt) + free(freeIt); if (result == var_Error) { return(FAILURE); } else { doSubst = TRUE; } - if (freeIt) { - free(result); - } cp += length; } else { cp++; @@ -289,7 +402,8 @@ Arch_ParseArchive (linePtr, nodeLst, ctxt) char *buf; char *sacrifice; char *oldMemName = memName; - + size_t sz; + memName = Var_Subst(NULL, memName, ctxt, TRUE); /* @@ -297,9 +411,10 @@ Arch_ParseArchive (linePtr, nodeLst, ctxt) * variables and multi-word variable values.... The results * are just placed at the end of the nodeLst we're returning. */ - buf = sacrifice = emalloc(strlen(memName)+strlen(libName)+3); + sz = strlen(memName)+strlen(libName)+3; + buf = sacrifice = bmake_malloc(sz); - sprintf(buf, "%s(%s)", libName, memName); + snprintf(buf, sz, "%s(%s)", libName, memName); if (strchr(memName, '$') && strcmp(memName, oldMemName) == 0) { /* @@ -309,12 +424,12 @@ Arch_ParseArchive (linePtr, nodeLst, ctxt) */ gn = Targ_FindNode(buf, TARG_CREATE); - if (gn == NILGNODE) { + if (gn == NULL) { free(buf); return(FAILURE); } else { gn->type |= OP_ARCHV; - (void)Lst_AtEnd(nodeLst, (ClientData)gn); + (void)Lst_AtEnd(nodeLst, gn); } } else if (Arch_ParseArchive(&sacrifice, nodeLst, ctxt)!=SUCCESS) { /* @@ -331,15 +446,21 @@ Arch_ParseArchive (linePtr, nodeLst, ctxt) } 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); - - sprintf(nameBuf, "%s(%s)", libName, member); + 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 == NILGNODE) { + gn = Targ_FindNode(nameBuf, TARG_CREATE); + if (gn == NULL) { + free(nameBuf); return (FAILURE); } else { /* @@ -350,14 +471,18 @@ Arch_ParseArchive (linePtr, nodeLst, ctxt) * end of the provided list. */ gn->type |= OP_ARCHV; - (void) Lst_AtEnd (nodeLst, (ClientData)gn); + (void)Lst_AtEnd(nodeLst, gn); } } - Lst_Destroy(members, NOFREE); + Lst_Destroy(members, NULL); + free(nameBuf); } else { - sprintf(nameBuf, "%s(%s)", libName, memName); - gn = Targ_FindNode (nameBuf, TARG_CREATE); - if (gn == NILGNODE) { + 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 { /* @@ -368,13 +493,13 @@ Arch_ParseArchive (linePtr, nodeLst, ctxt) * provided list. */ gn->type |= OP_ARCHV; - (void) Lst_AtEnd (nodeLst, (ClientData)gn); + (void)Lst_AtEnd(nodeLst, gn); } } if (doSubst) { free(memName); } - + *cp = saveChar; } @@ -392,7 +517,7 @@ Arch_ParseArchive (linePtr, nodeLst, ctxt) */ do { cp++; - } while (*cp != '\0' && isspace (*cp)); + } while (*cp != '\0' && isspace ((unsigned char)*cp)); *linePtr = cp; return (SUCCESS); @@ -404,6 +529,10 @@ Arch_ParseArchive (linePtr, nodeLst, ctxt) * 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. * @@ -413,11 +542,9 @@ Arch_ParseArchive (linePtr, nodeLst, ctxt) *----------------------------------------------------------------------- */ static int -ArchFindArchive (ar, archName) - ClientData ar; /* Current list element */ - ClientData archName; /* Name we want */ +ArchFindArchive(const void *ar, const void *archName) { - return (strcmp ((char *) archName, ((Arch *) ar)->name)); + return (strcmp(archName, ((const Arch *)ar)->name)); } /*- @@ -426,6 +553,12 @@ ArchFindArchive (ar, archName) * 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 @@ -438,14 +571,8 @@ ArchFindArchive (ar, archName) *----------------------------------------------------------------------- */ static struct ar_hdr * -ArchStatMember (archive, member, hash) - char *archive; /* Path to the archive */ - char *member; /* Name of member. If it is a path, only the - * last component is used. */ - Boolean hash; /* TRUE if archive should be hashed if not - * already so. */ +ArchStatMember(char *archive, char *member, Boolean hash) { -#define AR_MAX_NAME_LEN (sizeof(arh.ar_name)-1) FILE * arch; /* Stream to archive */ int size; /* Size of archive member */ char *cp; /* Useful character pointer */ @@ -463,32 +590,32 @@ ArchStatMember (archive, member, hash) * to point 'member' to the final component, if there is one, to make * the comparisons easier... */ - cp = strrchr (member, '/'); - if (cp != (char *) NULL) { + cp = strrchr(member, '/'); + if (cp != NULL) { member = cp + 1; } - ln = Lst_Find (archives, (ClientData) archive, ArchFindArchive); - if (ln != NILLNODE) { - ar = (Arch *) Lst_Datum (ln); + ln = Lst_Find(archives, archive, ArchFindArchive); + if (ln != NULL) { + ar = (Arch *)Lst_Datum(ln); - he = Hash_FindEntry (&ar->members, member); + he = Hash_FindEntry(&ar->members, member); - if (he != (Hash_Entry *) NULL) { - return ((struct ar_hdr *) Hash_GetValue (he)); + if (he != NULL) { + return ((struct ar_hdr *)Hash_GetValue(he)); } else { /* Try truncated name */ char copy[AR_MAX_NAME_LEN+1]; - int len = strlen (member); + 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)) - return ((struct ar_hdr *) Hash_GetValue (he)); - return ((struct ar_hdr *) NULL); + if ((he = Hash_FindEntry(&ar->members, copy)) != NULL) + return ((struct ar_hdr *)Hash_GetValue(he)); + return NULL; } } @@ -504,8 +631,8 @@ ArchStatMember (archive, member, hash) arch = ArchFindMember(archive, member, &sarh, "r"); - if (arch == (FILE *)NULL) { - return ((struct ar_hdr *)NULL); + if (arch == NULL) { + return NULL; } else { fclose(arch); return (&sarh); @@ -516,106 +643,230 @@ ArchStatMember (archive, member, hash) * We don't have this archive on the list yet, so we want to find out * everything that's in it and cache it so we can get at it quickly. */ - arch = fopen (archive, "r"); - if (arch == (FILE *) NULL) { - return ((struct ar_hdr *) NULL); + 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 ((struct ar_hdr *) NULL); + if ((fread(magic, SARMAG, 1, arch) != 1) || + (strncmp(magic, ARMAG, SARMAG) != 0)) { + fclose(arch); + return NULL; } - ar = (Arch *)emalloc (sizeof (Arch)); - ar->name = strdup (archive); - Hash_InitTable (&ar->members, -1); + 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... - */ - fclose (arch); - Hash_DeleteTable (&ar->members); - free ((Address)ar); - return ((struct ar_hdr *) NULL); + + 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 { - (void) strncpy (memName, arh.ar_name, sizeof(arh.ar_name)); + /* + * We need to advance the stream's pointer to the start of the + * next header. Files are padded with newlines to an even-byte + * boundary, so we need to extract the size of the file from the + * 'size' field of the header and round it up during the seek. + */ + arh.AR_SIZE[sizeof(arh.AR_SIZE)-1] = '\0'; + size = (int)strtol(arh.AR_SIZE, NULL, 10); + + (void)strncpy(memName, arh.AR_NAME, sizeof(arh.AR_NAME)); for (cp = &memName[AR_MAX_NAME_LEN]; *cp == ' '; cp--) { continue; } cp[1] = '\0'; +#ifdef SVR4ARCHIVES + /* + * svr4 names are slash terminated. Also svr4 extended AR format. + */ + if (memName[0] == '/') { + /* + * svr4 magic mode; handle it + */ + switch (ArchSVR4Entry(ar, memName, size, arch)) { + case -1: /* Invalid data */ + goto badarch; + case 0: /* List of files entry */ + continue; + default: /* Got the entry */ + break; + } + } + else { + if (cp[0] == '/') + cp[0] = '\0'; + } +#endif + #ifdef AR_EFMT1 /* * BSD 4.4 extended AR format: #1/, with name as the * first bytes of the file */ if (strncmp(memName, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 && - isdigit(memName[sizeof(AR_EFMT1) - 1])) { + isdigit((unsigned char)memName[sizeof(AR_EFMT1) - 1])) { unsigned int elen = atoi(&memName[sizeof(AR_EFMT1)-1]); - if (elen > MAXPATHLEN) { - fclose (arch); - Hash_DeleteTable (&ar->members); - free ((Address)ar); - return ((struct ar_hdr *) NULL); - } - if (fread (memName, elen, 1, arch) != 1) { - fclose (arch); - Hash_DeleteTable (&ar->members); - free ((Address)ar); - return ((struct ar_hdr *) NULL); - } + if (elen > MAXPATHLEN) + goto badarch; + if (fread(memName, elen, 1, arch) != 1) + goto badarch; memName[elen] = '\0'; - fseek (arch, -elen, 1); + fseek(arch, -elen, SEEK_CUR); if (DEBUG(ARCH) || DEBUG(MAKE)) { - printf("ArchStat: Extended format entry for %s\n", memName); + fprintf(debug_file, "ArchStat: Extended format entry for %s\n", memName); } } #endif - he = Hash_CreateEntry (&ar->members, memName, (Boolean *)NULL); - Hash_SetValue (he, (ClientData)emalloc (sizeof (struct ar_hdr))); - memcpy ((Address)Hash_GetValue (he), (Address)&arh, - sizeof (struct ar_hdr)); + 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)); } - /* - * 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); - fseek (arch, (size + 1) & ~1, 1); + fseek(arch, (size + 1) & ~1, SEEK_CUR); } - fclose (arch); + fclose(arch); - (void) Lst_AtEnd (archives, (ClientData) ar); + (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); + he = Hash_FindEntry(&ar->members, member); - if (he != (Hash_Entry *) NULL) { - return ((struct ar_hdr *) Hash_GetValue (he)); + if (he != NULL) { + return ((struct ar_hdr *)Hash_GetValue(he)); } else { - return ((struct ar_hdr *) NULL); + return NULL; } + +badarch: + fclose(arch); + Hash_DeleteTable(&ar->members); + if (ar->fnametab) + free(ar->fnametab); + free(ar); + return NULL; } +#ifdef SVR4ARCHIVES +/*- + *----------------------------------------------------------------------- + * ArchSVR4Entry -- + * Parse an SVR4 style entry that begins with a slash. + * If it is "//", then load the table of filenames + * If it is "/", then try to substitute the long file name + * from offset of a table previously read. + * + * Results: + * -1: Bad data in archive + * 0: A table was loaded from the file + * 1: Name was successfully substituted from table + * 2: Name was not successfully substituted from table + * + * Side Effects: + * If a table is read, the file pointer is moved to the next archive + * member + * + *----------------------------------------------------------------------- + */ +static int +ArchSVR4Entry(Arch *ar, char *name, size_t size, FILE *arch) +{ +#define ARLONGNAMES1 "//" +#define ARLONGNAMES2 "/ARFILENAMES" + size_t entry; + char *ptr, *eptr; + + if (strncmp(name, ARLONGNAMES1, sizeof(ARLONGNAMES1) - 1) == 0 || + strncmp(name, ARLONGNAMES2, sizeof(ARLONGNAMES2) - 1) == 0) { + + if (ar->fnametab != NULL) { + if (DEBUG(ARCH)) { + 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 -- @@ -623,6 +874,13 @@ ArchStatMember (archive, member, hash) * 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 @@ -634,32 +892,28 @@ ArchStatMember (archive, member, hash) *----------------------------------------------------------------------- */ static FILE * -ArchFindMember (archive, member, arhPtr, mode) - char *archive; /* Path to the archive */ - char *member; /* Name of member. If it is a path, only the - * last component is used. */ - struct ar_hdr *arhPtr; /* Pointer to header structure to be filled in */ - char *mode; /* The mode for opening the stream */ +ArchFindMember(char *archive, char *member, struct ar_hdr *arhPtr, + const char *mode) { FILE * arch; /* Stream to archive */ int size; /* Size of archive member */ char *cp; /* Useful character pointer */ char magic[SARMAG]; - int len, tlen; + size_t len, tlen; - arch = fopen (archive, mode); - if (arch == (FILE *) NULL) { - return ((FILE *) NULL); + 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 ((FILE *) NULL); + if ((fread(magic, SARMAG, 1, arch) != 1) || + (strncmp(magic, ARMAG, SARMAG) != 0)) { + fclose(arch); + return NULL; } /* @@ -668,24 +922,24 @@ ArchFindMember (archive, member, arhPtr, mode) * to point 'member' to the final component, if there is one, to make * the comparisons easier... */ - cp = strrchr (member, '/'); - if (cp != (char *) NULL) { + cp = strrchr(member, '/'); + if (cp != NULL) { member = cp + 1; } - len = tlen = strlen (member); - if (len > sizeof (arhPtr->ar_name)) { - tlen = sizeof (arhPtr->ar_name); + 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) { + + while (fread((char *)arhPtr, sizeof(struct ar_hdr), 1, arch) == 1) { + if (strncmp(arhPtr->AR_FMAG, ARFMAG, sizeof(arhPtr->AR_FMAG) ) != 0) { /* * The header is bogus, so the archive is bad * and there's no way we can recover... */ - fclose (arch); - return ((FILE *) NULL); - } else if (strncmp (member, arhPtr->ar_name, tlen) == 0) { + 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- @@ -693,7 +947,7 @@ ArchFindMember (archive, member, arhPtr, mode) * 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] != ' '){ + if (tlen != sizeof(arhPtr->AR_NAME) && arhPtr->AR_NAME[tlen] != ' '){ goto skip; } else { /* @@ -703,7 +957,7 @@ ArchFindMember (archive, member, arhPtr, mode) * the file at the actual member, rather than its header, but * not here... */ - fseek (arch, -sizeof(struct ar_hdr), 1); + fseek(arch, -sizeof(struct ar_hdr), SEEK_CUR); return (arch); } } else @@ -712,31 +966,31 @@ ArchFindMember (archive, member, arhPtr, mode) * BSD 4.4 extended AR format: #1/, with name as the * first bytes of the file */ - if (strncmp(arhPtr->ar_name, AR_EFMT1, + if (strncmp(arhPtr->AR_NAME, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 && - isdigit(arhPtr->ar_name[sizeof(AR_EFMT1) - 1])) { + isdigit((unsigned char)arhPtr->AR_NAME[sizeof(AR_EFMT1) - 1])) { - unsigned int elen = atoi(&arhPtr->ar_name[sizeof(AR_EFMT1)-1]); - char ename[MAXPATHLEN]; + unsigned int elen = atoi(&arhPtr->AR_NAME[sizeof(AR_EFMT1)-1]); + char ename[MAXPATHLEN + 1]; if (elen > MAXPATHLEN) { - fclose (arch); + fclose(arch); return NULL; } - if (fread (ename, elen, 1, arch) != 1) { - fclose (arch); + if (fread(ename, elen, 1, arch) != 1) { + fclose(arch); return NULL; } ename[elen] = '\0'; if (DEBUG(ARCH) || DEBUG(MAKE)) { - printf("ArchFind: Extended format entry for %s\n", ename); + 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, 1); + fseek(arch, -sizeof(struct ar_hdr) - elen, SEEK_CUR); return (arch); } - fseek (arch, -elen, 1); + fseek(arch, -elen, SEEK_CUR); goto skip; } else #endif @@ -749,9 +1003,9 @@ skip: * 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, 1); + arhPtr->ar_size[sizeof(arhPtr->AR_SIZE)-1] = '\0'; + size = (int)strtol(arhPtr->AR_SIZE, NULL, 10); + fseek(arch, (size + 1) & ~1, SEEK_CUR); } } @@ -759,8 +1013,8 @@ skip: * We've looked everywhere, but the member is not to be found. Close the * archive and return NULL -- an error. */ - fclose (arch); - return ((FILE *) NULL); + fclose(arch); + return NULL; } /*- @@ -768,6 +1022,9 @@ skip: * 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. * @@ -779,25 +1036,24 @@ skip: *----------------------------------------------------------------------- */ void -Arch_Touch (gn) - GNode *gn; /* Node of member to touch */ +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 (TARGET, gn, &p2), + arch = ArchFindMember(Var_Value(ARCHIVE, gn, &p1), + Var_Value(MEMBER, gn, &p2), &arh, "r+"); if (p1) free(p1); if (p2) free(p2); - sprintf(arh.ar_date, "%-12ld", (long) now); + snprintf(arh.AR_DATE, sizeof(arh.AR_DATE), "%-12ld", (long) now); - if (arch != (FILE *) NULL) { - (void)fwrite ((char *)&arh, sizeof (struct ar_hdr), 1, arch); - fclose (arch); + if (arch != NULL) { + (void)fwrite((char *)&arh, sizeof(struct ar_hdr), 1, arch); + fclose(arch); } } @@ -807,6 +1063,9 @@ Arch_Touch (gn) * 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. * @@ -817,24 +1076,28 @@ Arch_Touch (gn) *----------------------------------------------------------------------- */ void -Arch_TouchLib (gn) - GNode *gn; /* The node of the library to touch */ +#if !defined(RANLIBMAG) +Arch_TouchLib(GNode *gn __unused) +#else +Arch_TouchLib(GNode *gn) +#endif { +#ifdef RANLIBMAG FILE * arch; /* Stream open to archive */ struct ar_hdr arh; /* Header describing table of contents */ - struct timeval times[2]; /* Times for utimes() call */ + struct utimbuf times; /* Times for utime() call */ - arch = ArchFindMember (gn->path, RANLIBMAG, &arh, "r+"); - sprintf(arh.ar_date, "%-12ld", (long) now); + arch = ArchFindMember(gn->path, UNCONST(RANLIBMAG), &arh, "r+"); + snprintf(arh.AR_DATE, sizeof(arh.AR_DATE), "%-12ld", (long) now); - if (arch != (FILE *) NULL) { - (void)fwrite ((char *)&arh, sizeof (struct ar_hdr), 1, arch); - fclose (arch); + if (arch != NULL) { + (void)fwrite((char *)&arh, sizeof(struct ar_hdr), 1, arch); + fclose(arch); - times[0].tv_sec = times[1].tv_sec = now; - times[0].tv_usec = times[1].tv_usec = 0; - utimes(gn->path, times); + times.actime = times.modtime = now; + utime(gn->path, ×); } +#endif } /*- @@ -842,8 +1105,11 @@ Arch_TouchLib (gn) * Arch_MTime -- * Return the modification time of a member of an archive. * + * Input: + * gn Node describing archive member + * * Results: - * The modification time (seconds). + * The modification time(seconds). * * Side Effects: * The mtime field of the given node is filled in with the value @@ -851,24 +1117,23 @@ Arch_TouchLib (gn) * *----------------------------------------------------------------------- */ -int -Arch_MTime (gn) - GNode *gn; /* Node describing archive member */ +time_t +Arch_MTime(GNode *gn) { struct ar_hdr *arhPtr; /* Header of desired member */ - int modTime; /* Modification time as an integer */ + time_t modTime; /* Modification time as an integer */ char *p1, *p2; - arhPtr = ArchStatMember (Var_Value (ARCHIVE, gn, &p1), - Var_Value (TARGET, gn, &p2), + arhPtr = ArchStatMember(Var_Value(ARCHIVE, gn, &p1), + Var_Value(MEMBER, gn, &p2), TRUE); if (p1) free(p1); if (p2) free(p2); - if (arhPtr != (struct ar_hdr *) NULL) { - modTime = (int) strtol(arhPtr->ar_date, NULL, 10); + if (arhPtr != NULL) { + modTime = (time_t)strtol(arhPtr->AR_DATE, NULL, 10); } else { modTime = 0; } @@ -891,21 +1156,20 @@ Arch_MTime (gn) * *----------------------------------------------------------------------- */ -int -Arch_MemMTime (gn) - GNode *gn; +time_t +Arch_MemMTime(GNode *gn) { LstNode ln; GNode *pgn; char *nameStart, *nameEnd; - if (Lst_Open (gn->parents) != SUCCESS) { + if (Lst_Open(gn->parents) != SUCCESS) { gn->mtime = 0; return (0); } - while ((ln = Lst_Next (gn->parents)) != NILLNODE) { - pgn = (GNode *) Lst_Datum (ln); + while ((ln = Lst_Next(gn->parents)) != NULL) { + pgn = (GNode *)Lst_Datum(ln); if (pgn->type & OP_ARCHV) { /* @@ -915,14 +1179,14 @@ Arch_MemMTime (gn) * child. We keep searching its parents in case some other * parent requires this child to exist... */ - nameStart = strchr (pgn->name, '(') + 1; - nameEnd = strchr (nameStart, ')'); + nameStart = strchr(pgn->name, '(') + 1; + nameEnd = strchr(nameStart, ')'); - if (pgn->make && + if ((pgn->flags & REMAKE) && strncmp(nameStart, gn->name, nameEnd - nameStart) == 0) { gn->mtime = Arch_MTime(pgn); } - } else if (pgn->make) { + } else if (pgn->flags & REMAKE) { /* * Something which isn't a library depends on the existence of * this target, so it needs to exist. @@ -932,7 +1196,7 @@ Arch_MemMTime (gn) } } - Lst_Close (gn->parents); + Lst_Close(gn->parents); return (gn->mtime); } @@ -940,7 +1204,11 @@ Arch_MemMTime (gn) /*- *----------------------------------------------------------------------- * Arch_FindLib -- - * Search for a library along the given search path. + * Search for a library along the given search path. + * + * Input: + * gn Node of library to find + * path Search path * * Results: * None. @@ -958,24 +1226,23 @@ Arch_MemMTime (gn) *----------------------------------------------------------------------- */ void -Arch_FindLib (gn, path) - GNode *gn; /* Node of library to find */ - Lst path; /* Search path */ +Arch_FindLib(GNode *gn, Lst path) { char *libName; /* file name for archive */ + size_t sz = strlen(gn->name) + 6 - 2; - libName = (char *)emalloc (strlen (gn->name) + 6 - 2); - sprintf(libName, "lib%s.a", &gn->name[2]); + libName = bmake_malloc(sz); + snprintf(libName, sz, "lib%s.a", &gn->name[2]); - gn->path = Dir_FindFile (libName, path); + gn->path = Dir_FindFile(libName, path); - free (libName); + free(libName); #ifdef LIBRARIES - Var_Set (TARGET, gn->name, gn); + Var_Set(TARGET, gn->name, gn, 0); #else - Var_Set (TARGET, gn->path == (char *) NULL ? gn->name : gn->path, gn); -#endif LIBRARIES + Var_Set(TARGET, gn->path == NULL ? gn->name : gn->path, gn, 0); +#endif /* LIBRARIES */ } /*- @@ -993,7 +1260,7 @@ Arch_FindLib (gn, path) * A library will be considered out-of-date for any of these reasons, * given that it is a target on a dependency line somewhere: * Its modification time is less than that of one of its - * sources (gn->mtime < gn->cmtime). + * 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). @@ -1004,7 +1271,10 @@ Arch_FindLib (gn, path) * 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. + * TOC. + * + * Input: + * gn The library's graph node * * Results: * TRUE if the library is out-of-date. FALSE otherwise. @@ -1015,37 +1285,44 @@ Arch_FindLib (gn, path) *----------------------------------------------------------------------- */ Boolean -Arch_LibOODate (gn) - GNode *gn; /* The library's graph node */ +Arch_LibOODate(GNode *gn) { Boolean oodate; - - if (OP_NOP(gn->type) && Lst_IsEmpty(gn->children)) { + + if (gn->type & OP_PHONY) { + oodate = TRUE; + } else if (OP_NOP(gn->type) && Lst_IsEmpty(gn->children)) { oodate = FALSE; - } else if ((gn->mtime > now) || (gn->mtime < gn->cmtime)) { + } 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, RANLIBMAG, FALSE); + arhPtr = ArchStatMember(gn->path, UNCONST(RANLIBMAG), FALSE); - if (arhPtr != (struct ar_hdr *)NULL) { - modTimeTOC = (int) strtol(arhPtr->ar_date, NULL, 10); + if (arhPtr != NULL) { + modTimeTOC = (int)strtol(arhPtr->AR_DATE, NULL, 10); if (DEBUG(ARCH) || DEBUG(MAKE)) { - printf("%s modified %s...", RANLIBMAG, Targ_FmtTime(modTimeTOC)); + fprintf(debug_file, "%s modified %s...", RANLIBMAG, Targ_FmtTime(modTimeTOC)); } - oodate = (gn->cmtime > 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)) { - printf("No t.o.c...."); + fprintf(debug_file, "No t.o.c...."); } oodate = TRUE; } +#else + oodate = FALSE; +#endif } return (oodate); } @@ -1064,9 +1341,9 @@ Arch_LibOODate (gn) *----------------------------------------------------------------------- */ void -Arch_Init () +Arch_Init(void) { - archives = Lst_Init (FALSE); + archives = Lst_Init(FALSE); } @@ -1085,7 +1362,42 @@ Arch_Init () *----------------------------------------------------------------------- */ void -Arch_End () +Arch_End(void) { +#ifdef CLEANUP Lst_Destroy(archives, ArchFree); +#endif +} + +/*- + *----------------------------------------------------------------------- + * Arch_IsLib -- + * Check if the node is a library + * + * Results: + * True or False. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +int +Arch_IsLib(GNode *gn) +{ + static const char armag[] = "!\n"; + char buf[sizeof(armag)-1]; + int fd; + + if ((fd = open(gn->path, O_RDONLY)) == -1) + return FALSE; + + if (read(fd, buf, sizeof(buf)) != sizeof(buf)) { + (void)close(fd); + return FALSE; + } + + (void)close(fd); + + return memcmp(buf, armag, sizeof(buf)) == 0; } diff --git a/bmake.1 b/bmake.1 new file mode 100644 index 000000000000..0bb377ae4967 --- /dev/null +++ b/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/bmake.cat1 b/bmake.cat1 new file mode 100644 index 000000000000..7624c43e6599 --- /dev/null +++ b/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 + + ..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/boot-strap b/boot-strap new file mode 100755 index 000000000000..660b7667dd52 --- /dev/null +++ b/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 + +# 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 [-- ...][-s ][-m ][ [[] []]]" >&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/bsd.after-import.mk b/bsd.after-import.mk new file mode 100644 index 000000000000..eeb397c5ff49 --- /dev/null +++ b/bsd.after-import.mk @@ -0,0 +1,87 @@ +# $Id: bsd.after-import.mk,v 1.3 2012/06/06 17:48:14 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: ${.CURDIR}/Makefile +all: after-import + +# we rely on bmake +_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) +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,${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 + @cmp -s ${.CURDIR}/$f ${HOST_OS}/$f || \ + cp ${HOST_OS}/$f ${.CURDIR}/$f +.endfor +.for f in ${configured_files:M*Makefile} + @echo Capturing $f + @${MAKEFILE_SED} ${HOST_OS}/$f > ${.CURDIR}/$f +.endfor + +# this needs the most work +${.CURDIR}/Makefile: bootstrap ${MAKEFILE} .PRECIOUS + @echo Generating ${.TARGET:T} + @(echo '# This is a generated file, do NOT edit!'; \ + echo '# See ${_this:S,${SRCTOP}/,,}'; \ + 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 '.-include "Makefile.inc"'; \ + echo ) > ${.TARGET:T}.new + @mv ${.TARGET:T}.new ${.TARGET} + +.include + diff --git a/buf.c b/buf.c new file mode 100644 index 000000000000..ac95c16c1586 --- /dev/null +++ b/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 +#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/buf.h b/buf.h new file mode 100644 index 000000000000..25be67d79afb --- /dev/null +++ b/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/usr.bin/make/compat.c b/compat.c similarity index 52% rename from usr.bin/make/compat.c rename to compat.c index 9cab63c07345..ae3c9a1f92af 100644 --- a/usr.bin/make/compat.c +++ b/compat.c @@ -1,6 +1,39 @@ +/* $NetBSD: compat.c,v 1.88 2012/06/05 17:31:04 sjg Exp $ */ + /* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. + * 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. * @@ -36,9 +69,18 @@ * SUCH DAMAGE. */ +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: compat.c,v 1.88 2012/06/05 17:31:04 sjg Exp $"; +#else +#include #ifndef lint -static char sccsid[] = "@(#)compat.c 8.3 (Berkeley) 4/28/95"; +#if 0 +static char sccsid[] = "@(#)compat.c 8.2 (Berkeley) 3/19/94"; +#else +__RCSID("$NetBSD: compat.c,v 1.88 2012/06/05 17:31:04 sjg Exp $"); +#endif #endif /* not lint */ +#endif /*- * compat.c -- @@ -53,18 +95,23 @@ static char sccsid[] = "@(#)compat.c 8.3 (Berkeley) 4/28/95"; * thems as need creatin' */ -#include +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif #include -#include -#include -#include #include +#include "wait.h" + #include +#include +#include +#include + #include "make.h" #include "hash.h" #include "dir.h" #include "job.h" -extern int errno; +#include "pathnames.h" /* * The following array is used to make a fast determination of which @@ -75,11 +122,25 @@ extern int errno; static char meta[256]; -static GNode *curTarg = NILGNODE; +static GNode *curTarg = NULL; static GNode *ENDNode; -static void CompatInterrupt __P((int)); -static int CompatRunCommand __P((ClientData, ClientData)); -static int CompatMake __P((ClientData, ClientData)); +static void CompatInterrupt(int) __dead; + +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; +} /*- *----------------------------------------------------------------------- @@ -97,19 +158,16 @@ static int CompatMake __P((ClientData, ClientData)); *----------------------------------------------------------------------- */ static void -CompatInterrupt (signo) - int signo; +CompatInterrupt(int signo) { GNode *gn; - - if ((curTarg != NILGNODE) && !Targ_Precious (curTarg)) { - char *p1; - char *file = Var_Value (TARGET, curTarg, &p1); - struct stat st; - if (!noExecute && lstat(file, &st) != -1 && !S_ISDIR(st.st_mode) && - unlink(file) != -1) { - printf ("*** %s removed\n", file); + 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); @@ -119,13 +177,16 @@ CompatInterrupt (signo) */ if (signo == SIGINT) { gn = Targ_FindNode(".INTERRUPT", TARG_NOCREATE); - if (gn != NILGNODE) { - Lst_ForEach(gn->commands, CompatRunCommand, (ClientData)gn); + if (gn != NULL) { + Compat_Make(gn, gn); } } } - exit (signo); + if (signo == SIGQUIT) + _exit(signo); + bmake_signal(signo, SIG_DFL); + kill(myPid, signo); } /*- @@ -134,6 +195,10 @@ CompatInterrupt (signo) * 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. * @@ -142,78 +207,96 @@ CompatInterrupt (signo) * *----------------------------------------------------------------------- */ -static int -CompatRunCommand (cmdp, gnp) - ClientData cmdp; /* Command to execute */ - ClientData gnp; /* Node from which the command came */ +int +CompatRunCommand(void *cmdp, void *gnp) { char *cmdStart; /* Start of expanded command */ - register char *cp; + char *cp, *bp; Boolean silent, /* Don't print command */ - errCheck; /* Check errors */ - union wait reason; /* Reason for child's death */ + 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 */ - int cpid; /* Child actually found */ - ReturnStatus stat; /* Status of fork */ + pid_t cpid; /* Child actually found */ + pid_t retstat; /* Result of wait */ LstNode cmdNode; /* Node where current command is located */ - char **av; /* Argument vector for thing to exec */ + 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 */ - char *cmd = (char *) cmdp; - GNode *gn = (GNode *) gnp; + Boolean useShell; /* TRUE if command should be executed + * using a shell */ + char * volatile cmd = (char *)cmdp; + GNode *gn = (GNode *)gnp; - /* - * Avoid clobbered variable warnings by forcing the compiler - * to ``unregister'' variables - */ -#if __GNUC__ - (void) &av; - (void) &errCheck; -#endif silent = gn->type & OP_SILENT; errCheck = !(gn->type & OP_IGNORE); - - cmdNode = Lst_Member (gn->commands, (ClientData)cmd); - cmdStart = Var_Subst (NULL, cmd, gn, FALSE); + 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[1], thus causing + * brk_string will return an argv with a NULL in av[0], thus causing * execvp to choke and die horribly. Besides, how can we execute a null * command? In any case, we warn the user that the command expanded to * nothing (is this the right thing to do?). */ - + if (*cmdStart == '\0') { free(cmdStart); Error("%s expands to empty string", cmd); return(0); - } else { - cmd = cmdStart; } - Lst_Replace (cmdNode, (ClientData)cmdStart); + cmd = cmdStart; + Lst_Replace(cmdNode, cmdStart); if ((gn->type & OP_SAVE_CMDS) && (gn != ENDNode)) { - (void)Lst_AtEnd(ENDNode->commands, (ClientData)cmdStart); + (void)Lst_AtEnd(ENDNode->commands, cmdStart); return(0); - } else if (strcmp(cmdStart, "...") == 0) { + } + if (strcmp(cmdStart, "...") == 0) { gn->type |= OP_SAVE_CMDS; return(0); } - while ((*cmd == '@') || (*cmd == '-')) { - if (*cmd == '@') { - silent = TRUE; - } else { + 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 @@ -222,13 +305,15 @@ CompatRunCommand (cmdp, gnp) 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) { - printf ("%s\n", cmd); + if (!silent || NoExecute(gn)) { + printf("%s\n", cmd); fflush(stdout); } @@ -236,116 +321,180 @@ CompatRunCommand (cmdp, gnp) * If we're not supposed to execute any commands, this is as far as * we go... */ - if (noExecute) { + if (!doIt && NoExecute(gn)) { return (0); } - - if (*cp != '\0') { - /* - * If *cp isn't the null character, we hit a "meta" character and - * need to pass the command off to the shell. We give the shell the - * -e flag as well as -c if it's supposed to exit when it hits an - * error. - */ - static char *shargv[4] = { "/bin/sh" }; + if (DEBUG(JOB)) + fprintf(debug_file, "Execute: '%s'\n", cmd); - shargv[1] = (errCheck ? "-ec" : "-c"); +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] = (char *)NULL; + 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. - * brk_string sticks our name in av[0], so we have to - * skip over it... */ - av = brk_string(cmd, &argc, TRUE); - av += 1; + 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(); + cpid = vFork(); if (cpid < 0) { Fatal("Could not fork"); } if (cpid == 0) { - if (local) { - execvp(av[0], av); - (void) write (2, av[0], strlen (av[0])); - (void) write (2, ": not found\n", sizeof(": not found")); - } else { - (void)execv(av[0], av); + Check_Cwd(av); + Var_ExportVars(); +#ifdef USE_META + if (useMeta) { + meta_compat_child(); } - exit(1); +#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); } - free(cmdStart); - Lst_Replace (cmdNode, (ClientData) NULL); - + 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 ((stat = wait((int *)&reason)) != cpid) { - if (stat == -1 && errno != EINTR) { + while ((retstat = wait(&reason)) != cpid) { + if (retstat > 0) + JobReapChild(retstat, reason, FALSE); /* not ours? */ + if (retstat == -1 && errno != EINTR) { break; } } - - if (stat > -1) { + + if (retstat > -1) { if (WIFSTOPPED(reason)) { - status = reason.w_stopval; /* stopped */ + status = WSTOPSIG(reason); /* stopped */ } else if (WIFEXITED(reason)) { - status = reason.w_retcode; /* exited */ + status = WEXITSTATUS(reason); /* exited */ +#if defined(USE_META) && defined(USE_FILEMON_ONCE) + if (useMeta) { + meta_cmd_finish(NULL); + } +#endif if (status != 0) { - printf ("*** Error code %d", status); + 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 = reason.w_termsig; /* signaled */ - printf ("*** Signal %d", status); - } + 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"); + printf(" (continuing)\n"); } } else { /* * Continue executing commands for this target. * If we return 0, this will happen... */ - printf (" (ignored)\n"); + printf(" (ignored)\n"); status = 0; } } break; } else { - Fatal ("error in wait: %d", stat); + Fatal("error in wait: %d: %s", retstat, strerror(errno)); /*NOTREACHED*/ } } + free(cmdStart); return (status); } /*- *----------------------------------------------------------------------- - * CompatMake -- + * Compat_Make -- * Make a target. * + * Input: + * gnp The node to make + * pgnp Parent to abort if necessary + * * Results: * 0 * @@ -354,16 +503,15 @@ CompatRunCommand (cmdp, gnp) * *----------------------------------------------------------------------- */ -static int -CompatMake (gnp, pgnp) - ClientData gnp; /* The node to make */ - ClientData pgnp; /* Parent to abort if necessary */ +int +Compat_Make(void *gnp, void *pgnp) { - GNode *gn = (GNode *) gnp; - GNode *pgn = (GNode *) pgnp; - if (gn->type & OP_USE) { - Make_HandleUse(gn, pgn); - } else if (gn->made == UNMADE) { + 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 @@ -372,40 +520,41 @@ CompatMake (gnp, pgnp) * This is our signal to not attempt to do anything but abort our * parent as well. */ - gn->make = TRUE; + gn->flags |= REMAKE; gn->made = BEINGMADE; - Suff_FindDeps (gn); - Lst_ForEach (gn->children, CompatMake, (ClientData)gn); - if (!gn->make) { + 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->make = FALSE; - return (0); + pgn->flags &= ~REMAKE; + goto cohorts; } - if (Lst_Member (gn->iParents, pgn) != NILLNODE) { + if (Lst_Member(gn->iParents, pgn) != NULL) { char *p1; - Var_Set (IMPSRC, Var_Value(TARGET, gn, &p1), pgn); + Var_Set(IMPSRC, Var_Value(TARGET, gn, &p1), pgn, 0); if (p1) free(p1); } - + /* - * All the children were made ok. Now cmtime contains the modification - * time of the newest child, we need to find out if we exist and when - * we were modified last. The criteria for datedness are defined by the - * Make_OODate function. + * 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)) { - printf("Examining %s...", gn->name); + fprintf(debug_file, "Examining %s...", gn->name); } if (! Make_OODate(gn)) { gn->made = UPTODATE; if (DEBUG(MAKE)) { - printf("up-to-date.\n"); + fprintf(debug_file, "up-to-date.\n"); } - return (0); + goto cohorts; } else if (DEBUG(MAKE)) { - printf("out-of-date.\n"); + fprintf(debug_file, "out-of-date.\n"); } /* @@ -413,7 +562,7 @@ CompatMake (gnp, pgnp) * to tell him/her "yes". */ if (queryFlag) { - exit (-1); + exit(1); } /* @@ -422,33 +571,43 @@ CompatMake (gnp, pgnp) * 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)) { + if (Targ_Ignore(gn)) { gn->type |= OP_IGNORE; } - if (Targ_Silent (gn)) { + if (Targ_Silent(gn)) { gn->type |= OP_SILENT; } - if (Job_CheckCommands (gn, Fatal)) { + if (Job_CheckCommands(gn, Fatal)) { /* * Our commands are ok, but we still have to worry about the -t * flag... */ - if (!touchFlag) { + if (!touchFlag || (gn->type & OP_MAKE)) { curTarg = gn; - Lst_ForEach (gn->commands, CompatRunCommand, (ClientData)gn); - curTarg = NILGNODE; +#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); + 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) { /* @@ -458,96 +617,39 @@ CompatMake (gnp, pgnp) * This is to keep its state from affecting that of its parent. */ gn->made = MADE; -#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 (and the speed of this program), there are times - * when the modification time of a file created on a remote - * machine will not be modified before the 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 - */ - if (noExecute || Dir_MTime(gn) == 0) { - gn->mtime = now; - } - if (gn->cmtime > gn->mtime) - gn->mtime = gn->cmtime; - if (DEBUG(MAKE)) { - printf("update time: %s\n", Targ_FmtTime(gn->mtime)); - } -#endif + pgn->flags |= Make_Recheck(gn) == 0 ? FORCE : 0; if (!(gn->type & OP_EXEC)) { - pgn->childMade = TRUE; + pgn->flags |= CHILDMADE; Make_TimeStamp(pgn, gn); } } else if (keepgoing) { - pgn->make = FALSE; + pgn->flags &= ~REMAKE; } else { - printf ("\n\nStop.\n"); - exit (1); + 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->make = FALSE; + pgn->flags &= ~REMAKE; } else { - if (Lst_Member (gn->iParents, pgn) != NILLNODE) { + if (Lst_Member(gn->iParents, pgn) != NULL) { char *p1; - Var_Set (IMPSRC, Var_Value(TARGET, gn, &p1), pgn); + Var_Set(IMPSRC, Var_Value(TARGET, gn, &p1), pgn, 0); if (p1) free(p1); } switch(gn->made) { case BEINGMADE: - Error("Graph cycles through %s\n", gn->name); + Error("Graph cycles through %s", gn->name); gn->made = ERROR; - pgn->make = FALSE; + pgn->flags &= ~REMAKE; break; case MADE: if ((gn->type & OP_EXEC) == 0) { - pgn->childMade = TRUE; + pgn->flags |= CHILDMADE; Make_TimeStamp(pgn, gn); } break; @@ -561,14 +663,19 @@ CompatMake (gnp, pgnp) } } +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. * @@ -578,49 +685,52 @@ CompatMake (gnp, pgnp) *----------------------------------------------------------------------- */ void -Compat_Run(targs) - Lst targs; /* List of target nodes to re-create */ +Compat_Run(Lst targs) { - char *cp; /* Pointer to string of shell meta-characters */ GNode *gn = NULL;/* Current root target */ int errors; /* Number of targets not remade due to errors */ - if (signal(SIGINT, SIG_IGN) != SIG_IGN) { - signal(SIGINT, CompatInterrupt); - } - if (signal(SIGTERM, SIG_IGN) != SIG_IGN) { - signal(SIGTERM, CompatInterrupt); - } - if (signal(SIGHUP, SIG_IGN) != SIG_IGN) { - signal(SIGHUP, CompatInterrupt); - } - if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) { - signal(SIGQUIT, CompatInterrupt); - } + Compat_Init(); - for (cp = "#=|^(){};&<>*?[]:$`\\\n"; *cp != '\0'; cp++) { - meta[(unsigned char) *cp] = 1; + 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); } - /* - * The null character serves as a sentinel in the string. - */ - meta[0] = 1; ENDNode = Targ_FindNode(".END", TARG_CREATE); + ENDNode->type = OP_SPECIAL; /* * If the user has defined a .BEGIN target, execute the commands attached * to it. */ if (!queryFlag) { gn = Targ_FindNode(".BEGIN", TARG_NOCREATE); - if (gn != NILGNODE) { - Lst_ForEach(gn->commands, CompatRunCommand, (ClientData)gn); + if (gn != NULL) { + Compat_Make(gn, gn); + if (gn->made == ERROR) { + PrintOnError(gn, "\n\nStop."); + exit(1); + } } } /* - * For each entry in the list of targets to create, call CompatMake on - * it to create the thing. CompatMake will leave the 'made' field of gn + * 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 @@ -630,13 +740,13 @@ Compat_Run(targs) */ errors = 0; while (!Lst_IsEmpty (targs)) { - gn = (GNode *) Lst_DeQueue (targs); - CompatMake (gn, gn); + gn = (GNode *)Lst_DeQueue(targs); + Compat_Make(gn, gn); if (gn->made == UPTODATE) { - printf ("`%s' is up to date.\n", gn->name); + printf("`%s' is up to date.\n", gn->name); } else if (gn->made == ABORTED) { - printf ("`%s' not remade because of errors.\n", gn->name); + printf("`%s' not remade because of errors.\n", gn->name); errors += 1; } } @@ -645,6 +755,10 @@ Compat_Run(targs) * If the user has defined a .END target, run its commands. */ if (errors == 0) { - Lst_ForEach(ENDNode->commands, CompatRunCommand, (ClientData)gn); + Compat_Make(ENDNode, ENDNode); + if (gn->made == ERROR) { + PrintOnError(gn, "\n\nStop."); + exit(1); + } } } diff --git a/cond.c b/cond.c new file mode 100644 index 000000000000..134e620aa523 --- /dev/null +++ b/cond.c @@ -0,0 +1,1410 @@ +/* $NetBSD: cond.c,v 1.63 2012/05/21 06:30:02 sjg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1988, 1989 by Adam de Boor + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: cond.c,v 1.63 2012/05/21 06:30:02 sjg Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)cond.c 8.2 (Berkeley) 1/2/94"; +#else +__RCSID("$NetBSD: cond.c,v 1.63 2012/05/21 06:30:02 sjg Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * cond.c -- + * Functions to handle conditionals in a makefile. + * + * Interface: + * Cond_Eval Evaluate the conditional in the passed line. + * + */ + +#include +#include /* 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 __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 __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 __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 __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 __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 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 __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 __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: + * . + * where is any of if, ifmake, ifnmake, ifdef, + * ifndef, elif, elifmake, elifnmake, elifdef, elifndef + * and consists of &&, ||, !, make(target), defined(variable) + * and parenthetical groupings thereof. + * + * Input: + * line Line to parse + * + * Results: + * COND_PARSE if should parse lines after the conditional + * COND_SKIP if should skip lines after the conditional + * COND_INVALID if not a valid conditional. + * + * Side Effects: + * None. + * + * 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/config.h.in b/config.h.in new file mode 100644 index 000000000000..7108dcf2f67b --- /dev/null +++ b/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 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 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 header file. */ +#undef HAVE_ERR_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define to 1 if you have the `fork' function. */ +#undef HAVE_FORK + +/* Define to 1 if you have the `getcwd' function. */ +#undef HAVE_GETCWD + +/* Define to 1 if you have the `getenv' function. */ +#undef HAVE_GETENV + +/* Define to 1 if you have the `getopt' function. */ +#undef HAVE_GETOPT + +/* Define to 1 if you have the `getwd' function. */ +#undef HAVE_GETWD + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `killpg' function. */ +#undef HAVE_KILLPG + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `mmap' function. */ +#undef HAVE_MMAP + +/* Define to 1 if you have the header file, and it defines `DIR'. */ +#undef HAVE_NDIR_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_PATHS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_POLL_H + +/* Define to 1 if you have the `putenv' function. */ +#undef HAVE_PUTENV + +/* Define to 1 if you have the header file. */ +#undef HAVE_RANLIB_H + +/* Define to 1 if you have the `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 header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `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 header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `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 header file, and it defines `DIR'. + */ +#undef HAVE_SYS_DIR_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_MMAN_H + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#undef HAVE_SYS_NDIR_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SELECT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_UIO_H + +/* Define to 1 if you have that is POSIX.1 compatible. */ +#undef HAVE_SYS_WAIT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `unsetenv' function. */ +#undef HAVE_UNSETENV + +/* Define to 1 if you have the 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 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 do not work properly. */ +#undef STAT_MACROS_BROKEN + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define to 1 if you can safely include both and . */ +#undef TIME_WITH_SYS_TIME + +/* Define to 1 if your declares `struct tm'. */ +#undef TM_IN_SYS_TIME + +/* 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 does not define. */ +#undef off_t + +/* Define to `int' if does not define. */ +#undef pid_t + +/* Define to `unsigned int' if does not define. */ +#undef size_t + +/* Define as `fork' if `vfork' does not work. */ +#undef vfork diff --git a/configure b/configure new file mode 100755 index 000000000000..36fce0d2ba2c --- /dev/null +++ b/configure @@ -0,0 +1,7134 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.64 for bmake 20120606. +# +# Report bugs to . +# +# 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 &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='20120606' +PACKAGE_STRING='bmake 20120606' +PACKAGE_BUGREPORT='sjg@NetBSD.org' +PACKAGE_URL='' + +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#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 20120606 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 20120606:";; + 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 if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to . +_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 20120606 +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 declares $2. + For example, HP-UX 11i declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#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 20120606, 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 "$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 +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 +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* 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 to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#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 +_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 to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#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 +_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 +#include +#include +#include + +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 + +_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 + +_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 +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + 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 +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* 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 +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 +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 +#include +#include +#include + +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 + +_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 + +_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 +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + 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 +#include +#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 +#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 +#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 + +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 + #include + +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 + #include + +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 defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +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 + +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 +/* NetBSD declares sys_siglist in unistd.h. */ +#ifdef HAVE_UNISTD_H +# include +#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 +#include +#include + +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 +#include + +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 +#include + +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 +#ifdef HAVE_VFORK_H +# include +#endif +/* On some sparc systems, changes by the child to local and incoming + argument registers are propagated back to the parent. The compiler + is told about this with #include , but some compilers + (e.g. gcc -O) don't grok . Test for this by using a + static variable whose address is put into a register that is + clobbered by the vfork. */ +static 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 +#include +#include +/* HP-UX has wait3 but does not fill in rusage at all. */ +int +main () +{ + struct rusage r; + int i; + /* Use a field that we can force nonzero -- + voluntary context switches. + For systems like NeXT and OSF/1 that don't set it, + also use the system CPU time. And page faults (I/O) for Linux. */ + r.ru_nvcsw = 0; + r.ru_stime.tv_sec = 0; + r.ru_stime.tv_usec = 0; + r.ru_majflt = r.ru_minflt = 0; + switch (fork ()) + { + case 0: /* Child. */ + sleep(1); /* Give up the CPU. */ + _exit(0); + break; + case -1: /* What can we do? */ + _exit(0); + break; + default: /* Parent. */ + wait3(&i, 0, &r); + /* Avoid "text file busy" from rm on fast HP-UX machines. */ + sleep(2); + 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 +#include + +#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 < +#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 20120606, 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 ." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_version="\\ +bmake config.status 20120606 +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` +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 +' >$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 +' >$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 <&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 +#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 < +#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 < #ifndef lint -static char sccsid[] = "@(#)dir.c 8.3 (Berkeley) 4/28/95"; +#if 0 +static char sccsid[] = "@(#)dir.c 8.2 (Berkeley) 1/2/94"; +#else +__RCSID("$NetBSD: dir.c,v 1.64 2012/04/07 18:29:08 christos Exp $"); +#endif #endif /* not lint */ +#endif /*- * dir.c -- @@ -49,8 +91,14 @@ static char sccsid[] = "@(#)dir.c 8.3 (Berkeley) 4/28/95"; * 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. * @@ -61,6 +109,10 @@ static char sccsid[] = "@(#)dir.c 8.3 (Berkeley) 4/28/95"; * 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 @@ -83,10 +135,13 @@ static char sccsid[] = "@(#)dir.c 8.3 (Berkeley) 4/28/95"; * Dir_PrintDirectories Print stats about the directory cache. */ -#include #include -#include #include + +#include +#include +#include + #include "make.h" #include "hash.h" #include "dir.h" @@ -175,6 +230,9 @@ static int hits, /* Found in directory cache */ 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 @@ -186,12 +244,16 @@ static Hash_Table mtimes; /* Results of doing a last-resort stat in * should be ok, but... */ -static int DirFindName __P((ClientData, ClientData)); -static int DirMatchFiles __P((char *, Path *, Lst)); -static void DirExpandCurly __P((char *, char *, Lst, Lst)); -static void DirExpandInt __P((char *, Lst, Lst)); -static int DirPrintWord __P((ClientData, ClientData)); -static int DirPrintDir __P((ClientData, ClientData)); +static 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 *); /*- *----------------------------------------------------------------------- @@ -206,26 +268,84 @@ static int DirPrintDir __P((ClientData, ClientData)); *----------------------------------------------------------------------- */ void -Dir_Init () +Dir_Init(const char *cdname) { - dirSearchPath = Lst_Init (FALSE); - openDirectories = Lst_Init (FALSE); + 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; - /* - * Since the Path structure is placed on both openDirectories and - * the path we give Dir_AddDir (which in this case is openDirectories), - * we need to remove "." from openDirectories and what better time to - * do it than when we have to fetch the thing anyway? - */ - Dir_AddDir (openDirectories, "."); - dot = (Path *) Lst_DeQueue (openDirectories); + 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 */ } /*- @@ -241,15 +361,72 @@ Dir_Init () *----------------------------------------------------------------------- */ void -Dir_End() +Dir_End(void) { +#ifdef CLEANUP + if (cur) { + cur->refCount -= 1; + Dir_Destroy(cur); + } dot->refCount -= 1; - Dir_Destroy((ClientData) dot); + dotLast->refCount -= 1; + Dir_Destroy(dotLast); + Dir_Destroy(dot); Dir_ClearPath(dirSearchPath); - Lst_Destroy(dirSearchPath, NOFREE); + Lst_Destroy(dirSearchPath, NULL); Dir_ClearPath(openDirectories); - Lst_Destroy(openDirectories, NOFREE); + 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); + } } /*- @@ -259,6 +436,10 @@ Dir_End() * 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 * @@ -267,17 +448,23 @@ Dir_End() *----------------------------------------------------------------------- */ static int -DirFindName (p, dname) - ClientData p; /* Current name */ - ClientData dname; /* Desired name */ +DirFindName(const void *p, const void *dname) { - return (strcmp (((Path *)p)->name, (char *) 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 @@ -287,21 +474,36 @@ DirFindName (p, dname) *----------------------------------------------------------------------- */ Boolean -Dir_HasWildcards (name) - char *name; /* name to check */ +Dir_HasWildcards(char *name) { - register char *cp; - + 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 '*': - return (TRUE); + wild = 1; + break; + default: + break; } } - return (FALSE); + return wild && bracket == 0 && brace == 0; } /*- @@ -313,6 +515,11 @@ Dir_HasWildcards (name) * 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 * @@ -322,19 +529,16 @@ Dir_HasWildcards (name) *----------------------------------------------------------------------- */ static int -DirMatchFiles (pattern, p, expansions) - char *pattern; /* Pattern to look for */ - Path *p; /* Directory to search */ - Lst expansions; /* Place to store the results */ +DirMatchFiles(const char *pattern, Path *p, Lst expansions) { - Hash_Search search; /* Index into the directory's table */ + Hash_Search search; /* Index into the directory's table */ Hash_Entry *entry; /* Current entry in the table */ Boolean isDot; /* TRUE if the directory being searched is . */ - + isDot = (*p->name == '.' && p->name[1] == '\0'); - + for (entry = Hash_EnumFirst(&p->files, &search); - entry != (Hash_Entry *)NULL; + entry != NULL; entry = Hash_EnumNext(&search)) { /* @@ -348,7 +552,7 @@ DirMatchFiles (pattern, p, expansions) (pattern[0] == '.'))) { (void)Lst_AtEnd(expansions, - (isDot ? strdup(entry->name) : + (isDot ? bmake_strdup(entry->name) : str_concat(p->name, entry->name, STR_ADDSLASH))); } @@ -364,6 +568,12 @@ DirMatchFiles (pattern, p, expansions) * 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. * @@ -373,23 +583,19 @@ DirMatchFiles (pattern, p, expansions) *----------------------------------------------------------------------- */ static void -DirExpandCurly(word, brace, path, expansions) - char *word; /* Entire word to expand */ - char *brace; /* First curly brace in it */ - Lst path; /* Search path to use */ - Lst expansions; /* Place to store the expansions */ +DirExpandCurly(const char *word, const char *brace, Lst path, Lst expansions) { - char *end; /* Character after the closing brace */ - char *cp; /* Current position in brace clause */ - char *start; /* Start of current piece of brace clause */ + 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 */ + 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 + char *cp2; /* Pointer for checking for wildcards in * expansion before calling Dir_Expand */ start = brace+1; @@ -429,7 +635,7 @@ DirExpandCurly(word, brace, path, expansions) /* * Allocate room for the combination and install the three pieces. */ - file = emalloc(otherLen + cp - start + 1); + file = bmake_malloc(otherLen + cp - start + 1); if (brace != word) { strncpy(file, word, brace-word); } @@ -475,6 +681,11 @@ DirExpandCurly(word, brace, path, expansions) * 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. * @@ -484,16 +695,13 @@ DirExpandCurly(word, brace, path, expansions) *----------------------------------------------------------------------- */ static void -DirExpandInt(word, path, expansions) - char *word; /* Word to expand */ - Lst path; /* Path on which to look */ - Lst expansions; /* Place to store the result */ +DirExpandInt(const char *word, Lst path, Lst expansions) { LstNode ln; /* Current node */ Path *p; /* Directory in the node */ if (Lst_Open(path) == SUCCESS) { - while ((ln = Lst_Next(path)) != NILLNODE) { + while ((ln = Lst_Next(path)) != NULL) { p = (Path *)Lst_Datum(ln); DirMatchFiles(word, p, expansions); } @@ -516,11 +724,9 @@ DirExpandInt(word, path, expansions) *----------------------------------------------------------------------- */ static int -DirPrintWord(word, dummy) - ClientData word; - ClientData dummy; +DirPrintWord(void *word, void *dummy) { - printf("%s ", (char *) word); + fprintf(debug_file, "%s ", (char *)word); return(dummy ? 0 : 0); } @@ -531,6 +737,12 @@ DirPrintWord(word, dummy) * 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. @@ -540,18 +752,14 @@ DirPrintWord(word, dummy) *----------------------------------------------------------------------- */ void -Dir_Expand (word, path, expansions) - char *word; /* the word to expand */ - Lst path; /* the list of directories in which to find - * the resulting files */ - Lst expansions; /* the list on which to place the results */ +Dir_Expand(const char *word, Lst path, Lst expansions) { - char *cp; + const char *cp; if (DEBUG(DIR)) { - printf("expanding \"%s\"...", word); + fprintf(debug_file, "Expanding \"%s\"... ", word); } - + cp = strchr(word, '{'); if (cp) { DirExpandCurly(word, cp, path, expansions); @@ -589,9 +797,9 @@ Dir_Expand (word, path, expansions) * all the components up to the one with a wildcard. */ sc = cp[1]; - cp[1] = '\0'; + ((char *)UNCONST(cp))[1] = '\0'; dirpath = Dir_FindFile(word, path); - cp[1] = sc; + ((char *)UNCONST(cp))[1] = sc; /* * dirpath is null if can't find the leading component * XXX: Dir_FindFile won't find internal components. @@ -599,14 +807,14 @@ Dir_Expand (word, path, expansions) * looking for Etc, it won't be found. Ah well. * Probably not important. */ - if (dirpath != (char *)NULL) { + if (dirpath != NULL) { char *dp = &dirpath[strlen(dirpath) - 1]; if (*dp == '/') *dp = '\0'; path = Lst_Init(FALSE); - Dir_AddDir(path, dirpath); + (void)Dir_AddDir(path, dirpath); DirExpandInt(cp+1, path, expansions); - Lst_Destroy(path, NOFREE); + Lst_Destroy(path, NULL); } } else { /* @@ -625,7 +833,7 @@ Dir_Expand (word, path, expansions) * First the files in dot */ DirMatchFiles(word, dot, expansions); - + /* * Then the files in every other directory on the path. */ @@ -633,16 +841,202 @@ Dir_Expand (word, path, expansions) } } if (DEBUG(DIR)) { - Lst_ForEach(expansions, DirPrintWord, (ClientData) 0); - fputc('\n', stdout); + 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 __unused, const char *cp, + Boolean hasSlash __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 __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. @@ -657,25 +1051,23 @@ Dir_Expand (word, path, expansions) *----------------------------------------------------------------------- */ char * -Dir_FindFile (name, path) - char *name; /* the file to find */ - Lst path; /* the Lst of directories to search */ +Dir_FindFile(const char *name, Lst path) { - register char *p1; /* pointer into p->name */ - register char *p2; /* pointer into name */ - LstNode ln; /* a list element */ - register char *file; /* the current filename to check */ - register Path *p; /* current path member */ - register char *cp; /* index of first slash, if any */ - Boolean hasSlash; /* true if 'name' contains a / */ - struct stat stb; /* Buffer for stat, if necessary */ - Hash_Entry *entry; /* Entry for mtimes table */ - + 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, '/'); + cp = strrchr(name, '/'); if (cp) { hasSlash = TRUE; cp += 1; @@ -683,186 +1075,145 @@ Dir_FindFile (name, path) hasSlash = FALSE; cp = name; } - + if (DEBUG(DIR)) { - printf("Searching for %s...", name); + fprintf(debug_file, "Searching for %s ...", name); } - /* - * No matter what, we always look for the file in the current directory - * before anywhere else and we *do not* add the ./ to it if it exists. - * This is so there are no conflicts between what the user specifies - * (fish.c) and what pmake finds (./fish.c). - */ - if ((!hasSlash || (cp - name == 2 && *name == '.')) && - (Hash_FindEntry (&dot->files, cp) != (Hash_Entry *)NULL)) { - if (DEBUG(DIR)) { - printf("in '.'\n"); - } - hits += 1; - dot->hits += 1; - return (strdup (name)); - } - - if (Lst_Open (path) == FAILURE) { + + if (Lst_Open(path) == FAILURE) { if (DEBUG(DIR)) { - printf("couldn't open path, file not found\n"); + fprintf(debug_file, "couldn't open path, file not found\n"); } misses += 1; - return ((char *) NULL); + 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"); + } + /* - * We look through all the directories on the path seeking one which - * contains the final component of the given name and whose final - * component(s) match the name's initial component(s). If such a beast - * is found, we concatenate the directory name and the final component - * and return the resulting string. If we don't find any such thing, - * we go on to phase two... + * 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. */ - while ((ln = Lst_Next (path)) != NILLNODE) { - p = (Path *) Lst_Datum (ln); - if (DEBUG(DIR)) { - printf("%s...", p->name); - } - if (Hash_FindEntry (&p->files, cp) != (Hash_Entry *)NULL) { - if (DEBUG(DIR)) { - printf("here..."); - } - if (hasSlash) { - /* - * If the name had a slash, its initial components and p's - * final components must match. This is false if a mismatch - * is encountered before all of the initial components - * have been checked (p2 > name at the end of the loop), or - * we matched only part of one of the components of p - * along with all the rest of them (*p1 != '/'). - */ - p1 = p->name + strlen (p->name) - 1; - p2 = cp - 2; - while (p2 >= name && p1 >= p->name && *p1 == *p2) { - p1 -= 1; p2 -= 1; - } - if (p2 >= name || (p1 >= p->name && *p1 != '/')) { - if (DEBUG(DIR)) { - printf("component mismatch -- continuing..."); - } - continue; - } - } - file = str_concat (p->name, cp, STR_ADDSLASH); - if (DEBUG(DIR)) { - printf("returning %s\n", file); - } - Lst_Close (path); - p->hits += 1; - hits += 1; - return (file); - } else if (hasSlash) { + if (!hasSlash || (cp - name == 2 && *name == '.')) { /* - * If the file has a leading path component and that component - * exactly matches the entire name of the current search - * directory, we assume the file doesn't exist and return NULL. + * 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). */ - for (p1 = p->name, p2 = name; *p1 && *p1 == *p2; p1++, p2++) { - continue; + if (!hasLastDot && + (file = DirFindDot(hasSlash, name, cp)) != NULL) { + Lst_Close(path); + return file; } - if (*p1 == '\0' && p2 == cp - 1) { - if (DEBUG(DIR)) { - printf("must be here but isn't -- returing NULL\n"); + + 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; } - Lst_Close (path); - return ((char *) NULL); } - } + + if (hasLastDot && + (file = DirFindDot(hasSlash, name, cp)) != NULL) { + Lst_Close(path); + return file; + } } - + Lst_Close(path); + /* - * We didn't find the file on any existing members of the directory. + * 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) If we find such a beast, we assume there + * /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. + * end).] + * This phase is only performed if the file is *not* absolute. */ if (!hasSlash) { if (DEBUG(DIR)) { - printf("failed.\n"); + fprintf(debug_file, " failed.\n"); } misses += 1; - return ((char *) NULL); + return NULL; } - - if (*name != '/') { + + if (*cp == '\0') { + /* we were given a trailing "/" */ + cp = trailing_dot; + } + + if (name[0] != '/') { Boolean checkedDot = FALSE; - + if (DEBUG(DIR)) { - printf("failed. Trying subdirectories..."); + fprintf(debug_file, " Trying subdirectories...\n"); } - (void) Lst_Open (path); - while ((ln = Lst_Next (path)) != NILLNODE) { - p = (Path *) Lst_Datum (ln); - if (p != dot) { - file = str_concat (p->name, name, STR_ADDSLASH); - } else { - /* - * Checking in dot -- DON'T put a leading ./ on the thing. - */ - file = strdup(name); + + 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 (DEBUG(DIR)) { - printf("checking %s...", file); - } - - - if (stat (file, &stb) == 0) { - if (DEBUG(DIR)) { - printf("got it.\n"); - } - - Lst_Close (path); - - /* - * We've found another directory to search. We know there's - * a slash in 'file' because we put one there. We nuke it after - * finding it and call Dir_AddDir to add this new directory - * onto the existing search path. Once that's done, we restore - * the slash and triumphantly return the file name, knowing - * that should a file in this directory every be referenced - * again in such a manner, we will find it without having to do - * numerous numbers of access calls. Hurrah! - */ - cp = strrchr (file, '/'); - *cp = '\0'; - Dir_AddDir (path, file); - *cp = '/'; - - /* - * Save the modification time so if it's needed, we don't have - * to fetch it again. - */ - if (DEBUG(DIR)) { - printf("Caching %s for %s\n", Targ_FmtTime(stb.st_mtime), - file); - } - entry = Hash_CreateEntry(&mtimes, (char *) file, - (Boolean *)NULL); - Hash_SetValue(entry, (long)stb.st_mtime); - nearmisses += 1; - return (file); - } else { - free (file); + if ((file = DirLookupSubdir(p, name)) != NULL) { + Lst_Close(path); + return file; } } - - if (DEBUG(DIR)) { - printf("failed. "); + 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; } - Lst_Close (path); if (checkedDot) { /* @@ -870,12 +1221,45 @@ Dir_FindFile (name, path) * so no point in proceeding... */ if (DEBUG(DIR)) { - printf("Checked . already, returning NULL\n"); + fprintf(debug_file, " Checked . already, returning NULL\n"); } - return(NULL); + 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 @@ -894,58 +1278,146 @@ Dir_FindFile (name, path) * b/c we added it here. This is not good... */ #ifdef notdef - cp[-1] = '\0'; - Dir_AddDir (path, name); - cp[-1] = '/'; - - bigmisses += 1; - ln = Lst_Last (path); - if (ln == NILLNODE) { - return ((char *) NULL); - } else { - p = (Path *) Lst_Datum (ln); + if (cp == traling_dot) { + cp = strrchr(name, '/'); + cp += 1; } - - if (Hash_FindEntry (&p->files, cp) != (Hash_Entry *)NULL) { - return (strdup (name)); + cp[-1] = '\0'; + (void)Dir_AddDir(path, name); + cp[-1] = '/'; + + bigmisses += 1; + ln = Lst_Last(path); + if (ln == NULL) { + return NULL; } else { - return ((char *) NULL); + p = (Path *)Lst_Datum(ln); + } + + if (Hash_FindEntry(&p->files, cp) != NULL) { + return (bmake_strdup(name)); + } else { + return NULL; } #else /* !notdef */ if (DEBUG(DIR)) { - printf("Looking for \"%s\"...", name); + fprintf(debug_file, " Looking for \"%s\" ...\n", name); } - + bigmisses += 1; entry = Hash_FindEntry(&mtimes, name); - if (entry != (Hash_Entry *)NULL) { + if (entry != NULL) { if (DEBUG(DIR)) { - printf("got it (in mtime cache)\n"); + fprintf(debug_file, " got it (in mtime cache)\n"); } - return(strdup(name)); - } else if (stat (name, &stb) == 0) { - entry = Hash_CreateEntry(&mtimes, name, (Boolean *)NULL); + 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)) { - printf("Caching %s for %s\n", Targ_FmtTime(stb.st_mtime), + fprintf(debug_file, " Caching %s for %s\n", Targ_FmtTime(stb.st_mtime), name); } - Hash_SetValue(entry, (long)stb.st_mtime); - return (strdup (name)); + Hash_SetTimeValue(entry, stb.st_mtime); + return (bmake_strdup(name)); } else { if (DEBUG(DIR)) { - printf("failed. Returning NULL\n"); + fprintf(debug_file, " failed. Returning NULL\n"); } - return ((char *)NULL); + 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 * @@ -956,52 +1428,93 @@ Dir_FindFile (name, path) *----------------------------------------------------------------------- */ int -Dir_MTime (gn) - GNode *gn; /* the file whose modification time is - * desired */ +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->path == (char *)NULL) { - fullName = Dir_FindFile (gn->name, dirSearchPath); + 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 == (char *)NULL) { - fullName = strdup(gn->name); + + if (fullName == NULL) { + fullName = bmake_strdup(gn->name); } - entry = Hash_FindEntry(&mtimes, fullName); - if (entry != (Hash_Entry *)NULL) { - /* - * Only do this once -- the second time folks are checking to - * see if the file was actually updated, so we need to actually go - * to the file system. - */ + if (!recheck) + entry = Hash_FindEntry(&mtimes, fullName); + else + entry = NULL; + if (entry != NULL) { if (DEBUG(DIR)) { - printf("Using cached time %s for %s\n", - Targ_FmtTime((time_t)(long)Hash_GetValue(entry)), fullName); + fprintf(debug_file, "Using cached time %s for %s\n", + Targ_FmtTime(Hash_GetTimeValue(entry)), fullName); } - stb.st_mtime = (time_t)(long)Hash_GetValue(entry); - Hash_DeleteEntry(&mtimes, entry); - } else if (stat (fullName, &stb) < 0) { + 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); + 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 == (char *)NULL) { + + if (fullName && gn->path == NULL) { gn->path = fullName; } - + gn->mtime = stb.st_mtime; return (gn->mtime); } @@ -1013,53 +1526,59 @@ Dir_MTime (gn) * 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 + * A structure is added to the list and the directory is * read and hashed. *----------------------------------------------------------------------- */ -void -Dir_AddDir (path, name) - Lst path; /* the path to which the directory should be - * added */ - char *name; /* the name of the directory to add */ +Path * +Dir_AddDir(Lst path, const char *name) { - LstNode ln; /* node in case Path structure is found */ - register Path *p; /* pointer to new Path structure */ + LstNode ln = NULL; /* node in case Path structure is found */ + Path *p = NULL; /* pointer to new Path structure */ DIR *d; /* for reading directory */ - register struct dirent *dp; /* entry in directory */ - - ln = Lst_Find (openDirectories, (ClientData)name, DirFindName); - if (ln != NILLNODE) { - p = (Path *)Lst_Datum (ln); - if (Lst_Member(path, (ClientData)p) == NILLNODE) { + 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, (ClientData)p); + (void)Lst_AtEnd(path, p); } } else { if (DEBUG(DIR)) { - printf("Caching %s...", name); - fflush(stdout); + fprintf(debug_file, "Caching %s ...", name); } - - if ((d = opendir (name)) != (DIR *) NULL) { - p = (Path *) emalloc (sizeof (Path)); - p->name = strdup (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); - - /* - * Skip the first two entries -- these will *always* be . and .. - */ - (void)readdir(d); - (void)readdir(d); - - while ((dp = readdir (d)) != (struct dirent *) NULL) { -#ifdef sun + 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 @@ -1068,17 +1587,19 @@ Dir_AddDir (path, name) if (dp->d_fileno == 0) { continue; } -#endif /* sun */ - (void)Hash_CreateEntry(&p->files, dp->d_name, (Boolean *)NULL); +#endif /* sun && d_ino */ + (void)Hash_CreateEntry(&p->files, dp->d_name, NULL); } - (void) closedir (d); - (void)Lst_AtEnd (openDirectories, (ClientData)p); - (void)Lst_AtEnd (path, (ClientData)p); + (void)closedir(d); + (void)Lst_AtEnd(openDirectories, p); + if (path != NULL) + (void)Lst_AtEnd(path, p); } if (DEBUG(DIR)) { - printf("done\n"); + fprintf(debug_file, "done\n"); } } + return p; } /*- @@ -1095,13 +1616,12 @@ Dir_AddDir (path, name) * *----------------------------------------------------------------------- */ -ClientData -Dir_CopyDir(p) - ClientData p; +void * +Dir_CopyDir(void *p) { - ((Path *) p)->refCount += 1; + ((Path *)p)->refCount += 1; - return ((ClientData)p); + return (p); } /*- @@ -1112,6 +1632,10 @@ Dir_CopyDir(p) * 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 @@ -1122,26 +1646,26 @@ Dir_CopyDir(p) *----------------------------------------------------------------------- */ char * -Dir_MakeFlags (flag, path) - char *flag; /* flag which should precede each directory */ - Lst path; /* list of directories */ +Dir_MakeFlags(const char *flag, Lst path) { char *str; /* the string which will be returned */ - char *tstr; /* the current directory preceded by 'flag' */ + 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 = strdup (""); - - if (Lst_Open (path) == SUCCESS) { - while ((ln = Lst_Next (path)) != NILLNODE) { - p = (Path *) Lst_Datum (ln); - tstr = str_concat (flag, p->name, 0); - str = str_concat (str, tstr, STR_ADDSPACE | STR_DOFREE); + + 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); + Lst_Close(path); } - + return (str); } @@ -1151,6 +1675,9 @@ Dir_MakeFlags (flag, path) * 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. * @@ -1161,21 +1688,20 @@ Dir_MakeFlags (flag, path) *----------------------------------------------------------------------- */ void -Dir_Destroy (pp) - ClientData pp; /* The directory descriptor to nuke */ +Dir_Destroy(void *pp) { - Path *p = (Path *) pp; + Path *p = (Path *)pp; p->refCount -= 1; if (p->refCount == 0) { LstNode ln; - ln = Lst_Member (openDirectories, (ClientData)p); - (void) Lst_Remove (openDirectories, ln); + ln = Lst_Member(openDirectories, p); + (void)Lst_Remove(openDirectories, ln); - Hash_DeleteTable (&p->files); - free((Address)p->name); - free((Address)p); + Hash_DeleteTable(&p->files); + free(p->name); + free(p); } } @@ -1185,6 +1711,9 @@ Dir_Destroy (pp) * Clear out all elements of the given search path. This is different * from destroying the list, notice. * + * Input: + * path Path to clear + * * Results: * None. * @@ -1194,16 +1723,15 @@ Dir_Destroy (pp) *----------------------------------------------------------------------- */ void -Dir_ClearPath(path) - Lst path; /* Path to clear */ +Dir_ClearPath(Lst path) { Path *p; while (!Lst_IsEmpty(path)) { p = (Path *)Lst_DeQueue(path); - Dir_Destroy((ClientData) p); + Dir_Destroy(p); } } - + /*- *----------------------------------------------------------------------- @@ -1211,6 +1739,10 @@ Dir_ClearPath(path) * Concatenate two paths, adding the second to the end of the first. * Makes sure to avoid duplicates. * + * Input: + * path1 Dest + * path2 Source + * * Results: * None * @@ -1220,55 +1752,51 @@ Dir_ClearPath(path) *----------------------------------------------------------------------- */ void -Dir_Concat(path1, path2) - Lst path1; /* Dest */ - Lst path2; /* Source */ +Dir_Concat(Lst path1, Lst path2) { LstNode ln; Path *p; - for (ln = Lst_First(path2); ln != NILLNODE; ln = Lst_Succ(ln)) { + for (ln = Lst_First(path2); ln != NULL; ln = Lst_Succ(ln)) { p = (Path *)Lst_Datum(ln); - if (Lst_Member(path1, (ClientData)p) == NILLNODE) { + if (Lst_Member(path1, p) == NULL) { p->refCount += 1; - (void)Lst_AtEnd(path1, (ClientData)p); + (void)Lst_AtEnd(path1, p); } } } /********** DEBUG INFO **********/ void -Dir_PrintDirectories() +Dir_PrintDirectories(void) { LstNode ln; Path *p; - - printf ("#*** Directory Cache:\n"); - printf ("# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n", + + 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)); - printf ("# %-20s referenced\thits\n", "directory"); - if (Lst_Open (openDirectories) == SUCCESS) { - while ((ln = Lst_Next (openDirectories)) != NILLNODE) { - p = (Path *) Lst_Datum (ln); - printf ("# %-20s %10d\t%4d\n", p->name, p->refCount, p->hits); + 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); + Lst_Close(openDirectories); } } -static int DirPrintDir (p, dummy) - ClientData p; - ClientData dummy; -{ - printf ("%s ", ((Path *) p)->name); +static int +DirPrintDir(void *p, void *dummy) +{ + fprintf(debug_file, "%s ", ((Path *)p)->name); return (dummy ? 0 : 0); } void -Dir_PrintPath (path) - Lst path; +Dir_PrintPath(Lst path) { - Lst_ForEach (path, DirPrintDir, (ClientData)0); + Lst_ForEach(path, DirPrintDir, NULL); } diff --git a/dir.h b/dir.h new file mode 100644 index 000000000000..aa004504a5bc --- /dev/null +++ b/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/dirname.c b/dirname.c new file mode 100644 index 000000000000..8b6b6c3d44de --- /dev/null +++ b/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 +#endif +#ifndef HAVE_DIRNAME + +#include + +#ifdef HAVE_LIMITS_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#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/find_lib.sh b/find_lib.sh new file mode 100755 index 000000000000..3c2e4af2f251 --- /dev/null +++ b/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/for.c b/for.c new file mode 100644 index 000000000000..33bcf1392b8e --- /dev/null +++ b/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 +#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 +#include + +#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 in + * ... + * .endfor + * + * The trick is to look for the matching end inside for for loop + * To do that, we count the current nesting level of the for loops. + * and the .endfor statements, accumulating all the statements between + * the initial .for loop and the matching .endfor; + * then we evaluate the for loop for each variable in the varlist. + * + * Note that any nested fors are just passed through; they get handled + * recursively in For_Eval when we're expanding the enclosing for in + * For_Run. + */ + +static int forLevel = 0; /* Nesting level */ + +/* + * State of a for loop. + */ +typedef struct _For { + Buffer buf; /* Body of loop */ + 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 in + * + * 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...} 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 */ + 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} */ + 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/getopt.c b/getopt.c new file mode 100644 index 000000000000..c40bc1356d8c --- /dev/null +++ b/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 +#include +#include + + +#define BADCH (int)'?' +#define BADARG (int)':' +#define EMSG "" + +int opterr = 1, /* if error message should be printed */ + optind = 1, /* index into parent argv vector */ + optopt = BADCH, /* character checked for validity */ + optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ + +/* + * getopt -- + * Parse argc/argv argument vector. + */ +int +getopt(nargc, nargv, ostr) + int nargc; + char * const *nargv; + const char *ostr; +{ + extern char *__progname; + static char *place = EMSG; /* option letter processing */ + char *oli; /* option letter list index */ + +#ifndef BSD4_4 + if (!__progname) { + if (__progname = strrchr(nargv[0], '/')) + ++__progname; + else + __progname = nargv[0]; + } +#endif + + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc || *(place = nargv[optind]) != '-') { + place = EMSG; + return (-1); + } + if (place[1] && *++place == '-' /* found "--" */ + && !place[1]) { /* and not "--foo" */ + ++optind; + place = EMSG; + return (-1); + } + } /* option letter okay? */ + if ((optopt = (int)*place++) == (int)':' || + !(oli = strchr(ostr, optopt))) { + /* + * if the user didn't specify '-' as an option, + * assume it means -1. + */ + if (optopt == (int)'-') + return (-1); + if (!*place) + ++optind; + if (opterr && *ostr != ':') + (void)fprintf(stderr, + "%s: illegal option -- %c\n", __progname, optopt); + return (BADCH); + } + if (*++oli != ':') { /* don't need argument */ + optarg = NULL; + if (!*place) + ++optind; + } + else { /* need an argument */ + if (*place) /* no white space */ + optarg = place; + else if (nargc <= ++optind) { /* no arg */ + place = EMSG; + if (*ostr == ':') + return (BADARG); + if (opterr) + (void)fprintf(stderr, + "%s: option requires an argument -- %c\n", + __progname, optopt); + return (BADCH); + } + else /* white space */ + optarg = nargv[optind]; + place = EMSG; + ++optind; + } + return (optopt); /* dump back option letter */ +} +#endif +#ifdef MAIN +#ifndef BSD4_4 +char *__progname; +#endif + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int c; + char *opts = argv[1]; + + --argc; + ++argv; + + while ((c = getopt(argc, argv, opts)) != EOF) { + switch (c) { + case '-': + if (optarg) + printf("--%s ", optarg); + break; + case '?': + exit(1); + break; + default: + if (optarg) + printf("-%c %s ", c, optarg); + else + printf("-%c ", c); + break; + } + } + + if (optind < argc) { + printf("-- "); + for (; optind < argc; ++optind) { + printf("%s ", argv[optind]); + } + } + printf("\n"); + exit(0); +} +#endif diff --git a/usr.bin/make/hash.c b/hash.c similarity index 68% rename from usr.bin/make/hash.c rename to hash.c index 37e839862b56..a22e2f2aa26e 100644 --- a/usr.bin/make/hash.c +++ b/hash.c @@ -1,6 +1,39 @@ +/* $NetBSD: hash.c,v 1.19 2009/01/24 10:59:09 dsl Exp $ */ + /* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. + * 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. * @@ -36,9 +69,18 @@ * SUCH DAMAGE. */ +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: hash.c,v 1.19 2009/01/24 10:59:09 dsl Exp $"; +#else +#include #ifndef lint -static char sccsid[] = "@(#)hash.c 8.2 (Berkeley) 4/28/95"; +#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 -- * @@ -56,23 +98,30 @@ static char sccsid[] = "@(#)hash.c 8.2 (Berkeley) 4/28/95"; * defined: */ -static void RebuildTable __P((Hash_Table *)); +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 8 +#define rebuildLimit 3 /* *--------------------------------------------------------- - * + * * Hash_InitTable -- * * This routine just sets up the hash table. * - * Results: + * 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: @@ -82,19 +131,13 @@ static void RebuildTable __P((Hash_Table *)); */ void -Hash_InitTable(t, numBuckets) - register Hash_Table *t; /* Structure to use to hold table. */ - int numBuckets; /* How many buckets to create for starters. - * This number is rounded up to a power of - * two. If <= 0, a reasonable default is - * chosen. The table will grow in size later - * as needed. */ +Hash_InitTable(Hash_Table *t, int numBuckets) { - register int i; - register struct Hash_Entry **hp; + int i; + struct Hash_Entry **hp; /* - * Round up the size to a power of two. + * Round up the size to a power of two. */ if (numBuckets <= 0) i = 16; @@ -105,7 +148,7 @@ Hash_InitTable(t, numBuckets) t->numEntries = 0; t->size = i; t->mask = i - 1; - t->bucketPtr = hp = (struct Hash_Entry **)emalloc(sizeof(*hp) * i); + t->bucketPtr = hp = bmake_malloc(sizeof(*hp) * i); while (--i >= 0) *hp++ = NULL; } @@ -119,7 +162,7 @@ Hash_InitTable(t, numBuckets) * and frees up the memory space it occupied (except for * the space in the Hash_Table structure). * - * Results: + * Results: * None. * * Side Effects: @@ -129,23 +172,22 @@ Hash_InitTable(t, numBuckets) */ void -Hash_DeleteTable(t) - Hash_Table *t; +Hash_DeleteTable(Hash_Table *t) { - register struct Hash_Entry **hp, *h, *nexth = NULL; - register int i; + 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((char *)h); + free(h); } } - free((char *)t->bucketPtr); + free(t->bucketPtr); /* * Set up the hash table to cause memory faults on any future access - * attempts until re-initialization. + * attempts until re-initialization. */ t->bucketPtr = NULL; } @@ -157,6 +199,10 @@ Hash_DeleteTable(t) * * 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 @@ -169,13 +215,11 @@ Hash_DeleteTable(t) */ Hash_Entry * -Hash_FindEntry(t, key) - Hash_Table *t; /* Hash table to search. */ - char *key; /* A hash key. */ +Hash_FindEntry(Hash_Table *t, const char *key) { - register Hash_Entry *e; - register unsigned h; - register char *p; + Hash_Entry *e; + unsigned h; + const char *p; for (h = 0, p = key; *p;) h = (h << 5) - h + *p++; @@ -183,7 +227,7 @@ Hash_FindEntry(t, 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); + return NULL; } /* @@ -194,6 +238,12 @@ Hash_FindEntry(t, key) * 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 @@ -206,15 +256,11 @@ Hash_FindEntry(t, key) */ Hash_Entry * -Hash_CreateEntry(t, key, newPtr) - register Hash_Table *t; /* Hash table to search. */ - char *key; /* A hash key. */ - Boolean *newPtr; /* Filled in with TRUE if new entry created, - * FALSE otherwise. */ +Hash_CreateEntry(Hash_Table *t, const char *key, Boolean *newPtr) { - register Hash_Entry *e; - register unsigned h; - register char *p; + Hash_Entry *e; + unsigned h; + const char *p; int keylen; struct Hash_Entry **hp; @@ -237,17 +283,17 @@ Hash_CreateEntry(t, key, newPtr) /* * The desired entry isn't there. Before allocating a new entry, * expand the table if necessary (and this changes the resulting - * bucket chain). + * bucket chain). */ if (t->numEntries >= rebuildLimit * t->size) RebuildTable(t); - e = (Hash_Entry *) emalloc(sizeof(*e) + keylen); + e = bmake_malloc(sizeof(*e) + keylen); hp = &t->bucketPtr[h & t->mask]; e->next = *hp; *hp = e; - e->clientData = NULL; + Hash_SetValue(e, NULL); e->namehash = h; - (void) strcpy(e->name, p); + (void)strcpy(e->name, p); t->numEntries++; if (newPtr != NULL) @@ -273,11 +319,9 @@ Hash_CreateEntry(t, key, newPtr) */ void -Hash_DeleteEntry(t, e) - Hash_Table *t; - Hash_Entry *e; +Hash_DeleteEntry(Hash_Table *t, Hash_Entry *e) { - register Hash_Entry **hp, *p; + Hash_Entry **hp, *p; if (e == NULL) return; @@ -285,12 +329,12 @@ Hash_DeleteEntry(t, e) (p = *hp) != NULL; hp = &p->next) { if (p == e) { *hp = p->next; - free((char *)p); + free(p); t->numEntries--; return; } } - (void) write(2, "bad call to Hash_DeleteEntry\n", 29); + (void)write(2, "bad call to Hash_DeleteEntry\n", 29); abort(); } @@ -301,7 +345,11 @@ Hash_DeleteEntry(t, e) * This procedure sets things up for a complete search * of all entries recorded in the hash table. * - * Results: + * 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. * @@ -314,10 +362,7 @@ Hash_DeleteEntry(t, e) */ Hash_Entry * -Hash_EnumFirst(t, searchPtr) - Hash_Table *t; /* Table to be searched. */ - register Hash_Search *searchPtr;/* Area in which to keep state - * about search.*/ +Hash_EnumFirst(Hash_Table *t, Hash_Search *searchPtr) { searchPtr->tablePtr = t; searchPtr->nextIndex = 0; @@ -331,6 +376,9 @@ Hash_EnumFirst(t, 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 @@ -344,11 +392,9 @@ Hash_EnumFirst(t, searchPtr) */ Hash_Entry * -Hash_EnumNext(searchPtr) - register Hash_Search *searchPtr; /* Area used to keep state about - search. */ +Hash_EnumNext(Hash_Search *searchPtr) { - register Hash_Entry *e; + Hash_Entry *e; Hash_Table *t = searchPtr->tablePtr; /* @@ -365,7 +411,7 @@ Hash_EnumNext(searchPtr) */ while (e == NULL) { if (searchPtr->nextIndex >= t->size) - return (NULL); + return NULL; e = t->bucketPtr[searchPtr->nextIndex++]; } searchPtr->hashEntryPtr = e; @@ -379,7 +425,7 @@ Hash_EnumNext(searchPtr) * This local routine makes a new hash table that * is larger than the old one. * - * Results: + * Results: * None. * * Side Effects: @@ -390,12 +436,11 @@ Hash_EnumNext(searchPtr) */ static void -RebuildTable(t) - register Hash_Table *t; +RebuildTable(Hash_Table *t) { - register Hash_Entry *e, *next = NULL, **hp, **xp; - register int i, mask; - register Hash_Entry **oldhp; + Hash_Entry *e, *next = NULL, **hp, **xp; + int i, mask; + Hash_Entry **oldhp; int oldsize; oldhp = t->bucketPtr; @@ -403,7 +448,7 @@ RebuildTable(t) i <<= 1; t->size = i; t->mask = mask = i - 1; - t->bucketPtr = hp = (struct Hash_Entry **) emalloc(sizeof(*hp) * i); + t->bucketPtr = hp = bmake_malloc(sizeof(*hp) * i); while (--i >= 0) *hp++ = NULL; for (hp = oldhp, i = oldsize; --i >= 0;) { @@ -414,5 +459,5 @@ RebuildTable(t) *xp = e; } } - free((char *)oldhp); + free(oldhp); } diff --git a/usr.bin/make/hash.h b/hash.h similarity index 54% rename from usr.bin/make/hash.h rename to hash.h index 63e8783726ce..31d2ff1ea757 100644 --- a/usr.bin/make/hash.h +++ b/hash.h @@ -1,6 +1,40 @@ +/* $NetBSD: hash.h,v 1.10 2009/01/24 10:59:09 dsl Exp $ */ + /* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. + * 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. * @@ -35,7 +69,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)hash.h 8.2 (Berkeley) 4/28/95 + * from: @(#)hash.h 8.1 (Berkeley) 6/6/93 */ /* hash.h -- @@ -47,7 +81,7 @@ #ifndef _HASH #define _HASH -/* +/* * The following defines one entry in the hash table. */ @@ -55,8 +89,10 @@ typedef struct Hash_Entry { struct Hash_Entry *next; /* Used to link together all the * entries associated with the same * bucket. */ - ClientData clientData; /* Arbitrary piece of data associated - * with key. */ + union { + void *clientPtr; /* Arbitrary pointer */ + time_t clientTime; /* Arbitrary Time */ + } clientInfo; unsigned namehash; /* hash value of key */ char name[1]; /* key string */ } Hash_Entry; @@ -69,7 +105,7 @@ typedef struct Hash_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. */ @@ -85,32 +121,34 @@ typedef struct Hash_Search { */ /* - * ClientData Hash_GetValue(h) - * Hash_Entry *h; + * void * Hash_GetValue(h) + * Hash_Entry *h; */ -#define Hash_GetValue(h) ((h)->clientData) +#define Hash_GetValue(h) ((h)->clientInfo.clientPtr) +#define Hash_GetTimeValue(h) ((h)->clientInfo.clientTime) -/* - * Hash_SetValue(h, val); - * Hash_Entry *h; - * char *val; +/* + * Hash_SetValue(h, val); + * Hash_Entry *h; + * char *val; */ -#define Hash_SetValue(h, val) ((h)->clientData = (ClientData) (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 +/* + * Hash_Size(n) returns the number of words in an object of n bytes */ #define Hash_Size(n) (((n) + sizeof (int) - 1) / sizeof (int)) -void Hash_InitTable __P((Hash_Table *, int)); -void Hash_DeleteTable __P((Hash_Table *)); -Hash_Entry *Hash_FindEntry __P((Hash_Table *, char *)); -Hash_Entry *Hash_CreateEntry __P((Hash_Table *, char *, Boolean *)); -void Hash_DeleteEntry __P((Hash_Table *, Hash_Entry *)); -Hash_Entry *Hash_EnumFirst __P((Hash_Table *, Hash_Search *)); -Hash_Entry *Hash_EnumNext __P((Hash_Search *)); +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/install-sh b/install-sh new file mode 100755 index 000000000000..a2473298efcb --- /dev/null +++ b/install-sh @@ -0,0 +1,201 @@ +: +# NAME: +# install.sh - portable version of install(1) +# +# SYNOPSIS: +# install [-CNcs] [-f flags] [-i errs] [-o owner] [-g group] [-m mode] file1 file2 ... +# install -d [-i errs] [-o owner] [-g group] [-m mode] directory ... +# +# DESCRIPTION: +# Compatible with BSD install(1). Except that '-c' is always +# true and we always move an already installed target aside as +# this is important on many systems. Recent BSD install(1) +# versions have a '-b' option for this. +# +# +# OPTIONS: +# -b move previous target file aside (always true). +# +# -B "suffix" +# use "suffix" instead of .old for saving existing target. +# +# -c copy rather than move the file into place (always true). +# +# -C compare. Only install if target is missing or +# different. +# +# -N newer. Only install if target is missing or older. +# +# -s strip target +# +# -o "owner" +# make target owned by "owner" +# +# -g "group" +# make target group owned by "group" +# +# -m "mode" +# set permissions to "mode" +# +# -f "flags" +# Pass "flags" onto chflags(1) +# +# -i "errs" +# Ignore errors from steps indicated by "errs" (``s,o,g,m''). +# +# BUGS: +# The '-i' option is to save your sanity when 'bsd.prog.mk' +# insists on haveing a '-o' "owner" option which is doomed to +# fail on many systems. We ignore '-b', '-B' and '-c' options. +# +# AUTHOR: +# Simon J. Gerraty +# + +# RCSid: +# $Id: install-sh,v 1.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/job.c b/job.c new file mode 100644 index 000000000000..395966a6a061 --- /dev/null +++ b/job.c @@ -0,0 +1,2988 @@ +/* $NetBSD: job.c,v 1.161 2012/04/07 18:29:08 christos 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.161 2012/04/07 18:29:08 christos Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)job.c 8.2 (Berkeley) 3/19/94"; +#else +__RCSID("$NetBSD: job.c,v 1.161 2012/04/07 18:29:08 christos 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 +#include +#include +#include +#include "wait.h" + +#include +#include +#if !defined(USE_SELECT) && defined(HAVE_POLL_H) +#include +#else +#ifndef USE_SELECT /* no poll.h */ +# define USE_SELECT +#endif +#if defined(HAVE_SYS_SELECT_H) +# include +#endif +#endif +#include +#include +#include +#include +#if defined(HAVE_SYS_SOCKET_H) +# include +#endif + +#include "make.h" +#include "hash.h" +#include "dir.h" +#include "job.h" +#include "pathnames.h" +#include "trace.h" +# 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) __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 __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 __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. + * + *----------------------------------------------------------------------- + */ +__dead static void +JobPassSig_int(int signo) +{ + /* Run .INTERRUPT target then exit */ + JobInterrupt(TRUE, signo); +} + +__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- < 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 + * Job_Init. + */ + if (newShell.name == NULL) { + Parse_Error(PARSE_FATAL, "Neither path nor name specified"); + free(words); + return(FAILURE); + } else { + if ((sh = JobMatchShell(newShell.name)) == NULL) { + Parse_Error(PARSE_WARNING, "%s: No matching shell", + newShell.name); + free(words); + return(FAILURE); + } + commandShell = sh; + shellName = newShell.name; + } + } else { + /* + * The user provided a path. If s/he gave nothing else (fullSpec is + * FALSE), try and find a matching shell in the ones we know of. + * Else we just take the specification at its word and copy it + * to a new location. In either case, we need to record the + * path the user gave for the shell. + */ + shellPath = path; + path = strrchr(path, '/'); + if (path == NULL) { + path = UNCONST(shellPath); + } else { + path += 1; + } + if (newShell.name != NULL) { + shellName = newShell.name; + } else { + shellName = path; + } + if (!fullSpec) { + if ((sh = JobMatchShell(shellName)) == NULL) { + Parse_Error(PARSE_WARNING, "%s: No matching shell", + shellName); + free(words); + return(FAILURE); + } + commandShell = sh; + } else { + commandShell = 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/job.h b/job.h new file mode 100644 index 000000000000..560b70bf7326 --- /dev/null +++ b/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/usr.bin/make/lst.h b/lst.h similarity index 53% rename from usr.bin/make/lst.h rename to lst.h index 8d8abe04c814..e0674071a98f 100644 --- a/usr.bin/make/lst.h +++ b/lst.h @@ -1,6 +1,41 @@ +/* $NetBSD: lst.h,v 1.18 2009/01/23 21:58:27 dsl Exp $ */ + /* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. + * 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. * @@ -35,7 +70,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)lst.h 8.2 (Berkeley) 4/28/95 + * from: @(#)lst.h 8.1 (Berkeley) 6/6/93 */ /*- @@ -45,28 +80,20 @@ #ifndef _LST_H_ #define _LST_H_ -#include -#if __STDC__ +#include #include -#endif + +#include "sprite.h" /* * basic typedef. This is what the Lst_ functions handle */ -typedef struct Lst *Lst; -typedef struct LstNode *LstNode; +typedef struct List *Lst; +typedef struct ListNode *LstNode; -#define NILLST ((Lst) NIL) -#define NILLNODE ((LstNode) NIL) - -/* - * NOFREE can be used as the freeProc to Lst_Destroy when the elements are - * not to be freed. - * NOCOPY performs similarly when given as the copyProc to Lst_Duplicate. - */ -#define NOFREE ((void (*) __P((ClientData))) 0) -#define NOCOPY ((ClientData (*) __P((ClientData))) 0) +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 */ @@ -75,89 +102,88 @@ typedef struct LstNode *LstNode; * Creation/destruction functions */ /* Create a new list */ -Lst Lst_Init __P((Boolean)); +Lst Lst_Init(Boolean); /* Duplicate an existing list */ -Lst Lst_Duplicate __P((Lst, ClientData (*)(ClientData))); +Lst Lst_Duplicate(Lst, DuplicateProc *); /* Destroy an old one */ -void Lst_Destroy __P((Lst, void (*)(ClientData))); +void Lst_Destroy(Lst, FreeProc *); /* True if list is empty */ -Boolean Lst_IsEmpty __P((Lst)); +Boolean Lst_IsEmpty(Lst); /* * Functions to modify a list */ /* Insert an element before another */ -ReturnStatus Lst_Insert __P((Lst, LstNode, ClientData)); +ReturnStatus Lst_InsertBefore(Lst, LstNode, void *); /* Insert an element after another */ -ReturnStatus Lst_Append __P((Lst, LstNode, ClientData)); +ReturnStatus Lst_InsertAfter(Lst, LstNode, void *); /* Place an element at the front of a lst. */ -ReturnStatus Lst_AtFront __P((Lst, ClientData)); +ReturnStatus Lst_AtFront(Lst, void *); /* Place an element at the end of a lst. */ -ReturnStatus Lst_AtEnd __P((Lst, ClientData)); +ReturnStatus Lst_AtEnd(Lst, void *); /* Remove an element */ -ReturnStatus Lst_Remove __P((Lst, LstNode)); +ReturnStatus Lst_Remove(Lst, LstNode); /* Replace a node with a new value */ -ReturnStatus Lst_Replace __P((LstNode, ClientData)); +ReturnStatus Lst_Replace(LstNode, void *); /* Concatenate two lists */ -ReturnStatus Lst_Concat __P((Lst, Lst, int)); +ReturnStatus Lst_Concat(Lst, Lst, int); /* * Node-specific functions */ /* Return first element in list */ -LstNode Lst_First __P((Lst)); +LstNode Lst_First(Lst); /* Return last element in list */ -LstNode Lst_Last __P((Lst)); +LstNode Lst_Last(Lst); /* Return successor to given element */ -LstNode Lst_Succ __P((LstNode)); +LstNode Lst_Succ(LstNode); +/* Return predecessor to given element */ +LstNode Lst_Prev(LstNode); /* Get datum from LstNode */ -ClientData Lst_Datum __P((LstNode)); +void *Lst_Datum(LstNode); /* * Functions for entire lists */ /* Find an element in a list */ -LstNode Lst_Find __P((Lst, ClientData, - int (*)(ClientData, ClientData))); +LstNode Lst_Find(Lst, const void *, int (*)(const void *, const void *)); /* Find an element starting from somewhere */ -LstNode Lst_FindFrom __P((Lst, LstNode, ClientData, - int (*cProc)(ClientData, ClientData))); -/* +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 __P((Lst, ClientData)); +LstNode Lst_Member(Lst, void *); /* Apply a function to all elements of a lst */ -void Lst_ForEach __P((Lst, int (*)(ClientData, ClientData), - ClientData)); +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. */ -void Lst_ForEachFrom __P((Lst, LstNode, - int (*)(ClientData, ClientData), - ClientData)); +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 __P((Lst)); +ReturnStatus Lst_Open(Lst); /* Next element please */ -LstNode Lst_Next __P((Lst)); +LstNode Lst_Next(Lst); /* Done yet? */ -Boolean Lst_IsAtEnd __P((Lst)); +Boolean Lst_IsAtEnd(Lst); /* Finish table access */ -void Lst_Close __P((Lst)); +void Lst_Close(Lst); /* * for using the list as a queue */ /* Place an element at tail of queue */ -ReturnStatus Lst_EnQueue __P((Lst, ClientData)); +ReturnStatus Lst_EnQueue(Lst, void *); /* Remove an element from head of queue */ -ClientData Lst_DeQueue __P((Lst)); +void *Lst_DeQueue(Lst); #endif /* _LST_H_ */ diff --git a/lst.lib/Makefile b/lst.lib/Makefile new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/usr.bin/make/lst.lib/lstAppend.c b/lst.lib/lstAppend.c similarity index 76% rename from usr.bin/make/lst.lib/lstAppend.c rename to lst.lib/lstAppend.c index 93800db01a0b..4dafe831450e 100644 --- a/usr.bin/make/lst.lib/lstAppend.c +++ b/lst.lib/lstAppend.c @@ -1,3 +1,5 @@ +/* $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. @@ -13,11 +15,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -34,9 +32,18 @@ * SUCH DAMAGE. */ +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstAppend.c,v 1.14 2009/01/23 21:26:30 dsl Exp $"; +#else +#include #ifndef lint -static char sccsid[] = "@(#)lstAppend.c 8.2 (Berkeley) 4/28/95"; +#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 -- @@ -47,9 +54,14 @@ static char sccsid[] = "@(#)lstAppend.c 8.2 (Berkeley) 4/28/95"; /*- *----------------------------------------------------------------------- - * Lst_Append -- + * 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. * @@ -57,57 +69,54 @@ static char sccsid[] = "@(#)lstAppend.c 8.2 (Berkeley) 4/28/95"; * A new ListNode is created and linked in to the List. The lastPtr * field of the List will be altered if ln is the last node in the * list. lastPtr and firstPtr will alter if the list was empty and - * ln was NILLNODE. + * ln was NULL. * *----------------------------------------------------------------------- */ ReturnStatus -Lst_Append (l, ln, d) - Lst l; /* affected list */ - LstNode ln; /* node after which to append the datum */ - ClientData d; /* said datum */ +Lst_InsertAfter(Lst l, LstNode ln, void *d) { - register List list; - register ListNode lNode; - register ListNode nLNode; - - if (LstValid (l) && (ln == NILLNODE && LstIsEmpty (l))) { + 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 = (List)l; - lNode = (ListNode)ln; + + list = l; + lNode = ln; PAlloc (nLNode, ListNode); nLNode->datum = d; nLNode->useCount = nLNode->flags = 0; - - if (lNode == NilListNode) { + + if (lNode == NULL) { if (list->isCirc) { nLNode->nextPtr = nLNode->prevPtr = nLNode; } else { - nLNode->nextPtr = nLNode->prevPtr = NilListNode; + nLNode->nextPtr = nLNode->prevPtr = NULL; } list->firstPtr = list->lastPtr = nLNode; } else { nLNode->prevPtr = lNode; nLNode->nextPtr = lNode->nextPtr; - + lNode->nextPtr = nLNode; - if (nLNode->nextPtr != NilListNode) { + if (nLNode->nextPtr != NULL) { nLNode->nextPtr->prevPtr = nLNode; } - + if (lNode == list->lastPtr) { list->lastPtr = nLNode; } } - + return (SUCCESS); } diff --git a/usr.bin/make/lst.lib/lstAtEnd.c b/lst.lib/lstAtEnd.c similarity index 77% rename from usr.bin/make/lst.lib/lstAtEnd.c rename to lst.lib/lstAtEnd.c index e61a28c0673f..10f191a2060c 100644 --- a/usr.bin/make/lst.lib/lstAtEnd.c +++ b/lst.lib/lstAtEnd.c @@ -1,3 +1,5 @@ +/* $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. @@ -13,11 +15,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -34,9 +32,18 @@ * SUCH DAMAGE. */ +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstAtEnd.c,v 1.13 2009/01/23 21:26:30 dsl Exp $"; +#else +#include #ifndef lint -static char sccsid[] = "@(#)lstAtEnd.c 8.2 (Berkeley) 4/28/95"; +#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 -- @@ -44,12 +51,16 @@ static char sccsid[] = "@(#)lstAtEnd.c 8.2 (Berkeley) 4/28/95"; */ #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. * @@ -59,12 +70,10 @@ static char sccsid[] = "@(#)lstAtEnd.c 8.2 (Berkeley) 4/28/95"; *----------------------------------------------------------------------- */ ReturnStatus -Lst_AtEnd (l, d) - Lst l; /* List to which to add the datum */ - ClientData d; /* Datum to add */ +Lst_AtEnd(Lst l, void *d) { - register LstNode end; - - end = Lst_Last (l); - return (Lst_Append (l, end, d)); + LstNode end; + + end = Lst_Last(l); + return (Lst_InsertAfter(l, end, d)); } diff --git a/usr.bin/make/lst.lib/lstAtFront.c b/lst.lib/lstAtFront.c similarity index 80% rename from usr.bin/make/lst.lib/lstAtFront.c rename to lst.lib/lstAtFront.c index 5e8b7f1ab4c2..d8be16647387 100644 --- a/usr.bin/make/lst.lib/lstAtFront.c +++ b/lst.lib/lstAtFront.c @@ -1,3 +1,5 @@ +/* $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. @@ -13,11 +15,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -34,9 +32,18 @@ * SUCH DAMAGE. */ +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstAtFront.c,v 1.13 2009/01/23 21:26:30 dsl Exp $"; +#else +#include #ifndef lint -static char sccsid[] = "@(#)lstAtFront.c 8.2 (Berkeley) 4/28/95"; +#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 -- @@ -60,12 +67,10 @@ static char sccsid[] = "@(#)lstAtFront.c 8.2 (Berkeley) 4/28/95"; *----------------------------------------------------------------------- */ ReturnStatus -Lst_AtFront (l, d) - Lst l; - ClientData d; +Lst_AtFront(Lst l, void *d) { - register LstNode front; - - front = Lst_First (l); - return (Lst_Insert (l, front, d)); + LstNode front; + + front = Lst_First(l); + return (Lst_InsertBefore(l, front, d)); } diff --git a/usr.bin/make/lst.lib/lstClose.c b/lst.lib/lstClose.c similarity index 83% rename from usr.bin/make/lst.lib/lstClose.c rename to lst.lib/lstClose.c index 6f9174f3db8a..06b68c5c038a 100644 --- a/usr.bin/make/lst.lib/lstClose.c +++ b/lst.lib/lstClose.c @@ -1,3 +1,5 @@ +/* $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. @@ -13,11 +15,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -34,9 +32,18 @@ * SUCH DAMAGE. */ +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstClose.c,v 1.11 2006/10/27 21:37:25 dsl Exp $"; +#else +#include #ifndef lint -static char sccsid[] = "@(#)lstClose.c 8.2 (Berkeley) 4/28/95"; +#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 -- @@ -55,6 +62,9 @@ static char sccsid[] = "@(#)lstClose.c 8.2 (Berkeley) 4/28/95"; * Lst_Close -- * Close a list which was opened for sequential access. * + * Input: + * l The list to close + * * Results: * None. * @@ -64,11 +74,10 @@ static char sccsid[] = "@(#)lstClose.c 8.2 (Berkeley) 4/28/95"; *----------------------------------------------------------------------- */ void -Lst_Close (l) - Lst l; /* The list to close */ +Lst_Close(Lst l) { - register List list = (List) l; - + List list = l; + if (LstValid(l) == TRUE) { list->isOpen = FALSE; list->atEnd = Unknown; diff --git a/usr.bin/make/lst.lib/lstConcat.c b/lst.lib/lstConcat.c similarity index 80% rename from usr.bin/make/lst.lib/lstConcat.c rename to lst.lib/lstConcat.c index e7726a862b60..534d34e45424 100644 --- a/usr.bin/make/lst.lib/lstConcat.c +++ b/lst.lib/lstConcat.c @@ -1,3 +1,5 @@ +/* $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. @@ -13,11 +15,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -34,9 +32,18 @@ * SUCH DAMAGE. */ +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstConcat.c,v 1.16 2008/12/13 15:19:29 dsl Exp $"; +#else +#include #ifndef lint -static char sccsid[] = "@(#)lstConcat.c 8.2 (Berkeley) 4/28/95"; +#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 -- @@ -55,33 +62,35 @@ static char sccsid[] = "@(#)lstConcat.c 8.2 (Berkeley) 4/28/95"; * 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 the first list. + * New elements are created and appended the first list. *----------------------------------------------------------------------- */ ReturnStatus -Lst_Concat (l1, l2, flags) - Lst l1; /* The list to which l2 is to be appended */ - Lst l2; /* The list to append to l1 */ - int flags; /* LST_CONCNEW if LstNode's should be duplicated - * LST_CONCLINK if should just be relinked */ +Lst_Concat(Lst l1, Lst l2, int flags) { - register ListNode ln; /* original LstNode */ - register ListNode nln; /* new LstNode */ - register ListNode last; /* the last element in the list. Keeps + ListNode ln; /* original LstNode */ + ListNode nln; /* new LstNode */ + ListNode last; /* the last element in the list. Keeps * bookkeeping until the end */ - register List list1 = (List)l1; - register List list2 = (List)l2; + List list1 = l1; + List list2 = l2; if (!LstValid (l1) || !LstValid (l2)) { return (FAILURE); } if (flags == LST_CONCLINK) { - if (list2->firstPtr != NilListNode) { + if (list2->firstPtr != NULL) { /* * We set the nextPtr of the * last element of list two to be NIL to make the loop easier and @@ -90,7 +99,7 @@ Lst_Concat (l1, l2, flags) * to NIL space and the first element will be untouched if it * existed before and will also point to NIL space if it didn't. */ - list2->lastPtr->nextPtr = NilListNode; + 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 @@ -100,14 +109,14 @@ Lst_Concat (l1, l2, flags) * the last element of the first list. */ list2->firstPtr->prevPtr = list1->lastPtr; - if (list1->lastPtr != NilListNode) { + if (list1->lastPtr != NULL) { list1->lastPtr->nextPtr = list2->firstPtr; } else { list1->firstPtr = list2->firstPtr; } list1->lastPtr = list2->lastPtr; } - if (list1->isCirc && list1->firstPtr != NilListNode) { + 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 @@ -116,8 +125,8 @@ Lst_Concat (l1, l2, flags) list1->firstPtr->prevPtr = list1->lastPtr; list1->lastPtr->nextPtr = list1->firstPtr; } - free ((Address)l2); - } else if (list2->firstPtr != NilListNode) { + 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 @@ -130,14 +139,14 @@ Lst_Concat (l1, l2, flags) * the first list must have been empty so the newly-created node is * made the first node of the list. */ - list2->lastPtr->nextPtr = NilListNode; + list2->lastPtr->nextPtr = NULL; for (last = list1->lastPtr, ln = list2->firstPtr; - ln != NilListNode; + ln != NULL; ln = ln->nextPtr) { PAlloc (nln, ListNode); nln->datum = ln->datum; - if (last != NilListNode) { + if (last != NULL) { last->nextPtr = nln; } else { list1->firstPtr = nln; @@ -149,7 +158,7 @@ Lst_Concat (l1, l2, flags) /* * Finish bookkeeping. The last new element becomes the last element - * of list one. + * of list one. */ list1->lastPtr = last; @@ -163,7 +172,7 @@ Lst_Concat (l1, l2, flags) list1->lastPtr->nextPtr = list1->firstPtr; list1->firstPtr->prevPtr = list1->lastPtr; } else { - last->nextPtr = NilListNode; + last->nextPtr = NULL; } if (list2->isCirc) { @@ -173,4 +182,4 @@ Lst_Concat (l1, l2, flags) return (SUCCESS); } - + diff --git a/usr.bin/make/lst.lib/lstDatum.c b/lst.lib/lstDatum.c similarity index 78% rename from usr.bin/make/lst.lib/lstDatum.c rename to lst.lib/lstDatum.c index 6b0988e0fa2b..6e2d9ad0e73b 100644 --- a/usr.bin/make/lst.lib/lstDatum.c +++ b/lst.lib/lstDatum.c @@ -1,3 +1,5 @@ +/* $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. @@ -13,11 +15,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -34,9 +32,18 @@ * SUCH DAMAGE. */ +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstDatum.c,v 1.13 2009/01/23 21:26:30 dsl Exp $"; +#else +#include #ifndef lint -static char sccsid[] = "@(#)lstDatum.c 8.2 (Berkeley) 4/28/95"; +#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 -- @@ -51,21 +58,20 @@ static char sccsid[] = "@(#)lstDatum.c 8.2 (Berkeley) 4/28/95"; * Return the datum stored in the given node. * * Results: - * The datum or (ick!) NIL if the node is invalid. + * The datum or NULL if the node is invalid. * * Side Effects: * None. * *----------------------------------------------------------------------- */ -ClientData -Lst_Datum (ln) - LstNode ln; +void * +Lst_Datum(LstNode ln) { - if (ln != NILLNODE) { - return (((ListNode)ln)->datum); + if (ln != NULL) { + return ((ln)->datum); } else { - return ((ClientData) NIL); + return NULL; } } diff --git a/usr.bin/make/lst.lib/lstDeQueue.c b/lst.lib/lstDeQueue.c similarity index 75% rename from usr.bin/make/lst.lib/lstDeQueue.c rename to lst.lib/lstDeQueue.c index 4b901257b001..bdb05cc1168d 100644 --- a/usr.bin/make/lst.lib/lstDeQueue.c +++ b/lst.lib/lstDeQueue.c @@ -1,3 +1,5 @@ +/* $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. @@ -13,11 +15,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -34,9 +32,18 @@ * SUCH DAMAGE. */ +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstDeQueue.c,v 1.14 2009/01/23 21:26:30 dsl Exp $"; +#else +#include #ifndef lint -static char sccsid[] = "@(#)lstDeQueue.c 8.2 (Berkeley) 4/28/95"; +#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 -- @@ -51,7 +58,7 @@ static char sccsid[] = "@(#)lstDeQueue.c 8.2 (Berkeley) 4/28/95"; * Remove and return the datum at the head of the given list. * * Results: - * The datum in the node at the head or (ick) NIL if the list + * The datum in the node at the head or NULL if the list * is empty. * * Side Effects: @@ -59,21 +66,20 @@ static char sccsid[] = "@(#)lstDeQueue.c 8.2 (Berkeley) 4/28/95"; * *----------------------------------------------------------------------- */ -ClientData -Lst_DeQueue (l) - Lst l; +void * +Lst_DeQueue(Lst l) { - ClientData rd; - register ListNode tln; - - tln = (ListNode) Lst_First (l); - if (tln == NilListNode) { - return ((ClientData) NIL); + void *rd; + ListNode tln; + + tln = Lst_First(l); + if (tln == NULL) { + return NULL; } - + rd = tln->datum; - if (Lst_Remove (l, (LstNode)tln) == FAILURE) { - return ((ClientData) NIL); + if (Lst_Remove(l, tln) == FAILURE) { + return NULL; } else { return (rd); } diff --git a/usr.bin/make/lst.lib/lstDestroy.c b/lst.lib/lstDestroy.c similarity index 69% rename from usr.bin/make/lst.lib/lstDestroy.c rename to lst.lib/lstDestroy.c index 56ea5c528fe6..92c5b2b2050c 100644 --- a/usr.bin/make/lst.lib/lstDestroy.c +++ b/lst.lib/lstDestroy.c @@ -1,3 +1,5 @@ +/* $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. @@ -13,11 +15,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -34,9 +32,18 @@ * SUCH DAMAGE. */ +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstDestroy.c,v 1.16 2008/12/13 15:19:29 dsl Exp $"; +#else +#include #ifndef lint -static char sccsid[] = "@(#)lstDestroy.c 8.2 (Berkeley) 4/28/95"; +#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 -- @@ -61,42 +68,34 @@ static char sccsid[] = "@(#)lstDestroy.c 8.2 (Berkeley) 4/28/95"; *----------------------------------------------------------------------- */ void -Lst_Destroy (l, freeProc) - Lst l; - register void (*freeProc) __P((ClientData)); +Lst_Destroy(Lst list, FreeProc *freeProc) { - register ListNode ln; - register ListNode tln = NilListNode; - register List list = (List)l; - - if (l == NILLST || ! l) { - /* - * Note the check for l == (Lst)0 to catch uninitialized static Lst's. - * Gross, but useful. - */ + ListNode ln; + ListNode tln = NULL; + + if (list == NULL) return; - } /* To ease scanning */ - if (list->lastPtr != NilListNode) - list->lastPtr->nextPtr = NilListNode; + if (list->lastPtr != NULL) + list->lastPtr->nextPtr = NULL; else { - free ((Address)l); + free(list); return; } if (freeProc) { - for (ln = list->firstPtr; ln != NilListNode; ln = tln) { + for (ln = list->firstPtr; ln != NULL; ln = tln) { tln = ln->nextPtr; - (*freeProc) (ln->datum); - free ((Address)ln); + freeProc(ln->datum); + free(ln); } } else { - for (ln = list->firstPtr; ln != NilListNode; ln = tln) { + for (ln = list->firstPtr; ln != NULL; ln = tln) { tln = ln->nextPtr; - free ((Address)ln); + free(ln); } } - - free ((Address)l); + + free(list); } diff --git a/usr.bin/make/lst.lib/lstDupl.c b/lst.lib/lstDupl.c similarity index 67% rename from usr.bin/make/lst.lib/lstDupl.c rename to lst.lib/lstDupl.c index 184b386228d3..2174ff782282 100644 --- a/usr.bin/make/lst.lib/lstDupl.c +++ b/lst.lib/lstDupl.c @@ -1,3 +1,5 @@ +/* $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. @@ -13,11 +15,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -34,9 +32,18 @@ * SUCH DAMAGE. */ +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstDupl.c,v 1.16 2009/01/23 21:26:30 dsl Exp $"; +#else +#include #ifndef lint -static char sccsid[] = "@(#)lstDupl.c 8.2 (Berkeley) 4/28/95"; +#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 -- @@ -49,51 +56,52 @@ static char sccsid[] = "@(#)lstDupl.c 8.2 (Berkeley) 4/28/95"; /*- *----------------------------------------------------------------------- * Lst_Duplicate -- - * Duplicate an entire list. If a function to copy a ClientData is + * 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 NILLST if failure. + * The new Lst structure or NULL if failure. * * Side Effects: * A new list is created. *----------------------------------------------------------------------- */ Lst -Lst_Duplicate (l, copyProc) - Lst l; /* the list to duplicate */ - /* A function to duplicate each ClientData */ - ClientData (*copyProc) __P((ClientData)); +Lst_Duplicate(Lst l, DuplicateProc *copyProc) { - register Lst nl; - register ListNode ln; - register List list = (List)l; - + Lst nl; + ListNode ln; + List list = l; + if (!LstValid (l)) { - return (NILLST); + return NULL; } - nl = Lst_Init (list->isCirc); - if (nl == NILLST) { - return (NILLST); + nl = Lst_Init(list->isCirc); + if (nl == NULL) { + return NULL; } ln = list->firstPtr; - while (ln != NilListNode) { - if (copyProc != NOCOPY) { - if (Lst_AtEnd (nl, (*copyProc) (ln->datum)) == FAILURE) { - return (NILLST); + 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 (NILLST); + } else if (Lst_AtEnd(nl, ln->datum) == FAILURE) { + return NULL; } if (list->isCirc && ln == list->lastPtr) { - ln = NilListNode; + ln = NULL; } else { ln = ln->nextPtr; } } - + return (nl); } diff --git a/usr.bin/make/lst.lib/lstEnQueue.c b/lst.lib/lstEnQueue.c similarity index 80% rename from usr.bin/make/lst.lib/lstEnQueue.c rename to lst.lib/lstEnQueue.c index 1016c88d2834..be386c91a512 100644 --- a/usr.bin/make/lst.lib/lstEnQueue.c +++ b/lst.lib/lstEnQueue.c @@ -1,3 +1,5 @@ +/* $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. @@ -13,11 +15,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -34,9 +32,18 @@ * SUCH DAMAGE. */ +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstEnQueue.c,v 1.13 2009/01/23 21:26:30 dsl Exp $"; +#else +#include #ifndef lint -static char sccsid[] = "@(#)lstEnQueue.c 8.2 (Berkeley) 4/28/95"; +#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-- @@ -51,7 +58,7 @@ static char sccsid[] = "@(#)lstEnQueue.c 8.2 (Berkeley) 4/28/95"; * Add the datum to the tail of the given list. * * Results: - * SUCCESS or FAILURE as returned by Lst_Append. + * SUCCESS or FAILURE as returned by Lst_InsertAfter. * * Side Effects: * the lastPtr field is altered all the time and the firstPtr field @@ -60,14 +67,12 @@ static char sccsid[] = "@(#)lstEnQueue.c 8.2 (Berkeley) 4/28/95"; *----------------------------------------------------------------------- */ ReturnStatus -Lst_EnQueue (l, d) - Lst l; - ClientData d; +Lst_EnQueue(Lst l, void *d) { if (LstValid (l) == FALSE) { return (FAILURE); } - - return (Lst_Append (l, Lst_Last(l), d)); + + return (Lst_InsertAfter(l, Lst_Last(l), d)); } diff --git a/usr.bin/make/lst.lib/lstFind.c b/lst.lib/lstFind.c similarity index 77% rename from usr.bin/make/lst.lib/lstFind.c rename to lst.lib/lstFind.c index bab85ccd173c..d07dbe7f9f8c 100644 --- a/usr.bin/make/lst.lib/lstFind.c +++ b/lst.lib/lstFind.c @@ -1,3 +1,5 @@ +/* $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. @@ -13,11 +15,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -34,9 +32,18 @@ * SUCH DAMAGE. */ +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstFind.c,v 1.15 2009/01/23 21:58:28 dsl Exp $"; +#else +#include #ifndef lint -static char sccsid[] = "@(#)lstFind.c 8.2 (Berkeley) 4/28/95"; +#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 -- @@ -52,7 +59,7 @@ static char sccsid[] = "@(#)lstFind.c 8.2 (Berkeley) 4/28/95"; * and the given datum. * * Results: - * The found node or NILLNODE if none matches. + * The found node or NULL if none matches. * * Side Effects: * None. @@ -60,11 +67,8 @@ static char sccsid[] = "@(#)lstFind.c 8.2 (Berkeley) 4/28/95"; *----------------------------------------------------------------------- */ LstNode -Lst_Find (l, d, cProc) - Lst l; - ClientData d; - int (*cProc) __P((ClientData, ClientData)); +Lst_Find(Lst l, const void *d, int (*cProc)(const void *, const void *)) { - return (Lst_FindFrom (l, Lst_First(l), d, cProc)); + return (Lst_FindFrom(l, Lst_First(l), d, cProc)); } diff --git a/usr.bin/make/lst.lib/lstFindFrom.c b/lst.lib/lstFindFrom.c similarity index 71% rename from usr.bin/make/lst.lib/lstFindFrom.c rename to lst.lib/lstFindFrom.c index 08de990afe5c..e2beab6321d5 100644 --- a/usr.bin/make/lst.lib/lstFindFrom.c +++ b/lst.lib/lstFindFrom.c @@ -1,3 +1,5 @@ +/* $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. @@ -13,11 +15,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -34,9 +32,18 @@ * SUCH DAMAGE. */ +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstFindFrom.c,v 1.15 2009/01/23 21:58:28 dsl Exp $"; +#else +#include #ifndef lint -static char sccsid[] = "@(#)lstFindFrom.c 8.2 (Berkeley) 4/28/95"; +#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 -- @@ -53,7 +60,7 @@ static char sccsid[] = "@(#)lstFindFrom.c 8.2 (Berkeley) 4/28/95"; * determine when it has been found. * * Results: - * The found node or NILLNODE + * The found node or NULL * * Side Effects: * None. @@ -61,34 +68,23 @@ static char sccsid[] = "@(#)lstFindFrom.c 8.2 (Berkeley) 4/28/95"; *----------------------------------------------------------------------- */ LstNode -Lst_FindFrom (l, ln, d, cProc) - Lst l; - register LstNode ln; - register ClientData d; - register int (*cProc) __P((ClientData, ClientData)); +Lst_FindFrom(Lst l, LstNode ln, const void *d, + int (*cProc)(const void *, const void *)) { - register ListNode tln; - Boolean found = FALSE; - + ListNode tln; + if (!LstValid (l) || LstIsEmpty (l) || !LstNodeValid (ln, l)) { - return (NILLNODE); + return NULL; } - - tln = (ListNode)ln; - + + tln = ln; + do { - if ((*cProc) (tln->datum, d) == 0) { - found = TRUE; - break; - } else { - tln = tln->nextPtr; - } - } while (tln != (ListNode)ln && tln != NilListNode); - - if (found) { - return ((LstNode)tln); - } else { - return (NILLNODE); - } + if ((*cProc)(tln->datum, d) == 0) + return (tln); + tln = tln->nextPtr; + } while (tln != ln && tln != NULL); + + return NULL; } diff --git a/usr.bin/make/lst.lib/lstFirst.c b/lst.lib/lstFirst.c similarity index 80% rename from usr.bin/make/lst.lib/lstFirst.c rename to lst.lib/lstFirst.c index 9a95018777ab..4e8334f8bcc9 100644 --- a/usr.bin/make/lst.lib/lstFirst.c +++ b/lst.lib/lstFirst.c @@ -1,3 +1,5 @@ +/* $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. @@ -13,11 +15,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -34,9 +32,18 @@ * SUCH DAMAGE. */ +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstFirst.c,v 1.12 2008/12/13 15:19:29 dsl Exp $"; +#else +#include #ifndef lint -static char sccsid[] = "@(#)lstFirst.c 8.2 (Berkeley) 4/28/95"; +#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 -- @@ -51,7 +58,7 @@ static char sccsid[] = "@(#)lstFirst.c 8.2 (Berkeley) 4/28/95"; * Return the first node on the given list. * * Results: - * The first node or NILLNODE if the list is empty. + * The first node or NULL if the list is empty. * * Side Effects: * None. @@ -59,13 +66,12 @@ static char sccsid[] = "@(#)lstFirst.c 8.2 (Berkeley) 4/28/95"; *----------------------------------------------------------------------- */ LstNode -Lst_First (l) - Lst l; +Lst_First(Lst l) { if (!LstValid (l) || LstIsEmpty (l)) { - return (NILLNODE); + return NULL; } else { - return ((LstNode)((List)l)->firstPtr); + return (l->firstPtr); } } diff --git a/usr.bin/make/lst.lib/lstForEach.c b/lst.lib/lstForEach.c similarity index 80% rename from usr.bin/make/lst.lib/lstForEach.c rename to lst.lib/lstForEach.c index 1d6ba9396dc6..917e4ea80236 100644 --- a/usr.bin/make/lst.lib/lstForEach.c +++ b/lst.lib/lstForEach.c @@ -1,3 +1,5 @@ +/* $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. @@ -13,11 +15,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -34,9 +32,18 @@ * SUCH DAMAGE. */ +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstForEach.c,v 1.13 2009/01/23 21:26:30 dsl Exp $"; +#else +#include #ifndef lint -static char sccsid[] = "@(#)lstForEach.c 8.2 (Berkeley) 4/28/95"; +#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 -- @@ -61,12 +68,9 @@ static char sccsid[] = "@(#)lstForEach.c 8.2 (Berkeley) 4/28/95"; *----------------------------------------------------------------------- */ /*VARARGS2*/ -void -Lst_ForEach (l, proc, d) - Lst l; - register int (*proc) __P((ClientData, ClientData)); - register ClientData d; +int +Lst_ForEach(Lst l, int (*proc)(void *, void *), void *d) { - Lst_ForEachFrom(l, Lst_First(l), proc, d); + return Lst_ForEachFrom(l, Lst_First(l), proc, d); } diff --git a/usr.bin/make/lst.lib/lstForEachFrom.c b/lst.lib/lstForEachFrom.c similarity index 72% rename from usr.bin/make/lst.lib/lstForEachFrom.c rename to lst.lib/lstForEachFrom.c index 7c9e51e749f0..c7f44adc476a 100644 --- a/usr.bin/make/lst.lib/lstForEachFrom.c +++ b/lst.lib/lstForEachFrom.c @@ -1,3 +1,5 @@ +/* $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. @@ -13,11 +15,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -34,9 +32,18 @@ * SUCH DAMAGE. */ +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstForEachFrom.c,v 1.17 2009/01/23 21:26:30 dsl Exp $"; +#else +#include #ifndef lint -static char sccsid[] = "@(#)lstForEachFrom.c 8.2 (Berkeley) 4/28/95"; +#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 -- @@ -51,7 +58,7 @@ static char sccsid[] = "@(#)lstForEachFrom.c 8.2 (Berkeley) 4/28/95"; * 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. + * zero if it should abort. * * Results: * None. @@ -62,50 +69,57 @@ static char sccsid[] = "@(#)lstForEachFrom.c 8.2 (Berkeley) 4/28/95"; *----------------------------------------------------------------------- */ /*VARARGS2*/ -void -Lst_ForEachFrom (l, ln, proc, d) - Lst l; - LstNode ln; - register int (*proc)(); - register ClientData d; +int +Lst_ForEachFrom(Lst l, LstNode ln, int (*proc)(void *, void *), + void *d) { - register ListNode tln = (ListNode)ln; - register List list = (List)l; - register ListNode next; + ListNode tln = ln; + List list = l; + ListNode next; Boolean done; int result; - + if (!LstValid (list) || LstIsEmpty (list)) { - return; + 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--; /* - * We're done with the traversal if - * - nothing's been added after the current node and - * - the next node to examine is the first in the queue or - * doesn't exist. + * Now check whether a node has been added. + * Note: this doesn't work if this node was deleted before + * the new node was added. */ - done = (next == tln->nextPtr && - (next == NilListNode || next == list->firstPtr)); - - next = tln->nextPtr; + 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/usr.bin/make/lst.lib/lstInit.c b/lst.lib/lstInit.c similarity index 78% rename from usr.bin/make/lst.lib/lstInit.c rename to lst.lib/lstInit.c index 17276be6f1e7..f98ac42b06a0 100644 --- a/usr.bin/make/lst.lib/lstInit.c +++ b/lst.lib/lstInit.c @@ -1,3 +1,5 @@ +/* $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. @@ -13,11 +15,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -34,9 +32,18 @@ * SUCH DAMAGE. */ +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstInit.c,v 1.12 2008/12/13 15:19:29 dsl Exp $"; +#else +#include #ifndef lint -static char sccsid[] = "@(#)lstInit.c 8.2 (Berkeley) 4/28/95"; +#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 -- @@ -50,6 +57,9 @@ static char sccsid[] = "@(#)lstInit.c 8.2 (Berkeley) 4/28/95"; * Lst_Init -- * Create and initialize a new list. * + * Input: + * circ TRUE if the list should be made circular + * * Results: * The created list. * @@ -59,18 +69,17 @@ static char sccsid[] = "@(#)lstInit.c 8.2 (Berkeley) 4/28/95"; *----------------------------------------------------------------------- */ Lst -Lst_Init(circ) - Boolean circ; /* TRUE if the list should be made circular */ +Lst_Init(Boolean circ) { - register List nList; - + List nList; + PAlloc (nList, List); - - nList->firstPtr = NilListNode; - nList->lastPtr = NilListNode; + + nList->firstPtr = NULL; + nList->lastPtr = NULL; nList->isOpen = FALSE; nList->isCirc = circ; nList->atEnd = Unknown; - - return ((Lst)nList); + + return (nList); } diff --git a/usr.bin/make/lst.lib/lstInsert.c b/lst.lib/lstInsert.c similarity index 75% rename from usr.bin/make/lst.lib/lstInsert.c rename to lst.lib/lstInsert.c index 724924adf071..77187bb327e2 100644 --- a/usr.bin/make/lst.lib/lstInsert.c +++ b/lst.lib/lstInsert.c @@ -1,3 +1,5 @@ +/* $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. @@ -13,11 +15,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -34,9 +32,18 @@ * SUCH DAMAGE. */ +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstInsert.c,v 1.14 2009/01/23 21:26:30 dsl Exp $"; +#else +#include #ifndef lint -static char sccsid[] = "@(#)lstInsert.c 8.2 (Berkeley) 4/28/95"; +#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 -- @@ -47,10 +54,15 @@ static char sccsid[] = "@(#)lstInsert.c 8.2 (Berkeley) 4/28/95"; /*- *----------------------------------------------------------------------- - * Lst_Insert -- + * 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. * @@ -61,53 +73,50 @@ static char sccsid[] = "@(#)lstInsert.c 8.2 (Berkeley) 4/28/95"; *----------------------------------------------------------------------- */ ReturnStatus -Lst_Insert (l, ln, d) - Lst l; /* list to manipulate */ - LstNode ln; /* node before which to insert d */ - ClientData d; /* datum to be inserted */ +Lst_InsertBefore(Lst l, LstNode ln, void *d) { - register ListNode nLNode; /* new lnode for d */ - register ListNode lNode = (ListNode)ln; - register List list = (List)l; + ListNode nLNode; /* new lnode for d */ + ListNode lNode = ln; + List list = l; /* * check validity of arguments */ - if (LstValid (l) && (LstIsEmpty (l) && ln == NILLNODE)) + 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 == NILLNODE) { + + if (ln == NULL) { if (list->isCirc) { nLNode->prevPtr = nLNode->nextPtr = nLNode; } else { - nLNode->prevPtr = nLNode->nextPtr = NilListNode; + nLNode->prevPtr = nLNode->nextPtr = NULL; } list->firstPtr = list->lastPtr = nLNode; } else { nLNode->prevPtr = lNode->prevPtr; nLNode->nextPtr = lNode; - - if (nLNode->prevPtr != NilListNode) { + + if (nLNode->prevPtr != NULL) { nLNode->prevPtr->nextPtr = nLNode; } lNode->prevPtr = nLNode; - + if (lNode == list->firstPtr) { list->firstPtr = nLNode; } } - + return (SUCCESS); } - + diff --git a/usr.bin/make/lst.lib/lstInt.h b/lst.lib/lstInt.h similarity index 76% rename from usr.bin/make/lst.lib/lstInt.h rename to lst.lib/lstInt.h index 1253660f8e81..34a2fbdce96a 100644 --- a/usr.bin/make/lst.lib/lstInt.h +++ b/lst.lib/lstInt.h @@ -1,3 +1,5 @@ +/* $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. @@ -13,11 +15,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -33,7 +31,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)lstInt.h 8.2 (Berkeley) 4/28/95 + * from: @(#)lstInt.h 8.1 (Berkeley) 6/6/93 */ /*- @@ -43,29 +41,28 @@ #ifndef _LSTINT_H_ #define _LSTINT_H_ -#include "lst.h" +#include "../lst.h" +#include "../make_malloc.h" typedef struct ListNode { struct ListNode *prevPtr; /* previous element in list */ struct ListNode *nextPtr; /* next in list */ - short useCount:8, /* Count of functions using the node. + 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 */ - ClientData datum; /* datum associated with this element */ + void *datum; /* datum associated with this element */ } *ListNode; /* * Flags required for synchronization */ #define LN_DELETED 0x0001 /* List node should be removed when done */ -#define NilListNode ((ListNode)-1) - typedef enum { Head, Middle, Tail, Unknown } Where; -typedef struct { +typedef struct List { ListNode firstPtr; /* first node in list */ ListNode lastPtr; /* last node in list */ Boolean isCirc; /* true if the list should be considered @@ -75,36 +72,34 @@ typedef struct { */ Where atEnd; /* Where in the list the last access was */ Boolean isOpen; /* true if list has been Lst_Open'ed */ - ListNode curPtr; /* current node, if open. NilListNode if + ListNode curPtr; /* current node, if open. NULL if * *just* opened */ ListNode prevPtr; /* Previous node, if open. Used by * Lst_Remove */ } *List; -#define NilList ((List)-1) - /* * PAlloc (var, ptype) -- * Allocate a pointer-typedef structure 'ptype' into the variable 'var' */ -#define PAlloc(var,ptype) var = (ptype) malloc (sizeof (*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 == NILLST) ? FALSE : TRUE) +#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) ((((LstNode)ln) == NILLNODE) ? FALSE : TRUE) +#define LstNodeValid(ln, l) ((ln) != NULL) /* * LstIsEmpty (l) -- * TRUE if the list l is empty. */ -#define LstIsEmpty(l) (((List)l)->firstPtr == NilListNode) +#define LstIsEmpty(l) (((List)(l))->firstPtr == NULL) -#endif _LSTINT_H_ +#endif /* _LSTINT_H_ */ diff --git a/usr.bin/make/lst.lib/lstIsAtEnd.c b/lst.lib/lstIsAtEnd.c similarity index 85% rename from usr.bin/make/lst.lib/lstIsAtEnd.c rename to lst.lib/lstIsAtEnd.c index 7fdf68dca49c..70270d2956c1 100644 --- a/usr.bin/make/lst.lib/lstIsAtEnd.c +++ b/lst.lib/lstIsAtEnd.c @@ -1,3 +1,5 @@ +/* $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. @@ -13,11 +15,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -34,9 +32,18 @@ * SUCH DAMAGE. */ +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstIsAtEnd.c,v 1.13 2008/02/15 21:29:50 christos Exp $"; +#else +#include #ifndef lint -static char sccsid[] = "@(#)lstIsAtEnd.c 8.2 (Berkeley) 4/28/95"; +#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 -- @@ -70,10 +77,9 @@ static char sccsid[] = "@(#)lstIsAtEnd.c 8.2 (Berkeley) 4/28/95"; *----------------------------------------------------------------------- */ Boolean -Lst_IsAtEnd (l) - Lst l; +Lst_IsAtEnd(Lst l) { - register List list = (List) l; + List list = l; return (!LstValid (l) || !list->isOpen || (list->atEnd == Head) || (list->atEnd == Tail)); diff --git a/usr.bin/make/lst.lib/lstIsEmpty.c b/lst.lib/lstIsEmpty.c similarity index 80% rename from usr.bin/make/lst.lib/lstIsEmpty.c rename to lst.lib/lstIsEmpty.c index da475611bfa8..8b1d6ed0dd91 100644 --- a/usr.bin/make/lst.lib/lstIsEmpty.c +++ b/lst.lib/lstIsEmpty.c @@ -1,3 +1,5 @@ +/* $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. @@ -13,11 +15,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -34,9 +32,18 @@ * SUCH DAMAGE. */ +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstIsEmpty.c,v 1.11 2008/12/13 15:19:29 dsl Exp $"; +#else +#include #ifndef lint -static char sccsid[] = "@(#)lstIsEmpty.c 8.2 (Berkeley) 4/28/95"; +#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 -- @@ -56,13 +63,12 @@ static char sccsid[] = "@(#)lstIsEmpty.c 8.2 (Berkeley) 4/28/95"; * Side Effects: * None. * - * A list is considered empty if its firstPtr == NilListNode (or if - * the list itself is NILLIST). + * A list is considered empty if its firstPtr == NULL (or if + * the list itself is NULL). *----------------------------------------------------------------------- */ Boolean -Lst_IsEmpty (l) - Lst l; +Lst_IsEmpty(Lst l) { return ( ! LstValid (l) || LstIsEmpty(l)); } diff --git a/usr.bin/make/lst.lib/lstLast.c b/lst.lib/lstLast.c similarity index 80% rename from usr.bin/make/lst.lib/lstLast.c rename to lst.lib/lstLast.c index a87c50c0f837..096ca24d1372 100644 --- a/usr.bin/make/lst.lib/lstLast.c +++ b/lst.lib/lstLast.c @@ -1,3 +1,5 @@ +/* $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. @@ -13,11 +15,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -34,9 +32,18 @@ * SUCH DAMAGE. */ +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstLast.c,v 1.12 2008/12/13 15:19:29 dsl Exp $"; +#else +#include #ifndef lint -static char sccsid[] = "@(#)lstLast.c 8.2 (Berkeley) 4/28/95"; +#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 -- @@ -51,7 +58,7 @@ static char sccsid[] = "@(#)lstLast.c 8.2 (Berkeley) 4/28/95"; * Return the last node on the list l. * * Results: - * The requested node or NILLNODE if the list is empty. + * The requested node or NULL if the list is empty. * * Side Effects: * None. @@ -59,13 +66,12 @@ static char sccsid[] = "@(#)lstLast.c 8.2 (Berkeley) 4/28/95"; *----------------------------------------------------------------------- */ LstNode -Lst_Last (l) - Lst l; +Lst_Last(Lst l) { if (!LstValid(l) || LstIsEmpty (l)) { - return (NILLNODE); + return NULL; } else { - return ((LstNode)((List)l)->lastPtr); + return (l->lastPtr); } } diff --git a/usr.bin/make/lst.lib/lstMember.c b/lst.lib/lstMember.c similarity index 74% rename from usr.bin/make/lst.lib/lstMember.c rename to lst.lib/lstMember.c index a6145ee64fd3..0ff2ed19d869 100644 --- a/usr.bin/make/lst.lib/lstMember.c +++ b/lst.lib/lstMember.c @@ -1,3 +1,5 @@ +/* $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. @@ -13,11 +15,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -34,9 +32,18 @@ * SUCH DAMAGE. */ +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstMember.c,v 1.13 2009/01/23 21:26:30 dsl Exp $"; +#else +#include #ifndef lint -static char sccsid[] = "@(#)lstMember.c 8.2 (Berkeley) 4/28/95"; +#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 -- @@ -46,24 +53,22 @@ static char sccsid[] = "@(#)lstMember.c 8.2 (Berkeley) 4/28/95"; #include "lstInt.h" LstNode -Lst_Member (l, d) - Lst l; - ClientData d; +Lst_Member(Lst l, void *d) { - List list = (List) l; - register ListNode lNode; + List list = l; + ListNode lNode; lNode = list->firstPtr; - if (lNode == NilListNode) { - return NILLNODE; + if (lNode == NULL) { + return NULL; } - + do { if (lNode->datum == d) { - return (LstNode)lNode; + return lNode; } lNode = lNode->nextPtr; - } while (lNode != NilListNode && lNode != list->firstPtr); + } while (lNode != NULL && lNode != list->firstPtr); - return NILLNODE; + return NULL; } diff --git a/usr.bin/make/lst.lib/lstNext.c b/lst.lib/lstNext.c similarity index 79% rename from usr.bin/make/lst.lib/lstNext.c rename to lst.lib/lstNext.c index b6986d866d59..5c2e0eece592 100644 --- a/usr.bin/make/lst.lib/lstNext.c +++ b/lst.lib/lstNext.c @@ -1,3 +1,5 @@ +/* $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. @@ -13,11 +15,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -34,9 +32,18 @@ * SUCH DAMAGE. */ +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstNext.c,v 1.12 2008/12/13 15:19:29 dsl Exp $"; +#else +#include #ifndef lint -static char sccsid[] = "@(#)lstNext.c 8.2 (Berkeley) 4/28/95"; +#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 -- @@ -56,8 +63,8 @@ static char sccsid[] = "@(#)lstNext.c 8.2 (Berkeley) 4/28/95"; * Return the next node for the given list. * * Results: - * The next node or NILLNODE if the list has yet to be opened. Also - * if the list is non-circular and the end has been reached, NILLNODE + * 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: @@ -66,20 +73,19 @@ static char sccsid[] = "@(#)lstNext.c 8.2 (Berkeley) 4/28/95"; *----------------------------------------------------------------------- */ LstNode -Lst_Next (l) - Lst l; +Lst_Next(Lst l) { - register ListNode tln; - register List list = (List)l; - + ListNode tln; + List list = l; + if ((LstValid (l) == FALSE) || (list->isOpen == FALSE)) { - return (NILLNODE); + return NULL; } - + list->prevPtr = list->curPtr; - - if (list->curPtr == NilListNode) { + + if (list->curPtr == NULL) { if (list->atEnd == Unknown) { /* * If we're just starting out, atEnd will be Unknown. @@ -89,14 +95,14 @@ Lst_Next (l) list->curPtr = tln = list->firstPtr; list->atEnd = Middle; } else { - tln = NilListNode; + tln = NULL; list->atEnd = Tail; } } else { tln = list->curPtr->nextPtr; list->curPtr = tln; - if (tln == list->firstPtr || tln == NilListNode) { + if (tln == list->firstPtr || tln == NULL) { /* * If back at the front, then we've hit the end... */ @@ -108,7 +114,7 @@ Lst_Next (l) list->atEnd = Middle; } } - - return ((LstNode)tln); + + return (tln); } diff --git a/usr.bin/make/lst.lib/lstOpen.c b/lst.lib/lstOpen.c similarity index 82% rename from usr.bin/make/lst.lib/lstOpen.c rename to lst.lib/lstOpen.c index 9da8cfab5769..941293e7abb2 100644 --- a/usr.bin/make/lst.lib/lstOpen.c +++ b/lst.lib/lstOpen.c @@ -1,3 +1,5 @@ +/* $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. @@ -13,11 +15,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -34,9 +32,18 @@ * SUCH DAMAGE. */ +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstOpen.c,v 1.12 2008/12/13 15:19:29 dsl Exp $"; +#else +#include #ifndef lint -static char sccsid[] = "@(#)lstOpen.c 8.2 (Berkeley) 4/28/95"; +#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 -- @@ -59,22 +66,21 @@ static char sccsid[] = "@(#)lstOpen.c 8.2 (Berkeley) 4/28/95"; * SUCCESS or FAILURE. * * Side Effects: - * isOpen is set TRUE and curPtr is set to NilListNode so the + * 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 (l) - register Lst l; +Lst_Open(Lst l) { if (LstValid (l) == FALSE) { return (FAILURE); } - ((List) l)->isOpen = TRUE; - ((List) l)->atEnd = LstIsEmpty (l) ? Head : Unknown; - ((List) l)->curPtr = NilListNode; + (l)->isOpen = TRUE; + (l)->atEnd = LstIsEmpty (l) ? Head : Unknown; + (l)->curPtr = NULL; return (SUCCESS); } diff --git a/usr.bin/make/dir.h b/lst.lib/lstPrev.c similarity index 56% rename from usr.bin/make/dir.h rename to lst.lib/lstPrev.c index 81d76d11504d..0ec865d51652 100644 --- a/usr.bin/make/dir.h +++ b/lst.lib/lstPrev.c @@ -1,8 +1,8 @@ +/* $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. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. @@ -15,11 +15,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -34,37 +30,50 @@ * LIABILITY, OR TORT (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 +#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. * - * @(#)dir.h 8.2 (Berkeley) 4/28/95 + * 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); + } +} -/* dir.h -- - */ - -#ifndef _DIR -#define _DIR - -typedef struct Path { - char *name; /* Name of directory */ - int refCount; /* Number of paths with this directory */ - int hits; /* the number of times a file in this - * directory has been found */ - Hash_Table files; /* Hash table of files in directory */ -} Path; - -void Dir_Init __P((void)); -void Dir_End __P((void)); -Boolean Dir_HasWildcards __P((char *)); -void Dir_Expand __P((char *, Lst, Lst)); -char *Dir_FindFile __P((char *, Lst)); -int Dir_MTime __P((GNode *)); -void Dir_AddDir __P((Lst, char *)); -char *Dir_MakeFlags __P((char *, Lst)); -void Dir_ClearPath __P((Lst)); -void Dir_Concat __P((Lst, Lst)); -void Dir_PrintDirectories __P((void)); -void Dir_PrintPath __P((Lst)); -void Dir_Destroy __P((ClientData)); -ClientData Dir_CopyDir __P((ClientData)); - -#endif /* _DIR */ diff --git a/usr.bin/make/lst.lib/lstRemove.c b/lst.lib/lstRemove.c similarity index 80% rename from usr.bin/make/lst.lib/lstRemove.c rename to lst.lib/lstRemove.c index 98eb3ce53493..54d7b33df017 100644 --- a/usr.bin/make/lst.lib/lstRemove.c +++ b/lst.lib/lstRemove.c @@ -1,3 +1,5 @@ +/* $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. @@ -13,11 +15,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -34,9 +32,18 @@ * SUCH DAMAGE. */ +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstRemove.c,v 1.14 2008/12/13 15:19:29 dsl Exp $"; +#else +#include #ifndef lint -static char sccsid[] = "@(#)lstRemove.c 8.2 (Berkeley) 4/28/95"; +#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 -- @@ -54,35 +61,33 @@ static char sccsid[] = "@(#)lstRemove.c 8.2 (Berkeley) 4/28/95"; * SUCCESS or FAILURE. * * Side Effects: - * The list's firstPtr will be set to NilListNode if ln is the last + * 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 (l, ln) - Lst l; - LstNode ln; +Lst_Remove(Lst l, LstNode ln) { - register List list = (List) l; - register ListNode lNode = (ListNode) ln; + List list = l; + ListNode lNode = ln; if (!LstValid (l) || !LstNodeValid (ln, l)) { return (FAILURE); } - + /* * unlink it from the list */ - if (lNode->nextPtr != NilListNode) { + if (lNode->nextPtr != NULL) { lNode->nextPtr->prevPtr = lNode->prevPtr; } - if (lNode->prevPtr != NilListNode) { + 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 @@ -97,12 +102,12 @@ Lst_Remove (l, ln) /* * Sequential access stuff. If the node we're removing is the current * node in the list, reset the current node to the previous one. If the - * previous one was non-existent (prevPtr == NilListNode), we set the + * 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 == NilListNode) { + if (list->curPtr == NULL) { list->atEnd = Unknown; } } @@ -113,19 +118,19 @@ Lst_Remove (l, ln) * this case). The list is, therefore, empty and is marked as such */ if (list->firstPtr == lNode) { - list->firstPtr = NilListNode; + list->firstPtr = NULL; } - + /* * note that the datum is unmolested. The caller must free it as * necessary and as expected. */ if (lNode->useCount == 0) { - free ((Address)ln); + free(ln); } else { lNode->flags |= LN_DELETED; } - + return (SUCCESS); } diff --git a/usr.bin/make/lst.lib/lstReplace.c b/lst.lib/lstReplace.c similarity index 81% rename from usr.bin/make/lst.lib/lstReplace.c rename to lst.lib/lstReplace.c index 8adb6895aec1..090e91a72c6e 100644 --- a/usr.bin/make/lst.lib/lstReplace.c +++ b/lst.lib/lstReplace.c @@ -1,3 +1,5 @@ +/* $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. @@ -13,11 +15,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -34,9 +32,18 @@ * SUCH DAMAGE. */ +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstReplace.c,v 1.13 2009/01/23 21:26:30 dsl Exp $"; +#else +#include #ifndef lint -static char sccsid[] = "@(#)lstReplace.c 8.2 (Berkeley) 4/28/95"; +#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 -- @@ -59,14 +66,12 @@ static char sccsid[] = "@(#)lstReplace.c 8.2 (Berkeley) 4/28/95"; *----------------------------------------------------------------------- */ ReturnStatus -Lst_Replace (ln, d) - register LstNode ln; - ClientData d; +Lst_Replace(LstNode ln, void *d) { - if (ln == NILLNODE) { + if (ln == NULL) { return (FAILURE); } else { - ((ListNode) ln)->datum = d; + (ln)->datum = d; return (SUCCESS); } } diff --git a/usr.bin/make/lst.lib/lstSucc.c b/lst.lib/lstSucc.c similarity index 79% rename from usr.bin/make/lst.lib/lstSucc.c rename to lst.lib/lstSucc.c index a5875f686874..3f13aa5e7f66 100644 --- a/usr.bin/make/lst.lib/lstSucc.c +++ b/lst.lib/lstSucc.c @@ -1,3 +1,5 @@ +/* $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. @@ -13,11 +15,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -34,9 +32,18 @@ * SUCH DAMAGE. */ +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstSucc.c,v 1.13 2008/12/13 15:19:29 dsl Exp $"; +#else +#include #ifndef lint -static char sccsid[] = "@(#)lstSucc.c 8.2 (Berkeley) 4/28/95"; +#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 -- @@ -48,7 +55,7 @@ static char sccsid[] = "@(#)lstSucc.c 8.2 (Berkeley) 4/28/95"; /*- *----------------------------------------------------------------------- * Lst_Succ -- - * Return the sucessor to the given node on its list. + * Return the successor to the given node on its list. * * Results: * The successor of the node, if it exists (note that on a circular @@ -61,13 +68,12 @@ static char sccsid[] = "@(#)lstSucc.c 8.2 (Berkeley) 4/28/95"; *----------------------------------------------------------------------- */ LstNode -Lst_Succ (ln) - LstNode ln; +Lst_Succ(LstNode ln) { - if (ln == NILLNODE) { - return (NILLNODE); + if (ln == NULL) { + return NULL; } else { - return ((LstNode) ((ListNode) ln)->nextPtr); + return (ln->nextPtr); } } diff --git a/machine.sh b/machine.sh new file mode 100755 index 000000000000..32a0f7a1d84d --- /dev/null +++ b/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/main.c b/main.c new file mode 100644 index 000000000000..aab8e4ba3e8e --- /dev/null +++ b/main.c @@ -0,0 +1,2078 @@ +/* $NetBSD: main.c,v 1.199 2012/04/24 20:35: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: main.c,v 1.199 2012/04/24 20:35:04 sjg Exp $"; +#else +#include +#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.199 2012/04/24 20:35:04 sjg Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * main.c -- + * The main file for this entire program. Exit routines etc + * reside here. + * + * Utility functions defined in this file: + * Main_ParseArgLine Takes a line of arguments, breaks them and + * treats them as if they were given when first + * invoked. Used by the parse module to implement + * the .MFLAGS target. + * + * Error Print a tagged error message. The global + * MAKE variable must have been defined. This + * takes a format string and two optional + * arguments for it. + * + * Fatal Print an error message and exit. Also takes + * a format string and two arguments. + * + * Punt Aborts all jobs and exits with a message. Also + * takes a format string and two arguments. + * + * Finish Finish things up by printing the number of + * errors which occurred, as passed to it, and + * exiting. + */ + +#include +#include +#include +#include +#include +#include +#ifdef MAKE_NATIVE +#include +#endif +#include "wait.h" + +#include +#include +#include +#include +#include +#include + +#include "make.h" +#include "hash.h" +#include "dir.h" +#include "job.h" +#include "pathnames.h" +#include "trace.h" + +#ifdef USE_IOVEC +#include +#endif + +#ifndef DEFMAXLOCAL +#define DEFMAXLOCAL DEFMAXJOBS +#endif /* DEFMAXLOCAL */ + +Lst create; /* Targets to be made */ +time_t now; /* Time at start of make */ +GNode *DEFAULT; /* .DEFAULT node */ +Boolean allPrecious; /* .PRECIOUS given on line by itself */ + +static Boolean noBuiltins; /* -r flag */ +static Lst makefiles; /* ordered list of makefiles to read */ +static Boolean printVars; /* print value of one or more vars */ +static Lst variables; /* list of variables to print */ +int maxJobs; /* -j argument */ +static int 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) __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] == ':') { + /* - found, and 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 __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. + * ::... + */ + 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 __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/make-bootstrap.sh.in b/make-bootstrap.sh.in new file mode 100755 index 000000000000..d9ff9ff961a0 --- /dev/null +++ b/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/usr.bin/make/config.h b/make-conf.h similarity index 54% rename from usr.bin/make/config.h rename to make-conf.h index 1f70f3768aa3..a85b86d3efb5 100644 --- a/usr.bin/make/config.h +++ b/make-conf.h @@ -1,6 +1,40 @@ +/* $NetBSD: config.h,v 1.21 2012/03/31 00:12:24 christos Exp $ */ + /* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. + * 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. * @@ -35,11 +69,9 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)config.h 8.2 (Berkeley) 4/28/95 + * from: @(#)config.h 8.1 (Berkeley) 6/6/93 */ -#define DEFSHELL 1 /* Bourne shell */ - /* * DEFMAXJOBS * DEFMAXLOCAL @@ -78,15 +110,53 @@ * 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. - * - * POSIX - * If the POSIX standard for Make is to be followed. There are - * several areas that I dislike, hence this constant. */ #define LIBSUFF ".a" #define RECHECK -#ifndef RANLIBMAG -#define RANLIBMAG "__.SYMDEF" +/* + * 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 =] + */ +#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 -/*#define POSIX*/ diff --git a/make.1 b/make.1 new file mode 100644 index 000000000000..86f747b9177c --- /dev/null +++ b/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/make.c b/make.c new file mode 100644 index 000000000000..b51f400f3fac --- /dev/null +++ b/make.c @@ -0,0 +1,1561 @@ +/* $NetBSD: make.c,v 1.86 2012/05/10 19:53:26 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. + */ + +/* + * 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.86 2012/05/10 19:53:26 christos Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)make.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: make.c,v 1.86 2012/05/10 19:53:26 christos 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 *); + +__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 __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 __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/usr.bin/make/make.h b/make.h similarity index 55% rename from usr.bin/make/make.h rename to make.h index 3af67f7e4157..d67d182d5f3c 100644 --- a/usr.bin/make/make.h +++ b/make.h @@ -1,6 +1,40 @@ +/* $NetBSD: make.h,v 1.88 2012/06/04 20:34:20 sjg Exp $ */ + /* * Copyright (c) 1988, 1989, 1990, 1993 * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)make.h 8.3 (Berkeley) 6/13/95 + */ + +/* * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * @@ -35,7 +69,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)make.h 8.3 (Berkeley) 6/13/95 + * from: @(#)make.h 8.3 (Berkeley) 6/13/95 */ /*- @@ -46,27 +80,63 @@ #ifndef _MAKE_H_ #define _MAKE_H_ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + #include -#include -#include +#include + #include -#ifndef MAKE_BOOTSTRAP -#include -#else -#if defined(__STDC__) || defined(__cplusplus) -#define __P(protos) protos /* full-blown ANSI C */ -#else -#define __P(protos) () /* traditional C preprocessor */ -#endif -#endif -#if __STDC__ +#include #include -#include +#ifdef HAVE_STRING_H +#include +#else +#include #endif +#include +#include + +#if !defined(__GNUC_PREREQ__) +#if defined(__GNUC__) +#define __GNUC_PREREQ__(x, y) \ + ((__GNUC__ == (x) && __GNUC_MINOR__ >= (y)) || \ + (__GNUC__ > (x))) +#else /* defined(__GNUC__) */ +#define __GNUC_PREREQ__(x, y) 0 +#endif /* defined(__GNUC__) */ +#endif /* !defined(__GNUC_PREREQ__) */ + +#if !defined(__unused) +#if __GNUC_PREREQ__(2, 7) +#define __unused __attribute__((__unused__)) +#else +#define __unused /* delete */ +#endif +#endif + +#if !defined(__dead) +#define __dead +#endif + #include "sprite.h" #include "lst.h" -#include "config.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 @@ -80,80 +150,90 @@ * 7) the number of its children that are, as yet, unmade * 8) its modification time * 9) the modification time of its youngest child (qv. make.c) - * 10) a list of nodes for which this is a source - * 11) a list of nodes on which this depends + * 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. - * 13) a list of nodes of the same name created by the :: operator - * 14) a list of nodes that must be made (if they're made) before - * this node can be, but that do no enter into the datedness of + * 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. - * 15) a list of nodes that must be made (if they're made) after + * 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. - * 16) a Lst of ``local'' variables that are specific to this target + * 18) a Lst of ``local'' variables that are specific to this target * and this target only (qv. var.c [$@ $< $?, etc.]) - * 17) a Lst of strings that are commands to be given to a shell - * to create this target. + * 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) */ - Boolean make; /* TRUE if this target needs to be remade */ - enum { - UNMADE, BEINGMADE, MADE, UPTODATE, ERROR, ABORTED, - CYCLE, ENDCYCLE, + 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. (compat - * mode only) + * Indicates a cycle in the graph. * MADE - Was out-of-date and has been made * UPTODATE - Was already up-to-date - * ERROR - An error occured while it was being + * ERROR - An error occurred while it was being * made (used only in compat mode) * ABORTED - The target was aborted due to * an error making an inferior (compat). - * CYCLE - Marked as potentially being part of - * a graph cycle. If we come back to a - * node marked this way, it is printed - * and 'made' is changed to ENDCYCLE. - * ENDCYCLE - the cycle has been completely - * printed. Go back and unmark all its - * members. */ - Boolean childMade; /* TRUE if one of this target's children was - * made */ int unmade; /* The number of unmade children */ - int mtime; /* Its modification time */ - int cmtime; /* The modification time of its youngest - * child */ + 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 successors; /* Nodes that must be made after this one */ - Lst preds; /* Nodes that must be made before this one */ + Lst order_pred; /* .ORDER nodes we need made */ + Lst order_succ; /* .ORDER nodes who need us */ - Lst context; /* The local variables */ + 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; -/* - * Manifest constants - */ -#define NILGNODE ((GNode *) NIL) - /* * The OP_ constants are used when parsing a dependency line as a way of * communicating to other parts of the program the way in which a target @@ -161,7 +241,7 @@ typedef struct GNode { * 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... + * righthand side... */ #define OP_DEPENDS 0x00000001 /* Execution of commands depends on * kids (:) */ @@ -181,18 +261,27 @@ typedef struct GNode { #define OP_PRECIOUS 0x00000080 /* Don't remove the target when * interrupted */ #define OP_SILENT 0x00000100 /* Don't echo commands when executed */ -#define OP_MAKE 0x00000200 /* Target is a recurrsive make so its +#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 */ @@ -203,34 +292,28 @@ typedef struct GNode { * commands for a target */ #define OP_SAVE_CMDS 0x04000000 /* Saving commands on .END (Compat) */ #define OP_DEPS_FOUND 0x02000000 /* Already processed by Suff_FindDeps */ +#define OP_MARK 0x01000000 /* Node found while expanding .ALLSRC */ +#define NoExecute(gn) ((gn->type & OP_MAKE) ? noRecursiveExecute : noExecute) /* * OP_NOP will return TRUE if the node with the given type was not the * object of a dependency operator */ #define OP_NOP(t) (((t) & OP_OPMASK) == 0x00000000) +#define OP_NOTARGET (OP_NOTMAIN|OP_USE|OP_EXEC|OP_TRANSFORM) + /* * The TARG_ constants are used when calling the Targ_FindNode and * Targ_FindList functions in targ.c. They simply tell the functions what to * do if the desired node(s) is (are) not found. If the TARG_CREATE constant * is given, a new, empty node will be created for the target, placed in the * table of all targets and its address returned. If TARG_NOCREATE is given, - * a NIL pointer will be returned. + * a NULL pointer will be returned. */ -#define TARG_CREATE 0x01 /* create node if not found */ #define TARG_NOCREATE 0x00 /* don't create it */ - -/* - * There are several places where expandable buffers are used (parse.c and - * var.c). This constant is merely the starting point for those buffers. If - * lines tend to be much shorter than this, it would be best to reduce BSIZE. - * If longer, it should be increased. Reducing it will cause more copying to - * be done for longer lines, but will save space for shorter ones. In any - * case, it ought to be a power of two simply because most storage allocation - * schemes allocate in powers of two. - */ -#define MAKE_BSIZE 256 /* starting size for expandable buffers */ +#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 @@ -239,11 +322,10 @@ typedef struct GNode { * 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. + * Str_Concat returns. */ #define STR_ADDSPACE 0x01 /* add a space when Str_Concat'ing */ -#define STR_DOFREE 0x02 /* free source strings after concatenation */ -#define STR_ADDSLASH 0x04 /* add a slash when Str_Concat'ing */ +#define STR_ADDSLASH 0x02 /* add a slash when Str_Concat'ing */ /* * Error levels for parsing. PARSE_FATAL means the process cannot continue @@ -279,7 +361,7 @@ typedef struct GNode { #define DPREFIX "*D" /* directory part of PREFIX */ /* - * Global Variables + * Global Variables */ extern Lst create; /* The list of target names specified on the * command line. used to resolve #if @@ -291,28 +373,34 @@ 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 usePipes; /* TRUE if should capture the output of - * subshells by means of pipes. Otherwise it - * is routed to temporary files from which it - * is retrieved when the shell exits */ 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 @@ -323,44 +411,101 @@ extern time_t now; /* The time at the start of this whole 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 0x0001 -#define DEBUG_COND 0x0002 -#define DEBUG_DIR 0x0004 -#define DEBUG_GRAPH1 0x0008 -#define DEBUG_GRAPH2 0x0010 -#define DEBUG_JOB 0x0020 -#define DEBUG_MAKE 0x0040 -#define DEBUG_SUFF 0x0080 -#define DEBUG_TARG 0x0100 -#define DEBUG_VAR 0x0200 -#define DEBUG_FOR 0x0400 +#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 -#ifdef __STDC__ #define CONCAT(a,b) a##b -#else -#define I(a) a -#define CONCAT(a,b) I(a)b -#endif /* __STDC__ */ #define DEBUG(module) (debug & CONCAT(DEBUG_,module)) -/* - * Since there are so many, all functions that return non-integer values are - * extracted by means of a sed script or two and stuck in the file "nonints.h" - */ #include "nonints.h" -int Make_TimeStamp __P((GNode *, GNode *)); -Boolean Make_OODate __P((GNode *)); -int Make_HandleUse __P((GNode *, GNode *)); -void Make_Update __P((GNode *)); -void Make_DoAllVar __P((GNode *)); -Boolean Make_Run __P((Lst)); +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/make_malloc.c b/make_malloc.c new file mode 100644 index 000000000000..87f6840979e1 --- /dev/null +++ b/make_malloc.c @@ -0,0 +1,119 @@ +/* $NetBSD: make_malloc.c,v 1.7 2012/05/18 02:28:16 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 +__RCSID("$NetBSD: make_malloc.c,v 1.7 2012/05/18 02:28:16 sjg Exp $"); +#endif + +#include +#include +#include +#include + +#include "make_malloc.h" + +#ifndef USE_EMALLOC +/* + * enomem -- + * die when out of memory. + */ +extern char *progname; + +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/make_malloc.h b/make_malloc.h new file mode 100644 index 000000000000..36d3eff3c027 --- /dev/null +++ b/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 +#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/meta.c b/meta.c new file mode 100644 index 000000000000..2152dbb9cb6e --- /dev/null +++ b/meta.c @@ -0,0 +1,1346 @@ +/* $NetBSD: meta.c,v 1.24 2011/09/21 14:30:47 christos 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 +#include +#include +#include +#include +#if !defined(HAVE_CONFIG_H) || defined(HAVE_ERR_H) +#include +#endif + +#include "make.h" +#include "job.h" + +#ifdef HAVE_FILEMON_H +# include +#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; + +#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) +{ + extern char **environ; + 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: + * + * + * + * Where: + * is a single letter, denoting the syscall. + * is the process that made the syscall. + * 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/meta.h b/meta.h new file mode 100644 index 000000000000..1ce01ca901cb --- /dev/null +++ b/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/mkdeps.sh b/mkdeps.sh new file mode 100755 index 000000000000..dd87c4f5020e --- /dev/null +++ b/mkdeps.sh @@ -0,0 +1,314 @@ +: +# NAME: +# mkdeps - generate dependencies +# +# SYNOPSIS: +# mkdeps [options] file ... +# +# DESCRIPTION: +# This script updates "makefile" with dependencies for +# "file"(s). It borrows ideas from various makedepend scripts +# and should be compatible with most. +# +# By default we use grep to extract include file names from +# source files. We source an "rc" file '$Mydir/.${Myname}rc' which +# can contain variable assignments such as: +#.nf +# +# cpp_c=/usr/lib/cpp +# cpp_cc=g++ -E +# ... +# +#.fi +# If the variable 'cpp_$suffix' is set, we use it as our cpp in +# place of grep. The program referenced by these variables are +# expected to produce output like: +#.nf +# +# # 10 \"/usr/include/stdio.h\" 1 +# +#.fi +# This allows us to skip most of our processing. For lex,yacc +# and other source files, grep is probably just as quick and +# certainly more portable. +# +# If the "rc" file does not exist, we create it and attempt to +# find cpp or an equivalent cc invocation to assign to 'cpp_c'. +# +# AUTHOR: +# Simon J. Gerraty +# + +# RCSid: +# $Id: mkdeps.sh,v 1.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 " > /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/nonints.h b/nonints.h new file mode 100644 index 000000000000..616de8fe6464 --- /dev/null +++ b/nonints.h @@ -0,0 +1,206 @@ +/* $NetBSD: nonints.h,v 1.63 2011/09/16 15:38:04 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 + */ + +#ifndef MAKE_NATIVE +#undef __attribute__ +#define __attribute__(x) +#endif + +/* arch.c */ +ReturnStatus Arch_ParseArchive(char **, Lst, GNode *); +void Arch_Touch(GNode *); +void Arch_TouchLib(GNode *); +time_t Arch_MTime(GNode *); +time_t Arch_MemMTime(GNode *); +void Arch_FindLib(GNode *, Lst); +Boolean Arch_LibOODate(GNode *); +void Arch_Init(void); +void Arch_End(void); +int Arch_IsLib(GNode *); + +/* compat.c */ +int CompatRunCommand(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 *, ...) __attribute__((__format__(__printf__, 1, 2))); +void Fatal(const char *, ...) + __attribute__((__format__(__printf__, 1, 2),__noreturn__)); +void Punt(const char *, ...) + __attribute__((__format__(__printf__, 1, 2),__noreturn__)); +void DieHorribly(void) __attribute__((__noreturn__)); +int PrintAddr(void *, void *); +void Finish(int) __dead; +int eunlink(const char *); +void execError(const char *, const char *); +char *getTmpdir(void); + +/* parse.c */ +void Parse_Error(int, const char *, ...) + __attribute__((__format__(__printf__, 2, 3))); +Boolean Parse_AnyExport(void); +Boolean Parse_IsVar(char *); +void Parse_DoVar(char *, GNode *); +void Parse_AddIncludeDir(char *); +void Parse_File(const char *, 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/os.sh b/os.sh new file mode 100755 index 000000000000..9e45f3792f9e --- /dev/null +++ b/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 + +# 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/parse.c b/parse.c new file mode 100644 index 000000000000..8639371634f3 --- /dev/null +++ b/parse.c @@ -0,0 +1,3122 @@ +/* $NetBSD: parse.c,v 1.184 2012/04/24 20:12:16 sjg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: parse.c,v 1.184 2012/04/24 20:12:16 sjg Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)parse.c 8.3 (Berkeley) 3/19/94"; +#else +__RCSID("$NetBSD: parse.c,v 1.184 2012/04/24 20:12:16 sjg Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * parse.c -- + * Functions to parse a makefile. + * + * One function, Parse_Init, must be called before any functions + * in this module are used. After that, the function Parse_File is the + * main entry point and controls most of the other functions in this + * module. + * + * Most important structures are kept in Lsts. Directories for + * the .include "..." function are kept in the 'parseIncPath' Lst, while + * those for the .include <...> are kept in the 'sysIncPath' Lst. The + * targets currently being defined are kept in the 'targets' Lst. + * + * The variables 'fname' and 'lineno' are used to track the name + * of the current file and the line number in that file so that error + * messages can be more meaningful. + * + * Interface: + * Parse_Init Initialization function which must be + * called before anything else in this module + * is used. + * + * Parse_End Cleanup the module + * + * Parse_File Function used to parse a makefile. It must + * be given the name of the file, which should + * already have been opened, and a function + * to call to read a character from the file. + * + * Parse_IsVar Returns TRUE if the given line is a + * variable assignment. Used by MainParseArgs + * to determine if an argument is a target + * or a variable assignment. Used internally + * for pretty much the same thing... + * + * Parse_Error Function called when an error occurs in + * parsing. Used by the variable and + * conditional modules. + * Parse_MainName Returns a Lst of the main target to create. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "make.h" +#include "hash.h" +#include "dir.h" +#include "job.h" +#include "buf.h" +#include "pathnames.h" + +#ifdef HAVE_MMAP +#include + +#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 *, ...) + __attribute__((__format__(__printf__, 4, 5))); +static void ParseVErrorInternal(FILE *, const char *, size_t, int, const char *, va_list) + __attribute__((__format__(__printf__, 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 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 = + * + * 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/usr.bin/make/pathnames.h b/pathnames.h similarity index 66% rename from usr.bin/make/pathnames.h rename to pathnames.h index 8cb066148393..9c597b1e6758 100644 --- a/usr.bin/make/pathnames.h +++ b/pathnames.h @@ -1,3 +1,5 @@ +/* $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. @@ -10,11 +12,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,10 +28,35 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)pathnames.h 8.2 (Berkeley) 4/28/95 + * 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 +#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" +#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/ranlib.h b/ranlib.h new file mode 100644 index 000000000000..503c7c169763 --- /dev/null +++ b/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 "!\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/realpath.c b/realpath.c new file mode 100644 index 000000000000..1ef2cd878c2a --- /dev/null +++ b/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 +#endif +#ifndef HAVE_REALPATH + +#include +#include +#include + +#include +#ifdef HAVE_STDLIB_H +# include +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#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/setenv.c b/setenv.c new file mode 100644 index 000000000000..efd0f74f308f --- /dev/null +++ b/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 +#include +#include + +/* + * __findenv -- + * Returns pointer to value associated with name, if any, else NULL. + * Sets offset to be the offset of the name/value combination in the + * environmental array, for use by setenv(3) and unsetenv(3). + * Explicitly removes '=' in argument name. + * + * This routine *should* be a static; don't use it. + */ +static char * +__findenv(name, offset) + register char *name; + int *offset; +{ + extern char **environ; + register int len; + register char **P, *C; + + for (C = name, len = 0; *C && *C != '='; ++C, ++len); + for (P = environ; *P; ++P) + if (!strncmp(*P, name, len)) + if (*(C = *P + len) == '=') { + *offset = P - environ; + return(++C); + } + return(NULL); +} + +/* + * setenv -- + * Set the value of the environmental variable "name" to be + * "value". If rewrite is set, replace any current value. + */ +setenv(name, value, rewrite) + register const char *name; + register const char *value; + int rewrite; +{ + extern char **environ; + static int alloced; /* if allocated space before */ + register char *C; + int l_value, offset; + char *__findenv(); + + if (*value == '=') /* no `=' in value */ + ++value; + l_value = strlen(value); + if ((C = __findenv(name, &offset))) { /* find if already exists */ + if (!rewrite) + return (0); + if (strlen(C) >= l_value) { /* old larger; copy over */ + while (*C++ = *value++); + return (0); + } + } else { /* create new slot */ + register int cnt; + register char **P; + + for (P = environ, cnt = 0; *P; ++P, ++cnt); + if (alloced) { /* just increase size */ + environ = (char **)realloc((char *)environ, + (size_t)(sizeof(char *) * (cnt + 2))); + if (!environ) + return (-1); + } + else { /* get new space */ + alloced = 1; /* copy old entries into it */ + P = (char **)malloc((size_t)(sizeof(char *) * + (cnt + 2))); + if (!P) + return (-1); + bcopy(environ, P, cnt * sizeof(char *)); + environ = P; + } + environ[cnt + 1] = NULL; + offset = cnt; + } + for (C = (char *)name; *C && *C != '='; ++C); /* no `=' in name */ + if (!(environ[offset] = /* name + `=' + value */ + malloc((size_t)((int)(C - name) + l_value + 2)))) + return (-1); + for (C = environ[offset]; (*C = *name++) && *C != '='; ++C) + ; + for (*C++ = '='; *C++ = *value++; ) + ; + return (0); +} + +/* + * unsetenv(name) -- + * Delete environmental variable "name". + */ +void +unsetenv(name) + const char *name; +{ + extern char **environ; + register char **P; + int offset; + char *__findenv(); + + while (__findenv(name, &offset)) /* if set multiple times */ + for (P = &environ[offset];; ++P) + if (!(*P = *(P + 1))) + break; +} +#endif diff --git a/sigcompat.c b/sigcompat.c new file mode 100644 index 000000000000..608538d77d0f --- /dev/null +++ b/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 + */ +/* + * @(#)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 +#ifdef HAVE_UNISTD_H +#include +#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 +#include +#include +#include +#include "assert.h" + +#ifndef ASSERT +# define ASSERT assert +#endif + +#ifdef NDEBUG +# define _DBUG(x) +#else +# define _DBUG(x) x +#endif + +#ifndef SA_RESTART +# define SA_RESTART 2 +#endif +#ifndef SV_INTERRUPT +# define SV_INTERRUPT SA_RESTART +#endif + +#ifndef MASK_T +# 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/usr.bin/make/sprite.h b/sprite.h similarity index 60% rename from usr.bin/make/sprite.h rename to sprite.h index 0da22f180d05..6ec4fe2e4d09 100644 --- a/usr.bin/make/sprite.h +++ b/sprite.h @@ -1,6 +1,40 @@ +/* $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. * @@ -35,7 +69,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)sprite.h 8.2 (Berkeley) 4/28/95 + * from: @(#)sprite.h 8.1 (Berkeley) 6/6/93 */ /* @@ -56,10 +90,10 @@ typedef int Boolean; #ifndef TRUE #define TRUE 1 -#endif TRUE +#endif /* TRUE */ #ifndef FALSE #define FALSE 0 -#endif FALSE +#endif /* FALSE */ /* * Functions that must return a status can return a ReturnStatus to @@ -69,7 +103,7 @@ typedef int Boolean; typedef int ReturnStatus; /* - * The following statuses overlap with the first 2 generic statuses + * The following statuses overlap with the first 2 generic statuses * defined in status.h: * * SUCCESS There was no error. @@ -79,33 +113,4 @@ typedef int ReturnStatus; #define SUCCESS 0x00000000 #define FAILURE 0x00000001 - -/* - * A nil pointer must be something that will cause an exception if - * referenced. There are two nils: the kernels nil and the nil used - * by user processes. - */ - -#define NIL ~0 -#define USER_NIL 0 -#ifndef NULL -#define NULL 0 -#endif NULL - -/* - * An address is just a pointer in C. It is defined as a character pointer - * so that address arithmetic will work properly, a byte at a time. - */ - -typedef char *Address; - -/* - * ClientData is an uninterpreted word. It is defined as an int so that - * kdbx will not interpret client data as a string. Unlike an "Address", - * client data will generally not be used in arithmetic. - * But we don't have kdbx anymore so we define it as void (christos) - */ - -typedef void *ClientData; - #endif /* _SPRITE */ diff --git a/usr.bin/make/str.c b/str.c similarity index 68% rename from usr.bin/make/str.c rename to str.c index 7df8e8d2f113..bc324b8c9c20 100644 --- a/usr.bin/make/str.c +++ b/str.c @@ -1,6 +1,38 @@ +/* $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. * @@ -36,46 +68,21 @@ * SUCH DAMAGE. */ +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: str.c,v 1.34 2012/03/03 23:16:47 dholland Exp $"; +#else +#include #ifndef lint -static char sccsid[] = "@(#)str.c 8.6 (Berkeley) 4/28/95"; -#endif /* not 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" -static char **argv, *buffer; -static int argmax, curlen; - -/* - * str_init -- - * Initialize the strings package - * - */ -void -str_init() -{ - char *p1; - argv = (char **)emalloc((argmax = 50) * sizeof(char *)); - argv[0] = Var_Value(".MAKE", VAR_GLOBAL, &p1); -} - - -/* - * str_end -- - * Cleanup the strings package - * - */ -void -str_end() -{ - if (argv[0]) { - free(argv[0]); - free((Address) argv); - } - if (buffer) - free(buffer); -} - - /*- * str_concat -- * concatenate the two strings, inserting a space or slash between them, @@ -85,19 +92,17 @@ str_end() * the resulting string in allocated space. */ char * -str_concat(s1, s2, flags) - char *s1, *s2; - int flags; +str_concat(const char *s1, const char *s2, int flags) { - register int len1, len2; - register char *result; + int len1, len2; + char *result; /* get the length of both strings */ len1 = strlen(s1); len2 = strlen(s2); /* allocate length plus separator plus EOS */ - result = emalloc((u_int)(len1 + len2 + 2)); + result = bmake_malloc((u_int)(len1 + len2 + 2)); /* copy first string into place */ memcpy(result, s1, len1); @@ -114,11 +119,6 @@ str_concat(s1, s2, flags) /* copy second string plus EOS into place */ memcpy(result + len1, s2, len2 + 1); - /* free original strings */ - if (flags & STR_DOFREE) { - (void)free(s1); - (void)free(s2); - } return(result); } @@ -128,51 +128,60 @@ str_concat(s1, s2, flags) * 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. To make life easier, - * the first word is always the value of the .MAKE variable. + * 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(str, store_argc, expand) - register char *str; - int *store_argc; - Boolean expand; +brk_string(const char *str, int *store_argc, Boolean expand, char **buffer) { - register int argc, ch; - register char inquote, *p, *start, *t; + 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) { - if (buffer) - free(buffer); - buffer = emalloc(curlen = len); - } + 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 = 1; + argc = 0; inquote = '\0'; - for (p = str, start = t = buffer;; ++p) { + for (p = str, start = t = *buffer;; ++p) { switch(ch = *p) { case '"': case '\'': - if (inquote) + if (inquote) { if (inquote == ch) inquote = '\0'; else break; + } else { inquote = (char) ch; /* Don't miss "" or '' */ if (start == NULL && p[1] == inquote) { - start = t + 1; + if (!expand) { + start = t; + *t++ = ch; + } else + start = t + 1; + p++; + inquote = '\0'; break; } } @@ -201,24 +210,32 @@ brk_string(str, store_argc, expand) *t++ = '\0'; if (argc == argmax) { argmax *= 2; /* ramp up fast */ - if (!(argv = (char **)realloc(argv, - argmax * sizeof(char *)))) - enomem(); + argv = (char **)bmake_realloc(argv, + (argmax + 1) * sizeof(char *)); } argv[argc++] = start; - start = (char *)NULL; - if (ch == '\n' || ch == '\0') + 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': @@ -248,27 +265,29 @@ brk_string(str, store_argc, expand) start = t; *t++ = (char) ch; } -done: argv[argc] = (char *)NULL; +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(string, substring) - register char *string; /* String to search. */ - char *substring; /* Substring to find in string */ +Str_FindSubstring(const char *string, const char *substring) { - register char *a, *b; + const char *a, *b; /* * First scan quickly through the two strings looking for a single- @@ -282,30 +301,30 @@ Str_FindSubstring(string, substring) a = string; for (;;) { if (*b == 0) - return(string); + return UNCONST(string); if (*a++ != *b++) break; } b = substring; } - return((char *) NULL); + 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(string, pattern) - register char *string; /* String */ - register char *pattern; /* Pattern */ +Str_Match(const char *string, const char *pattern) { char c2; @@ -396,8 +415,13 @@ thisCharOK: ++pattern; /*- *----------------------------------------------------------------------- * Str_SYSVMatch -- - * Check word against pattern for a match (% is wild), - * + * 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. @@ -408,19 +432,16 @@ thisCharOK: ++pattern; *----------------------------------------------------------------------- */ char * -Str_SYSVMatch(word, pattern, len) - char *word; /* Word to examine */ - char *pattern; /* Pattern to examine against */ - int *len; /* Number of characters to substitute */ +Str_SYSVMatch(const char *word, const char *pattern, int *len) { - char *p = pattern; - char *w = word; - char *m; + const char *p = pattern; + const char *w = word; + const char *m; if (*p == '\0') { /* Null pattern is the whole string */ *len = strlen(w); - return w; + return UNCONST(w); } if ((m = strchr(p, '%')) != NULL) { @@ -434,7 +455,7 @@ Str_SYSVMatch(word, pattern, len) if (*++p == '\0') { /* No more pattern, return the rest of the string */ *len = strlen(w); - return w; + return UNCONST(w); } } @@ -444,10 +465,10 @@ Str_SYSVMatch(word, pattern, len) do if (strcmp(p, w) == 0) { *len = w - m; - return m; + return UNCONST(m); } while (*w++ != '\0'); - + return NULL; } @@ -458,7 +479,7 @@ Str_SYSVMatch(word, pattern, len) * Substitute '%' on the pattern with len characters from src. * If the pattern does not contain a '%' prepend len characters * from src. - * + * * Results: * None * @@ -468,24 +489,20 @@ Str_SYSVMatch(word, pattern, len) *----------------------------------------------------------------------- */ void -Str_SYSVSubst(buf, pat, src, len) - Buffer buf; - char *pat; - char *src; - int len; +Str_SYSVSubst(Buffer *buf, char *pat, char *src, int len) { char *m; if ((m = strchr(pat, '%')) != NULL) { /* Copy the prefix */ - Buf_AddBytes(buf, m - pat, (Byte *) pat); + Buf_AddBytes(buf, m - pat, pat); /* skip the % */ pat = m + 1; } /* Copy the pattern */ - Buf_AddBytes(buf, len, (Byte *) src); + Buf_AddBytes(buf, len, src); /* append the rest */ - Buf_AddBytes(buf, strlen(pat), (Byte *) pat); + Buf_AddBytes(buf, strlen(pat), pat); } diff --git a/stresep.c b/stresep.c new file mode 100644 index 000000000000..b1b5714ca88d --- /dev/null +++ b/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 +#endif + +#include + +#include +#include + +#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/strlcpy.c b/strlcpy.c new file mode 100644 index 000000000000..b59b2f4ba5a6 --- /dev/null +++ b/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 + * + * 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 +#endif +#ifndef HAVE_STRLCPY + +#include + +#include +#include + +/* + * 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/strlist.c b/strlist.c new file mode 100644 index 000000000000..3fb2f7dbb6c8 --- /dev/null +++ b/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 +#ifndef lint +__RCSID("$NetBSD: strlist.c,v 1.4 2009/01/24 11:59:39 dsl Exp $"); +#endif /* not lint */ +#endif + +#include +#include +#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/strlist.h b/strlist.h new file mode 100644 index 000000000000..2fc049e8643a --- /dev/null +++ b/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/usr.bin/make/suff.c b/suff.c similarity index 59% rename from usr.bin/make/suff.c rename to suff.c index 1b344d499490..6abdeb0459e5 100644 --- a/usr.bin/make/suff.c +++ b/suff.c @@ -1,6 +1,38 @@ +/* $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. * @@ -36,9 +68,18 @@ * SUCH DAMAGE. */ +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: suff.c,v 1.69 2011/09/29 23:38:04 sjg Exp $"; +#else +#include #ifndef lint -static char sccsid[] = "@(#)suff.c 8.5 (Berkeley) 4/28/95"; +#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 -- @@ -87,18 +128,22 @@ static char sccsid[] = "@(#)suff.c 8.5 (Berkeley) 4/28/95"; * * Suff_FindDeps Find implicit sources for and the location of * a target based on its suffix. Returns the - * bottom-most node added to the graph or NILGNODE + * 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 #include "make.h" #include "hash.h" #include "dir.h" -#include "bit.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 */ @@ -123,6 +168,14 @@ typedef struct _Suff { 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. */ @@ -148,35 +201,44 @@ typedef struct { Src *s; } LstSrc; +typedef struct { + GNode **gn; + Suff *s; + Boolean r; +} GNodeSuff; + static Suff *suffNull; /* The NULL suffix for this run */ static Suff *emptySuff; /* The empty suffix required for POSIX * single-suffix transformation rules */ -static char *SuffStrIsPrefix __P((char *, char *)); -static char *SuffSuffIsSuffix __P((Suff *, char *)); -static int SuffSuffIsSuffixP __P((ClientData, ClientData)); -static int SuffSuffHasNameP __P((ClientData, ClientData)); -static int SuffSuffIsPrefix __P((ClientData, ClientData)); -static int SuffGNHasNameP __P((ClientData, ClientData)); -static void SuffFree __P((ClientData)); -static void SuffInsert __P((Lst, Suff *)); -static void SuffRemove __P((Lst, Suff *)); -static Boolean SuffParseTransform __P((char *, Suff **, Suff **)); -static int SuffRebuildGraph __P((ClientData, ClientData)); -static int SuffAddSrc __P((ClientData, ClientData)); -static int SuffRemoveSrc __P((Lst)); -static void SuffAddLevel __P((Lst, Src *)); -static Src *SuffFindThem __P((Lst, Lst)); -static Src *SuffFindCmds __P((Src *, Lst)); -static int SuffExpandChildren __P((ClientData, ClientData)); -static Boolean SuffApplyTransform __P((GNode *, GNode *, Suff *, Suff *)); -static void SuffFindDeps __P((GNode *, Lst)); -static void SuffFindArchiveDeps __P((GNode *, Lst)); -static void SuffFindNormalDeps __P((GNode *, Lst)); -static int SuffPrintName __P((ClientData, ClientData)); -static int SuffPrintSuff __P((ClientData, ClientData)); -static int SuffPrintTrans __P((ClientData, ClientData)); +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 ****************/ /*- @@ -184,6 +246,10 @@ static int SuffPrintTrans __P((ClientData, ClientData)); * 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 * @@ -191,10 +257,8 @@ static int SuffPrintTrans __P((ClientData, ClientData)); * None *----------------------------------------------------------------------- */ -static char * -SuffStrIsPrefix (pref, str) - register char *pref; /* possible prefix */ - register char *str; /* string to check */ +static const char * +SuffStrIsPrefix(const char *pref, const char *str) { while (*str && *pref == *str) { pref++; @@ -207,8 +271,12 @@ SuffStrIsPrefix (pref, str) /*- *----------------------------------------------------------------------- * SuffSuffIsSuffix -- - * See if suff is a suffix of str. Str should point to THE END of the - * string to check. (THE END == the null byte) + * 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 @@ -219,15 +287,16 @@ SuffStrIsPrefix (pref, str) *----------------------------------------------------------------------- */ static char * -SuffSuffIsSuffix (s, str) - register Suff *s; /* possible suffix */ - char *str; /* string to examine */ +SuffSuffIsSuffix(const Suff *s, const SuffixCmpData *sd) { - register char *p1; /* Pointer into suffix name */ - register char *p2; /* Pointer into string being examined */ + 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 = str; + p2 = sd->ename; while (p1 >= s->name && *p1 == *p2) { p1--; @@ -252,11 +321,9 @@ SuffSuffIsSuffix (s, str) *----------------------------------------------------------------------- */ static int -SuffSuffIsSuffixP(s, str) - ClientData s; - ClientData str; +SuffSuffIsSuffixP(const void *s, const void *sd) { - return(!SuffSuffIsSuffix((Suff *) s, (char *) str)); + return(!SuffSuffIsSuffix(s, sd)); } /*- @@ -265,6 +332,10 @@ SuffSuffIsSuffixP(s, str) * 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. * @@ -273,11 +344,9 @@ SuffSuffIsSuffixP(s, str) *----------------------------------------------------------------------- */ static int -SuffSuffHasNameP (s, sname) - ClientData s; /* Suffix to check */ - ClientData sname; /* Desired name */ +SuffSuffHasNameP(const void *s, const void *sname) { - return (strcmp ((char *) sname, ((Suff *) s)->name)); + return (strcmp(sname, ((const Suff *)s)->name)); } /*- @@ -288,6 +357,10 @@ SuffSuffHasNameP (s, sname) * 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 * @@ -296,11 +369,9 @@ SuffSuffHasNameP (s, sname) *----------------------------------------------------------------------- */ static int -SuffSuffIsPrefix (s, str) - ClientData s; /* suffix to compare */ - ClientData str; /* string to examine */ +SuffSuffIsPrefix(const void *s, const void *str) { - return (SuffStrIsPrefix (((Suff *) s)->name, (char *) str) == NULL ? 1 : 0); + return SuffStrIsPrefix(((const Suff *)s)->name, str) == NULL; } /*- @@ -308,6 +379,10 @@ SuffSuffIsPrefix (s, str) * 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 * @@ -316,26 +391,22 @@ SuffSuffIsPrefix (s, str) *----------------------------------------------------------------------- */ static int -SuffGNHasNameP (gn, name) - ClientData gn; /* current node we're looking at */ - ClientData name; /* name we're looking for */ +SuffGNHasNameP(const void *gn, const void *name) { - return (strcmp ((char *) name, ((GNode *) gn)->name)); + return (strcmp(name, ((const GNode *)gn)->name)); } /*********** Maintenance Functions ************/ static void -SuffUnRef(lp, sp) - ClientData lp; - ClientData sp; +SuffUnRef(void *lp, void *sp) { Lst l = (Lst) lp; LstNode ln = Lst_Member(l, sp); - if (ln != NILLNODE) { + if (ln != NULL) { Lst_Remove(l, ln); - ((Suff *) sp)->refCount--; + ((Suff *)sp)->refCount--; } } @@ -352,10 +423,9 @@ SuffUnRef(lp, sp) *----------------------------------------------------------------------- */ static void -SuffFree (sp) - ClientData sp; +SuffFree(void *sp) { - Suff *s = (Suff *) sp; + Suff *s = (Suff *)sp; if (s == suffNull) suffNull = NULL; @@ -363,13 +433,20 @@ SuffFree (sp) if (s == emptySuff) emptySuff = NULL; - Lst_Destroy (s->ref, NOFREE); - Lst_Destroy (s->children, NOFREE); - Lst_Destroy (s->parents, NOFREE); - Lst_Destroy (s->searchPath, Dir_Destroy); +#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 - free ((Address)s->name); - free ((Address)s); + 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); } /*- @@ -386,13 +463,13 @@ SuffFree (sp) *----------------------------------------------------------------------- */ static void -SuffRemove(l, s) - Lst l; - Suff *s; +SuffRemove(Lst l, Suff *s) { - SuffUnRef((ClientData) l, (ClientData) s); - if (s->refCount == 0) - SuffFree((ClientData) s); + SuffUnRef(l, s); + if (s->refCount == 0) { + SuffUnRef(sufflist, s); + SuffFree(s); + } } /*- @@ -401,6 +478,10 @@ SuffRemove(l, s) * 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 * @@ -409,43 +490,41 @@ SuffRemove(l, s) *----------------------------------------------------------------------- */ static void -SuffInsert (l, s) - Lst l; /* the list where in s should be inserted */ - Suff *s; /* the suffix to insert */ +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) { + if (Lst_Open(l) == FAILURE) { return; } - while ((ln = Lst_Next (l)) != NILLNODE) { - s2 = (Suff *) Lst_Datum (ln); + while ((ln = Lst_Next(l)) != NULL) { + s2 = (Suff *)Lst_Datum(ln); if (s2->sNum >= s->sNum) { break; } } - Lst_Close (l); + Lst_Close(l); if (DEBUG(SUFF)) { - printf("inserting %s(%d)...", s->name, s->sNum); + fprintf(debug_file, "inserting %s(%d)...", s->name, s->sNum); } - if (ln == NILLNODE) { + if (ln == NULL) { if (DEBUG(SUFF)) { - printf("at end of list\n"); + fprintf(debug_file, "at end of list\n"); } - (void)Lst_AtEnd (l, (ClientData)s); + (void)Lst_AtEnd(l, s); s->refCount++; - (void)Lst_AtEnd(s->ref, (ClientData) l); + (void)Lst_AtEnd(s->ref, l); } else if (s2->sNum != s->sNum) { if (DEBUG(SUFF)) { - printf("before %s(%d)\n", s2->name, s2->sNum); + fprintf(debug_file, "before %s(%d)\n", s2->name, s2->sNum); } - (void)Lst_Insert (l, ln, (ClientData)s); + (void)Lst_InsertBefore(l, ln, s); s->refCount++; - (void)Lst_AtEnd(s->ref, (ClientData) l); + (void)Lst_AtEnd(s->ref, l); } else if (DEBUG(SUFF)) { - printf("already there\n"); + fprintf(debug_file, "already there\n"); } } @@ -467,9 +546,11 @@ SuffInsert (l, s) *----------------------------------------------------------------------- */ void -Suff_ClearSuffixes () +Suff_ClearSuffixes(void) { - Lst_Concat (suffClean, sufflist, LST_CONCLINK); +#ifdef CLEANUP + Lst_Concat(suffClean, sufflist, LST_CONCLINK); +#endif sufflist = Lst_Init(FALSE); sNum = 0; suffNull = emptySuff; @@ -480,6 +561,11 @@ Suff_ClearSuffixes () * 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. * @@ -489,23 +575,20 @@ Suff_ClearSuffixes () *----------------------------------------------------------------------- */ static Boolean -SuffParseTransform(str, srcPtr, targPtr) - char *str; /* String being parsed */ - Suff **srcPtr; /* Place to store source of trans. */ - Suff **targPtr; /* Place to store target of trans. */ +SuffParseTransform(char *str, Suff **srcPtr, Suff **targPtr) { - register LstNode srcLn; /* element in suffix list of trans source*/ - register Suff *src; /* Source of transformation */ - register LstNode targLn; /* element in suffix list of trans target*/ - register char *str2; /* Extra pointer (maybe target suffix) */ + LstNode srcLn; /* element in suffix list of trans source*/ + Suff *src; /* Source of transformation */ + LstNode targLn; /* element in suffix list of trans target*/ + char *str2; /* Extra pointer (maybe target suffix) */ LstNode singleLn; /* element in suffix list of any suffix * that exactly matches str */ Suff *single = NULL;/* Source of possible transformation to * null suffix */ - srcLn = NILLNODE; - singleLn = NILLNODE; - + 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 @@ -513,17 +596,17 @@ SuffParseTransform(str, srcPtr, targPtr) * parsed the string. */ for (;;) { - if (srcLn == NILLNODE) { - srcLn = Lst_Find(sufflist, (ClientData)str, SuffSuffIsPrefix); + if (srcLn == NULL) { + srcLn = Lst_Find(sufflist, str, SuffSuffIsPrefix); } else { - srcLn = Lst_FindFrom (sufflist, Lst_Succ(srcLn), (ClientData)str, + srcLn = Lst_FindFrom(sufflist, Lst_Succ(srcLn), str, SuffSuffIsPrefix); } - if (srcLn == NILLNODE) { + if (srcLn == NULL) { /* * Ran out of source suffixes -- no such rule */ - if (singleLn != NILLNODE) { + if (singleLn != NULL) { /* * Not so fast Mr. Smith! There was a suffix that encompassed * the entire string, so we assume it was a transformation @@ -539,14 +622,14 @@ SuffParseTransform(str, srcPtr, targPtr) } return (FALSE); } - src = (Suff *) Lst_Datum (srcLn); + src = (Suff *)Lst_Datum(srcLn); str2 = str + src->nameLen; if (*str2 == '\0') { single = src; singleLn = srcLn; } else { - targLn = Lst_Find(sufflist, (ClientData)str2, SuffSuffHasNameP); - if (targLn != NILLNODE) { + targLn = Lst_Find(sufflist, str2, SuffSuffHasNameP); + if (targLn != NULL) { *srcPtr = src; *targPtr = (Suff *)Lst_Datum(targLn); return (TRUE); @@ -561,6 +644,9 @@ SuffParseTransform(str, srcPtr, targPtr) * 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 @@ -570,8 +656,7 @@ SuffParseTransform(str, srcPtr, targPtr) *----------------------------------------------------------------------- */ Boolean -Suff_IsTransform (str) - char *str; /* string to check */ +Suff_IsTransform(char *str) { Suff *src, *targ; @@ -584,6 +669,9 @@ Suff_IsTransform (str) * 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 * @@ -593,22 +681,21 @@ Suff_IsTransform (str) *----------------------------------------------------------------------- */ GNode * -Suff_AddTransform (line) - char *line; /* name of transformation to add */ +Suff_AddTransform(char *line) { GNode *gn; /* GNode of transformation rule */ Suff *s, /* source suffix */ *t; /* target suffix */ LstNode ln; /* Node for existing transformation */ - ln = Lst_Find (transforms, (ClientData)line, SuffGNHasNameP); - if (ln == NILLNODE) { + 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. + * by the Parse module. */ - gn = Targ_NewGN (line); - (void)Lst_AtEnd (transforms, (ClientData)gn); + gn = Targ_NewGN(line); + (void)Lst_AtEnd(transforms, gn); } else { /* * New specification for transformation rule. Just nuke the old list @@ -616,11 +703,11 @@ Suff_AddTransform (line) * free the commands themselves, because a given command can be * attached to several different transformations. */ - gn = (GNode *) Lst_Datum (ln); - Lst_Destroy (gn->commands, NOFREE); - Lst_Destroy (gn->children, NOFREE); - gn->commands = Lst_Init (FALSE); - gn->children = Lst_Init (FALSE); + gn = (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; @@ -628,14 +715,14 @@ Suff_AddTransform (line) (void)SuffParseTransform(line, &s, &t); /* - * link the two together in the proper relationship and order + * link the two together in the proper relationship and order */ if (DEBUG(SUFF)) { - printf("defining transformation from `%s' to `%s'\n", + fprintf(debug_file, "defining transformation from `%s' to `%s'\n", s->name, t->name); } - SuffInsert (t->children, s); - SuffInsert (s->parents, t); + SuffInsert(t->children, s); + SuffInsert(s->parents, t); return (gn); } @@ -648,50 +735,65 @@ Suff_AddTransform (line) * 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 suffices are altered. + * lists of the affected suffixes are altered. * *----------------------------------------------------------------------- */ int -Suff_EndTransform(gnp, dummy) - ClientData gnp; /* Node for transformation */ - ClientData dummy; /* Node for transformation */ +Suff_EndTransform(void *gnp, void *dummy) { - GNode *gn = (GNode *) gnp; - + 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; - (void)SuffParseTransform(gn->name, &s, &t); + /* + * SuffParseTransform() may fail for special rules which are not + * actual transformation rules. (e.g. .DEFAULT) + */ + if (SuffParseTransform(gn->name, &s, &t)) { + Lst p; - if (DEBUG(SUFF)) { - printf("deleting transformation from %s to %s\n", - s->name, t->name); + 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); } - - /* - * 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(s->parents, t); } else if ((gn->type & OP_TRANSFORM) && DEBUG(SUFF)) { - printf("transformation %s complete\n", gn->name); + fprintf(debug_file, "transformation %s complete\n", gn->name); } return(dummy ? 0 : 0); @@ -707,6 +809,10 @@ Suff_EndTransform(gnp, dummy) * existing suffix, the proper relationship is established between * the two. * + * Input: + * transformp Transformation to test + * sp Suffix to rebuild + * * Results: * Always 0. * @@ -717,23 +823,22 @@ Suff_EndTransform(gnp, dummy) *----------------------------------------------------------------------- */ static int -SuffRebuildGraph(transformp, sp) - ClientData transformp; /* Transformation to test */ - ClientData sp; /* Suffix to rebuild */ +SuffRebuildGraph(void *transformp, void *sp) { - GNode *transform = (GNode *) transformp; - Suff *s = (Suff *) sp; + GNode *transform = (GNode *)transformp; + Suff *s = (Suff *)sp; char *cp; LstNode ln; Suff *s2; + SuffixCmpData sd; /* * First see if it is a transformation from this suffix. */ - cp = SuffStrIsPrefix(s->name, transform->name); - if (cp != (char *)NULL) { - ln = Lst_Find(sufflist, (ClientData)cp, SuffSuffHasNameP); - if (ln != NILLNODE) { + 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. @@ -748,18 +853,20 @@ SuffRebuildGraph(transformp, sp) /* * Not from, maybe to? */ - cp = SuffSuffIsSuffix(s, transform->name + strlen(transform->name)); - if (cp != (char *)NULL) { + sd.len = strlen(transform->name); + sd.ename = transform->name + sd.len; + cp = SuffSuffIsSuffix(s, &sd); + if (cp != NULL) { /* * Null-terminate the source suffix in order to find it. */ cp[1] = '\0'; - ln = Lst_Find(sufflist, (ClientData)transform->name, SuffSuffHasNameP); + ln = Lst_Find(sufflist, transform->name, SuffSuffHasNameP); /* * Replace the start of the target suffix */ cp[1] = s->name[0]; - if (ln != NILLNODE) { + if (ln != NULL) { /* * Found it -- establish the proper relationship */ @@ -771,48 +878,123 @@ SuffRebuildGraph(transformp, sp) 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 (str) - char *str; /* the name of the suffix to add */ +Suff_AddSuffix(char *str, GNode **gn) { Suff *s; /* new suffix descriptor */ LstNode ln; + GNodeSuff gs; - ln = Lst_Find (sufflist, (ClientData)str, SuffSuffHasNameP); - if (ln == NILLNODE) { - s = (Suff *) emalloc (sizeof (Suff)); + ln = Lst_Find(sufflist, str, SuffSuffHasNameP); + if (ln == NULL) { + s = bmake_malloc(sizeof(Suff)); - s->name = 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->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 = 0; + s->refCount = 1; - (void)Lst_AtEnd (sufflist, (ClientData)s); + (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, (ClientData)s); - } + Lst_ForEach(transforms, SuffRebuildGraph, s); + } } /*- @@ -821,7 +1003,7 @@ Suff_AddSuffix (str) * Return the search path for the given suffix, if it's defined. * * Results: - * The searchPath for the desired suffix or NILLST if the suffix isn't + * The searchPath for the desired suffix or NULL if the suffix isn't * defined. * * Side Effects: @@ -829,17 +1011,16 @@ Suff_AddSuffix (str) *----------------------------------------------------------------------- */ Lst -Suff_GetPath (sname) - char *sname; +Suff_GetPath(char *sname) { LstNode ln; Suff *s; - ln = Lst_Find (sufflist, (ClientData)sname, SuffSuffHasNameP); - if (ln == NILLNODE) { - return (NILLST); + ln = Lst_Find(sufflist, sname, SuffSuffHasNameP); + if (ln == NULL) { + return NULL; } else { - s = (Suff *) Lst_Datum (ln); + s = (Suff *)Lst_Datum(ln); return (s->searchPath); } } @@ -857,29 +1038,29 @@ Suff_GetPath (sname) * 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 preceeded by a -I. The same + * 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() +Suff_DoPaths(void) { - register Suff *s; - register LstNode ln; + Suff *s; + LstNode ln; char *ptr; Lst inIncludes; /* Cumulative .INCLUDES path */ Lst inLibs; /* Cumulative .LIBS path */ - if (Lst_Open (sufflist) == FAILURE) { + if (Lst_Open(sufflist) == FAILURE) { return; } inIncludes = Lst_Init(FALSE); inLibs = Lst_Init(FALSE); - while ((ln = Lst_Next (sufflist)) != NILLNODE) { - s = (Suff *) Lst_Datum (ln); + while ((ln = Lst_Next(sufflist)) != NULL) { + s = (Suff *)Lst_Datum(ln); if (!Lst_IsEmpty (s->searchPath)) { #ifdef INCLUDES if (s->flags & SUFF_INCLUDE) { @@ -893,20 +1074,20 @@ Suff_DoPaths() #endif /* LIBRARIES */ Dir_Concat(s->searchPath, dirSearchPath); } else { - Lst_Destroy (s->searchPath, Dir_Destroy); + Lst_Destroy(s->searchPath, Dir_Destroy); s->searchPath = Lst_Duplicate(dirSearchPath, Dir_CopyDir); } } - Var_Set(".INCLUDES", ptr = Dir_MakeFlags("-I", inIncludes), VAR_GLOBAL); + Var_Set(".INCLUDES", ptr = Dir_MakeFlags("-I", inIncludes), VAR_GLOBAL, 0); free(ptr); - Var_Set(".LIBS", ptr = Dir_MakeFlags("-L", inLibs), VAR_GLOBAL); + 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); + Lst_Close(sufflist); } /*- @@ -916,6 +1097,9 @@ Suff_DoPaths() * 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. * @@ -925,15 +1109,14 @@ Suff_DoPaths() *----------------------------------------------------------------------- */ void -Suff_AddInclude (sname) - char *sname; /* Name of suffix to mark */ +Suff_AddInclude(char *sname) { LstNode ln; Suff *s; - ln = Lst_Find (sufflist, (ClientData)sname, SuffSuffHasNameP); - if (ln != NILLNODE) { - s = (Suff *) Lst_Datum (ln); + ln = Lst_Find(sufflist, sname, SuffSuffHasNameP); + if (ln != NULL) { + s = (Suff *)Lst_Datum(ln); s->flags |= SUFF_INCLUDE; } } @@ -946,6 +1129,9 @@ Suff_AddInclude (sname) * suffix must have been defined via .SUFFIXES before this is * called. * + * Input: + * sname Name of the suffix to mark + * * Results: * None. * @@ -955,15 +1141,14 @@ Suff_AddInclude (sname) *----------------------------------------------------------------------- */ void -Suff_AddLib (sname) - char *sname; /* Name of suffix to mark */ +Suff_AddLib(char *sname) { LstNode ln; Suff *s; - ln = Lst_Find (sufflist, (ClientData)sname, SuffSuffHasNameP); - if (ln != NILLNODE) { - s = (Suff *) Lst_Datum (ln); + ln = Lst_Find(sufflist, sname, SuffSuffHasNameP); + if (ln != NULL) { + s = (Suff *)Lst_Datum(ln); s->flags |= SUFF_LIBRARY; } } @@ -977,6 +1162,10 @@ Suff_AddLib (sname) * 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 * @@ -985,57 +1174,55 @@ Suff_AddLib (sname) *----------------------------------------------------------------------- */ static int -SuffAddSrc (sp, lsp) - ClientData sp; /* suffix for which to create a Src structure */ - ClientData lsp; /* list and parent for the new Src */ +SuffAddSrc(void *sp, void *lsp) { - Suff *s = (Suff *) sp; - LstSrc *ls = (LstSrc *) 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 = (Src *) emalloc (sizeof (Src)); - s2->file = strdup(targ->pref); + s2 = bmake_malloc(sizeof(Src)); + s2->file = bmake_strdup(targ->pref); s2->pref = targ->pref; s2->parent = targ; - s2->node = NILGNODE; + s2->node = NULL; s2->suff = s; s->refCount++; s2->children = 0; targ->children += 1; - (void)Lst_AtEnd (ls->l, (ClientData)s2); + (void)Lst_AtEnd(ls->l, s2); #ifdef DEBUG_SRC s2->cp = Lst_Init(FALSE); - Lst_AtEnd(targ->cp, (ClientData) s2); - printf("1 add %x %x to %x:", targ, s2, ls->l); - Lst_ForEach(ls->l, PrintAddr, (ClientData) 0); - printf("\n"); + 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 = (Src *) emalloc (sizeof (Src)); - s2->file = str_concat (targ->pref, s->name, 0); + s2 = bmake_malloc(sizeof(Src)); + s2->file = str_concat(targ->pref, s->name, 0); s2->pref = targ->pref; s2->parent = targ; - s2->node = NILGNODE; + s2->node = NULL; s2->suff = s; s->refCount++; s2->children = 0; targ->children += 1; - (void)Lst_AtEnd (ls->l, (ClientData)s2); + (void)Lst_AtEnd(ls->l, s2); #ifdef DEBUG_SRC s2->cp = Lst_Init(FALSE); - Lst_AtEnd(targ->cp, (ClientData) s2); - printf("2 add %x %x to %x:", targ, s2, ls->l); - Lst_ForEach(ls->l, PrintAddr, (ClientData) 0); - printf("\n"); + 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); @@ -1046,6 +1233,10 @@ SuffAddSrc (sp, lsp) * 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 * @@ -1054,16 +1245,14 @@ SuffAddSrc (sp, lsp) *----------------------------------------------------------------------- */ static void -SuffAddLevel (l, targ) - Lst l; /* list to which to add the new level */ - Src *targ; /* Src structure to use as the parent */ +SuffAddLevel(Lst l, Src *targ) { LstSrc ls; ls.s = targ; ls.l = l; - Lst_ForEach (targ->suff->children, SuffAddSrc, (ClientData)&ls); + Lst_ForEach(targ->suff->children, SuffAddSrc, &ls); } /*- @@ -1079,52 +1268,51 @@ SuffAddLevel (l, targ) *---------------------------------------------------------------------- */ static int -SuffRemoveSrc (l) - Lst l; +SuffRemoveSrc(Lst l) { LstNode ln; Src *s; int t = 0; - if (Lst_Open (l) == FAILURE) { + if (Lst_Open(l) == FAILURE) { return 0; } #ifdef DEBUG_SRC - printf("cleaning %lx: ", (unsigned long) l); - Lst_ForEach(l, PrintAddr, (ClientData) 0); - printf("\n"); + fprintf(debug_file, "cleaning %lx: ", (unsigned long) l); + Lst_ForEach(l, PrintAddr, NULL); + fprintf(debug_file, "\n"); #endif - while ((ln = Lst_Next (l)) != NILLNODE) { - s = (Src *) Lst_Datum (ln); + while ((ln = Lst_Next(l)) != NULL) { + s = (Src *)Lst_Datum(ln); if (s->children == 0) { - free ((Address)s->file); + free(s->file); if (!s->parent) - free((Address)s->pref); + free(s->pref); else { #ifdef DEBUG_SRC - LstNode ln = Lst_Member(s->parent->cp, (ClientData)s); - if (ln != NILLNODE) + LstNode ln = Lst_Member(s->parent->cp, s); + if (ln != NULL) Lst_Remove(s->parent->cp, ln); #endif --s->parent->children; } #ifdef DEBUG_SRC - printf("free: [l=%x] p=%x %d\n", l, s, s->children); - Lst_Destroy(s->cp, NOFREE); + 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 ((Address)s); + free(s); t |= 1; Lst_Close(l); return TRUE; } #ifdef DEBUG_SRC else { - printf("keep: [l=%x] p=%x %d: ", l, s, s->children); - Lst_ForEach(s->cp, PrintAddr, (ClientData) 0); - printf("\n"); + fprintf(debug_file, "keep: [l=%x] p=%x %d: ", l, s, s->children); + Lst_ForEach(s->cp, PrintAddr, NULL); + fprintf(debug_file, "\n"); } #endif } @@ -1139,6 +1327,9 @@ SuffRemoveSrc (l) * 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 * @@ -1147,54 +1338,52 @@ SuffRemoveSrc (l) *----------------------------------------------------------------------- */ static Src * -SuffFindThem (srcs, slst) - Lst srcs; /* list of Src structures to search through */ - Lst slst; +SuffFindThem(Lst srcs, Lst slst) { Src *s; /* current Src */ Src *rs; /* returned Src */ char *ptr; - rs = (Src *) NULL; + rs = NULL; while (!Lst_IsEmpty (srcs)) { - s = (Src *) Lst_DeQueue (srcs); + s = (Src *)Lst_DeQueue(srcs); if (DEBUG(SUFF)) { - printf ("\ttrying %s...", s->file); + 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) != NILGNODE) { + if (Targ_FindNode(s->file, TARG_NOCREATE) != NULL) { #ifdef DEBUG_SRC - printf("remove %x from %x\n", s, srcs); + fprintf(debug_file, "remove %x from %x\n", s, srcs); #endif rs = s; break; } - if ((ptr = Dir_FindFile (s->file, s->suff->searchPath)) != NULL) { + if ((ptr = Dir_FindFile(s->file, s->suff->searchPath)) != NULL) { rs = s; #ifdef DEBUG_SRC - printf("remove %x from %x\n", s, srcs); + fprintf(debug_file, "remove %x from %x\n", s, srcs); #endif free(ptr); break; } if (DEBUG(SUFF)) { - printf ("not there\n"); + fprintf(debug_file, "not there\n"); } - SuffAddLevel (srcs, s); - Lst_AtEnd(slst, (ClientData) s); + SuffAddLevel(srcs, s); + Lst_AtEnd(slst, s); } if (DEBUG(SUFF) && rs) { - printf ("got it\n"); + fprintf(debug_file, "got it\n"); } return (rs); } @@ -1206,8 +1395,11 @@ SuffFindThem (srcs, slst) * one from which the target can be transformed. If there is one, * a Src structure is put together for it and returned. * + * Input: + * targ Src structure to play with + * * Results: - * The Src structure of the "winning" child, or NIL if no such beast. + * The Src structure of the "winning" child, or NULL if no such beast. * * Side Effects: * A Src structure may be allocated. @@ -1215,12 +1407,10 @@ SuffFindThem (srcs, slst) *----------------------------------------------------------------------- */ static Src * -SuffFindCmds (targ, slst) - Src *targ; /* Src structure to play with */ - Lst slst; +SuffFindCmds(Src *targ, Lst slst) { LstNode ln; /* General-purpose list node */ - register GNode *t, /* Target GNode */ + GNode *t, /* Target GNode */ *s; /* Source GNode */ int prefLen;/* The length of the defined prefix */ Suff *suff; /* Suffix on matching beastie */ @@ -1228,68 +1418,80 @@ SuffFindCmds (targ, slst) char *cp; t = targ->node; - (void) Lst_Open (t->children); - prefLen = strlen (targ->pref); + (void)Lst_Open(t->children); + prefLen = strlen(targ->pref); - while ((ln = Lst_Next (t->children)) != NILLNODE) { - s = (GNode *)Lst_Datum (ln); + for (;;) { + ln = Lst_Next(t->children); + if (ln == NULL) { + Lst_Close(t->children); + return NULL; + } + s = (GNode *)Lst_Datum(ln); - cp = strrchr (s->name, '/'); - if (cp == (char *)NULL) { + 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) { - /* - * The node matches the prefix ok, see if it has a known - * suffix. - */ - ln = Lst_Find (sufflist, (ClientData)&cp[prefLen], - SuffSuffHasNameP); - if (ln != NILLNODE) { - /* - * 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 (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, - (ClientData)targ->suff) != NILLNODE) - { - /* - * 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 = (Src *)emalloc (sizeof (Src)); - ret->file = 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); - printf("3 add %x %x\n", targ, ret); - Lst_AtEnd(targ->cp, (ClientData) ret); -#endif - Lst_AtEnd(slst, (ClientData) ret); - if (DEBUG(SUFF)) { - printf ("\tusing existing source %s\n", s->name); - } - return (ret); - } - } - } + if (Lst_Member(suff->parents, targ->suff) != NULL) + break; } - Lst_Close (t->children); - return ((Src *)NULL); + + /* + * 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); } /*- @@ -1298,6 +1500,10 @@ SuffFindCmds (targ, slst) * 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) * @@ -1308,228 +1514,256 @@ SuffFindCmds (targ, slst) * *----------------------------------------------------------------------- */ -static int -SuffExpandChildren(cgnp, pgnp) - ClientData cgnp; /* Child to examine */ - ClientData pgnp; /* Parent node being processed */ +static void +SuffExpandChildren(LstNode cln, GNode *pgn) { - GNode *cgn = (GNode *) cgnp; - GNode *pgn = (GNode *) pgnp; + GNode *cgn = (GNode *)Lst_Datum(cln); GNode *gn; /* New source 8) */ - LstNode prevLN; /* Node after which new source should be put */ - LstNode ln; /* List element for old source */ char *cp; /* Expanded value */ - /* - * New nodes effectively take the place of the child, so place them - * after the child - */ - prevLN = Lst_Member(pgn->children, (ClientData)cgn); - + 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, '$') != (char *)NULL) { - if (DEBUG(SUFF)) { - printf("Expanding \"%s\"...", cgn->name); - } - cp = Var_Subst(NULL, cgn->name, pgn, TRUE); - - if (cp != (char *)NULL) { - Lst members = Lst_Init(FALSE); - - if (cgn->type & OP_ARCHV) { - /* - * Node was an archive(member) target, so we want to call - * on the Arch module to find the nodes for us, expanding - * variables in the parent's context. - */ - char *sacrifice = cp; - - (void)Arch_ParseArchive(&sacrifice, members, pgn); - } else { - /* - * Break the result into a vector of strings whose nodes - * we can find, then add those nodes to the members list. - * Unfortunately, we can't use brk_string b/c it - * doesn't understand about variable specifications with - * spaces in them... - */ - char *start; - char *initcp = cp; /* For freeing... */ - - for (start = cp; *start == ' ' || *start == '\t'; start++) - continue; - for (cp = start; *cp != '\0'; cp++) { - if (*cp == ' ' || *cp == '\t') { - /* - * White-space -- terminate element, find the node, - * add it, skip any further spaces. - */ - *cp++ = '\0'; - gn = Targ_FindNode(start, TARG_CREATE); - (void)Lst_AtEnd(members, (ClientData)gn); - while (*cp == ' ' || *cp == '\t') { - cp++; - } - /* - * Adjust cp for increment at start of loop, but - * set start to first non-space. - */ - start = cp--; - } else if (*cp == '$') { - /* - * Start of a variable spec -- contact variable module - * to find the end so we can skip over it. - */ - char *junk; - int len; - Boolean doFree; - - junk = Var_Parse(cp, pgn, TRUE, &len, &doFree); - if (junk != var_Error) { - cp += len - 1; - } - - if (doFree) { - free(junk); - } - } else if (*cp == '\\' && *cp != '\0') { - /* - * Escaped something -- skip over it - */ - cp++; - } - } - - if (cp != start) { - /* - * Stuff left over -- add it to the list too - */ - gn = Targ_FindNode(start, TARG_CREATE); - (void)Lst_AtEnd(members, (ClientData)gn); - } - /* - * Point cp back at the beginning again so the variable value - * can be freed. - */ - cp = initcp; - } - /* - * Add all elements of the members list to the parent node. - */ - while(!Lst_IsEmpty(members)) { - gn = (GNode *)Lst_DeQueue(members); - - if (DEBUG(SUFF)) { - printf("%s...", gn->name); - } - if (Lst_Member(pgn->children, (ClientData)gn) == NILLNODE) { - (void)Lst_Append(pgn->children, prevLN, (ClientData)gn); - prevLN = Lst_Succ(prevLN); - (void)Lst_AtEnd(gn->parents, (ClientData)pgn); - pgn->unmade++; - } - } - Lst_Destroy(members, NOFREE); - /* - * Free the result - */ - free((char *)cp); - } - /* - * Now the source is expanded, remove it from the list of children to - * keep it from being processed. - */ - ln = Lst_Member(pgn->children, (ClientData)cgn); - pgn->unmade--; - Lst_Remove(pgn->children, ln); - if (DEBUG(SUFF)) { - printf("\n"); - } - } else if (Dir_HasWildcards(cgn->name)) { - Lst exp; /* List of expansions */ - Lst path; /* Search path along which to expand */ - - /* - * Find a path along which to expand the word. - * - * If the word has a known suffix, use that path. - * If it has no known suffix and we're allowed to use the null - * suffix, use its path. - * Else use the default system search path. - */ - cp = cgn->name + strlen(cgn->name); - ln = Lst_Find(sufflist, (ClientData)cp, SuffSuffIsSuffixP); - - if (DEBUG(SUFF)) { - printf("Wildcard expanding \"%s\"...", cgn->name); - } - - if (ln != NILLNODE) { - Suff *s = (Suff *)Lst_Datum(ln); - - if (DEBUG(SUFF)) { - printf("suffix is \"%s\"...", s->name); - } - path = s->searchPath; - } else { - /* - * Use default search path - */ - path = dirSearchPath; - } - - /* - * Expand the word along the chosen path - */ - exp = Lst_Init(FALSE); - Dir_Expand(cgn->name, path, exp); - - while (!Lst_IsEmpty(exp)) { - /* - * Fetch next expansion off the list and find its GNode - */ - cp = (char *)Lst_DeQueue(exp); - - if (DEBUG(SUFF)) { - printf("%s...", cp); - } - gn = Targ_FindNode(cp, TARG_CREATE); - - /* - * If gn isn't already a child of the parent, make it so and - * up the parent's count of unmade children. - */ - if (Lst_Member(pgn->children, (ClientData)gn) == NILLNODE) { - (void)Lst_Append(pgn->children, prevLN, (ClientData)gn); - prevLN = Lst_Succ(prevLN); - (void)Lst_AtEnd(gn->parents, (ClientData)pgn); - pgn->unmade++; - } - } - - /* - * Nuke what's left of the list - */ - Lst_Destroy(exp, NOFREE); - - /* - * Now the source is expanded, remove it from the list of children to - * keep it from being processed. - */ - ln = Lst_Member(pgn->children, (ClientData)cgn); - pgn->unmade--; - Lst_Remove(pgn->children, ln); - if (DEBUG(SUFF)) { - printf("\n"); - } + if (strchr(cgn->name, '$') == NULL) { + SuffExpandWildcards(cln, pgn); + return; } - return(0); + 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; + } } /*- @@ -1538,6 +1772,12 @@ SuffExpandChildren(cgnp, pgnp) * 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. * @@ -1551,55 +1791,27 @@ SuffExpandChildren(cgnp, pgnp) *----------------------------------------------------------------------- */ static Boolean -SuffApplyTransform(tGn, sGn, t, s) - GNode *tGn; /* Target node */ - GNode *sGn; /* Source node */ - Suff *t; /* Target suffix */ - Suff *s; /* Source suffix */ +SuffApplyTransform(GNode *tGn, GNode *sGn, Suff *t, Suff *s) { - LstNode ln; /* General node */ + LstNode ln, nln; /* General node */ char *tname; /* Name of transformation rule */ GNode *gn; /* Node for same */ - if (Lst_Member(tGn->children, (ClientData)sGn) == NILLNODE) { - /* - * Not already linked, so form the proper links between the - * target and source. - */ - (void)Lst_AtEnd(tGn->children, (ClientData)sGn); - (void)Lst_AtEnd(sGn->parents, (ClientData)tGn); - tGn->unmade += 1; - } + /* + * Form the proper links between the target and source. + */ + (void)Lst_AtEnd(tGn->children, sGn); + (void)Lst_AtEnd(sGn->parents, tGn); + tGn->unmade += 1; - if ((sGn->type & OP_OPMASK) == OP_DOUBLEDEP) { - /* - * When a :: node is used as the implied source of a node, we have - * to link all its cohorts in as sources as well. Only the initial - * sGn gets the target in its iParents list, however, as that - * will be sufficient to get the .IMPSRC variable set for tGn - */ - for (ln=Lst_First(sGn->cohorts); ln != NILLNODE; ln=Lst_Succ(ln)) { - gn = (GNode *)Lst_Datum(ln); - - if (Lst_Member(tGn->children, (ClientData)gn) == NILLNODE) { - /* - * Not already linked, so form the proper links between the - * target and source. - */ - (void)Lst_AtEnd(tGn->children, (ClientData)gn); - (void)Lst_AtEnd(gn->parents, (ClientData)tGn); - tGn->unmade += 1; - } - } - } /* * Locate the transformation rule itself */ tname = str_concat(s->name, t->name, 0); - ln = Lst_Find(transforms, (ClientData)tname, SuffGNHasNameP); + ln = Lst_Find(transforms, tname, SuffGNHasNameP); free(tname); - if (ln == NILLNODE) { + 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 @@ -1609,16 +1821,16 @@ SuffApplyTransform(tGn, sGn, t, s) } gn = (GNode *)Lst_Datum(ln); - + if (DEBUG(SUFF)) { - printf("\tapplying %s -> %s to \"%s\"\n", s->name, t->name, tGn->name); + 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 */ @@ -1627,17 +1839,16 @@ SuffApplyTransform(tGn, sGn, t, s) /* * Deal with wildcards and variables in any acquired sources */ - ln = Lst_Succ(ln); - if (ln != NILLNODE) { - Lst_ForEachFrom(tGn->children, ln, - SuffExpandChildren, (ClientData)tGn); + 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, (ClientData)tGn); + (void)Lst_AtEnd(sGn->iParents, tGn); return(TRUE); } @@ -1648,6 +1859,9 @@ SuffApplyTransform(tGn, sGn, t, s) * SuffFindArchiveDeps -- * Locate dependencies for an OP_ARCHV node. * + * Input: + * gn Node for which to locate dependencies + * * Results: * None * @@ -1657,33 +1871,32 @@ SuffApplyTransform(tGn, sGn, t, s) *----------------------------------------------------------------------- */ static void -SuffFindArchiveDeps(gn, slst) - GNode *gn; /* Node for which to locate dependencies */ - Lst slst; +SuffFindArchiveDeps(GNode *gn, Lst slst) { char *eoarch; /* End of archive portion */ char *eoname; /* End of member portion */ GNode *mem; /* Node for member */ - static char *copy[] = { /* Variables to be copied from the member node */ + 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, ')'); + 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 @@ -1697,18 +1910,16 @@ SuffFindArchiveDeps(gn, slst) /* * Create the link between the two nodes right off */ - if (Lst_Member(gn->children, (ClientData)mem) == NILLNODE) { - (void)Lst_AtEnd(gn->children, (ClientData)mem); - (void)Lst_AtEnd(mem->parents, (ClientData)gn); - gn->unmade += 1; - } - + (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); + Var_Set(copy[i], Var_Value(copy[i], mem, &p1), gn, 0); if (p1) free(p1); @@ -1720,7 +1931,7 @@ SuffFindArchiveDeps(gn, slst) * Didn't know what it was -- use .NULL suffix if not in make mode */ if (DEBUG(SUFF)) { - printf("using null suffix\n"); + fprintf(debug_file, "using null suffix\n"); } ms = suffNull; } @@ -1729,8 +1940,8 @@ SuffFindArchiveDeps(gn, slst) /* * Set the other two local variables required for this target. */ - Var_Set (MEMBER, name, gn); - Var_Set (ARCHIVE, gn->name, gn); + Var_Set(MEMBER, name, gn, 0); + Var_Set(ARCHIVE, gn->name, gn, 0); if (ms != NULL) { /* @@ -1739,21 +1950,24 @@ SuffFindArchiveDeps(gn, slst) * through the entire list, we just look at suffixes to which the * member's suffix may be transformed... */ - LstNode ln; + LstNode ln; + SuffixCmpData sd; /* Search string data */ /* * Use first matching suffix... */ - ln = Lst_Find(ms->parents, eoarch, SuffSuffIsSuffixP); + sd.len = eoarch - gn->name; + sd.ename = eoarch; + ln = Lst_Find(ms->parents, &sd, SuffSuffIsSuffixP); - if (ln != NILLNODE) { + if (ln != NULL) { /* * Got one -- apply it */ if (!SuffApplyTransform(gn, mem, (Suff *)Lst_Datum(ln), ms) && DEBUG(SUFF)) { - printf("\tNo transformation from %s -> %s\n", + fprintf(debug_file, "\tNo transformation from %s -> %s\n", ms->name, ((Suff *)Lst_Datum(ln))->name); } } @@ -1786,6 +2000,9 @@ SuffFindArchiveDeps(gn, slst) * SuffFindNormalDeps -- * Locate implicit dependencies for regular targets. * + * Input: + * gn Node for which to find sources + * * Results: * None. * @@ -1795,13 +2012,11 @@ SuffFindArchiveDeps(gn, slst) *----------------------------------------------------------------------- */ static void -SuffFindNormalDeps(gn, slst) - GNode *gn; /* Node for which to find sources */ - Lst slst; +SuffFindNormalDeps(GNode *gn, Lst slst) { char *eoname; /* End of name */ char *sopref; /* Start of prefix */ - LstNode ln; /* Next suffix node to check */ + 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, @@ -1810,12 +2025,14 @@ SuffFindNormalDeps(gn, slst) Src *src; /* General Src pointer */ char *pref; /* Prefix to use */ Src *targ; /* General Src target pointer */ + SuffixCmpData sd; /* Search string data */ - eoname = gn->name + strlen(gn->name); + sd.len = strlen(gn->name); + sd.ename = eoname = gn->name + sd.len; sopref = gn->name; - + /* * Begin at the beginning... */ @@ -1841,36 +2058,36 @@ SuffFindNormalDeps(gn, slst) * children, then look for any overriding transformations they imply. * Should we find one, we discard the one we found before. */ - while(ln != NILLNODE) { + + while (ln != NULL) { /* * Look for next possible suffix... */ - ln = Lst_FindFrom(sufflist, ln, eoname, SuffSuffIsSuffixP); + ln = Lst_FindFrom(sufflist, ln, &sd, SuffSuffIsSuffixP); - if (ln != NILLNODE) { + if (ln != NULL) { int prefLen; /* Length of the prefix */ - Src *targ; - + /* * Allocate a Src structure to which things can be transformed */ - targ = (Src *)emalloc(sizeof (Src)); - targ->file = strdup(gn->name); + 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 = (Src *)NULL; + targ->parent = NULL; targ->children = 0; #ifdef DEBUG_SRC targ->cp = Lst_Init(FALSE); #endif - + /* * Allocate room for the prefix, whose end is found by subtracting * the length of the suffix from the end of the name. */ prefLen = (eoname - targ->suff->nameLen) - sopref; - targ->pref = emalloc(prefLen + 1); + targ->pref = bmake_malloc(prefLen + 1); memcpy(targ->pref, sopref, prefLen); targ->pref[prefLen] = '\0'; @@ -1882,7 +2099,7 @@ SuffFindNormalDeps(gn, slst) /* * Record the target so we can nuke it */ - (void)Lst_AtEnd(targs, (ClientData)targ); + (void)Lst_AtEnd(targs, targ); /* * Search from this suffix's successor... @@ -1896,45 +2113,47 @@ SuffFindNormalDeps(gn, slst) */ if (Lst_IsEmpty(targs) && suffNull != NULL) { if (DEBUG(SUFF)) { - printf("\tNo known suffix on %s. Using .NULL suffix: ", gn->name); + fprintf(debug_file, "\tNo known suffix on %s. Using .NULL suffix\n", gn->name); } - - targ = (Src *)emalloc(sizeof (Src)); - targ->file = strdup(gn->name); + + targ = bmake_malloc(sizeof(Src)); + targ->file = bmake_strdup(gn->name); targ->suff = suffNull; targ->suff->refCount++; targ->node = gn; - targ->parent = (Src *)NULL; + targ->parent = NULL; targ->children = 0; - targ->pref = strdup(sopref); + 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 - * or dependencies defined for this gnode + * 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) && Lst_IsEmpty(gn->children)) + if (Lst_IsEmpty(gn->commands)) SuffAddLevel(srcs, targ); else { - if (DEBUG(SUFF)) - printf("not "); + if (DEBUG(SUFF)) + fprintf(debug_file, "not "); } - if (DEBUG(SUFF)) - printf("adding suffix rules\n"); + if (DEBUG(SUFF)) + fprintf(debug_file, "adding suffix rules\n"); - (void)Lst_AtEnd(targs, (ClientData)targ); + (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 == (Src *)NULL) { + if (bottom == NULL) { /* * No known transformations -- use the first suffix found for setting * the local variables. @@ -1942,7 +2161,7 @@ SuffFindNormalDeps(gn, slst) if (!Lst_IsEmpty(targs)) { targ = (Src *)Lst_Datum(Lst_First(targs)); } else { - targ = (Src *)NULL; + targ = NULL; } } else { /* @@ -1953,62 +2172,65 @@ SuffFindNormalDeps(gn, slst) continue; } - /* - * The .TARGET variable we always set to be the name at this point, - * since it's only set to the path if the thing is only a source and - * if it's only a source, it doesn't matter what we put here as far - * as expanding sources is concerned, since it has none... - */ - Var_Set(TARGET, gn->name, gn); + Var_Set(TARGET, gn->path ? gn->path : gn->name, gn, 0); pref = (targ != NULL) ? targ->pref : gn->name; - Var_Set(PREFIX, pref, gn); + 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. */ - Lst_ForEach(gn->children, SuffExpandChildren, (ClientData)gn); - + for (ln = Lst_First(gn->children); ln != NULL; ln = nln) { + nln = Lst_Succ(ln); + SuffExpandChildren(ln, gn); + } + if (targ == NULL) { if (DEBUG(SUFF)) { - printf("\tNo valid suffix on %s\n", gn->name); + fprintf(debug_file, "\tNo valid suffix on %s\n", gn->name); } sfnd_abort: /* - * Deal with finding the thing on the default search path if the - * node is only a source (not on the lhs of a dependency operator - * or [XXX] it has neither children or commands). + * 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 (OP_NOP(gn->type) || - (Lst_IsEmpty(gn->children) && Lst_IsEmpty(gn->commands))) - { + 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) { - Var_Set(TARGET, gn->path, gn); + 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 len = strlen(gn->path); - char savec; + 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[len-targ->suff->nameLen]; - gn->path[len-targ->suff->nameLen] = '\0'; + savec = gn->path[savep]; + gn->path[savep] = '\0'; - Var_Set(PREFIX, gn->path, gn); + if ((ptr = strrchr(gn->path, '/')) != NULL) + ptr++; + else + ptr = gn->path; - gn->path[len-targ->suff->nameLen] = savec; + Var_Set(PREFIX, ptr, gn, 0); + + gn->path[savep] = savec; } else { /* * The .PREFIX gets the full path if the target has @@ -2018,25 +2240,16 @@ sfnd_abort: gn->suffix->refCount--; gn->suffix = NULL; - Var_Set(PREFIX, gn->path, gn); + if ((ptr = strrchr(gn->path, '/')) != NULL) + ptr++; + else + ptr = gn->path; + + Var_Set(PREFIX, ptr, gn, 0); } } - } else { - /* - * Not appropriate to search for the thing -- set the - * path to be the name so Dir_MTime won't go grovelling for - * it. - */ - if (gn->suffix) - gn->suffix->refCount--; - gn->suffix = (targ == NULL) ? NULL : targ->suff; - if (gn->suffix) - gn->suffix->refCount++; - if (gn->path != NULL) - free(gn->path); - gn->path = strdup(gn->name); } - + goto sfnd_return; } @@ -2054,14 +2267,14 @@ sfnd_abort: if (!Lst_IsEmpty(gn->children)) { src = SuffFindCmds(targ, slst); - if (src != (Src *)NULL) { + if (src != NULL) { /* * Free up all the Src structures in the transformation path * up to, but not including, the parent node. */ while (bottom && bottom->parent != NULL) { - if (Lst_Member(slst, (ClientData) bottom) == NILLNODE) { - Lst_AtEnd(slst, (ClientData) bottom); + if (Lst_Member(slst, bottom) == NULL) { + Lst_AtEnd(slst, bottom); } bottom = bottom->parent; } @@ -2086,13 +2299,13 @@ sfnd_abort: * 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. + * Etc. */ - if (bottom->node == NILGNODE) { + if (bottom->node == NULL) { bottom->node = Targ_FindNode(bottom->file, TARG_CREATE); } - - for (src = bottom; src->parent != (Src *)NULL; src = src->parent) { + + for (src = bottom; src->parent != NULL; src = src->parent) { targ = src->parent; if (src->node->suffix) @@ -2100,7 +2313,7 @@ sfnd_abort: src->node->suffix = src->suff; src->node->suffix->refCount++; - if (targ->node == NILGNODE) { + if (targ->node == NULL) { targ->node = Targ_FindNode(targ->file, TARG_CREATE); } @@ -2119,9 +2332,9 @@ sfnd_abort: */ targ->node->type |= OP_DEPS_FOUND; - Var_Set(PREFIX, targ->pref, targ->node); - - Var_Set(TARGET, targ->node->name, targ->node); + Var_Set(PREFIX, targ->pref, targ->node, 0); + + Var_Set(TARGET, targ->node->name, targ->node, 0); } } @@ -2130,21 +2343,14 @@ sfnd_abort: gn->suffix = src->suff; gn->suffix->refCount++; - /* - * So Dir_MTime doesn't go questing for it... - */ - if (gn->path) - free(gn->path); - gn->path = strdup(gn->name); - /* * Nuke the transformation path and the Src structures left over in the * two lists. */ sfnd_return: if (bottom) - if (Lst_Member(slst, (ClientData) bottom) == NILLNODE) - Lst_AtEnd(slst, (ClientData) bottom); + if (Lst_Member(slst, bottom) == NULL) + Lst_AtEnd(slst, bottom); while (SuffRemoveSrc(srcs) || SuffRemoveSrc(targs)) continue; @@ -2152,8 +2358,8 @@ sfnd_return: Lst_Concat(slst, srcs, LST_CONCLINK); Lst_Concat(slst, targs, LST_CONCLINK); } - - + + /*- *----------------------------------------------------------------------- * Suff_FindDeps -- @@ -2183,20 +2389,22 @@ sfnd_return: */ void -Suff_FindDeps(gn) - GNode *gn; +Suff_FindDeps(GNode *gn) { - + SuffFindDeps(gn, srclist); while (SuffRemoveSrc(srclist)) continue; } +/* + * Input: + * gn node we're dealing with + * + */ static void -SuffFindDeps (gn, slst) - GNode *gn; /* node we're dealing with */ - Lst slst; +SuffFindDeps(GNode *gn, Lst slst) { if (gn->type & OP_DEPS_FOUND) { /* @@ -2206,11 +2414,21 @@ SuffFindDeps (gn, slst) } else { gn->type |= OP_DEPS_FOUND; } - - if (DEBUG(SUFF)) { - printf ("SuffFindDeps (%s)\n", gn->name); + /* + * 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) { @@ -2224,24 +2442,24 @@ SuffFindDeps (gn, slst) */ LstNode ln; Suff *s; - - ln = Lst_Find (sufflist, (ClientData)LIBSUFF, SuffSuffHasNameP); + + ln = Lst_Find(sufflist, LIBSUFF, SuffSuffHasNameP); if (gn->suffix) gn->suffix->refCount--; - if (ln != NILLNODE) { - gn->suffix = s = (Suff *) Lst_Datum (ln); + if (ln != NULL) { + gn->suffix = s = (Suff *)Lst_Datum(ln); gn->suffix->refCount++; - Arch_FindLib (gn, s->searchPath); + Arch_FindLib(gn, s->searchPath); } else { gn->suffix = NULL; - Var_Set (TARGET, gn->name, gn); + 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); + Var_Set(PREFIX, "", gn, 0); } else { SuffFindNormalDeps(gn, slst); } @@ -2252,6 +2470,9 @@ SuffFindDeps (gn, slst) * Suff_SetNull -- * Define which suffix is the null suffix. * + * Input: + * name Name of null suffix + * * Results: * None. * @@ -2265,16 +2486,15 @@ SuffFindDeps (gn, slst) *----------------------------------------------------------------------- */ void -Suff_SetNull(name) - char *name; /* Name of null suffix */ +Suff_SetNull(char *name) { Suff *s; LstNode ln; - ln = Lst_Find(sufflist, (ClientData)name, SuffSuffHasNameP); - if (ln != NILLNODE) { + ln = Lst_Find(sufflist, name, SuffSuffHasNameP); + if (ln != NULL) { s = (Suff *)Lst_Datum(ln); - if (suffNull != (Suff *)NULL) { + if (suffNull != NULL) { suffNull->flags &= ~SUFF_NULL; } s->flags |= SUFF_NULL; @@ -2283,7 +2503,7 @@ Suff_SetNull(name) */ suffNull = s; } else { - Parse_Error (PARSE_WARNING, "Desired null suffix %s not defined.", + Parse_Error(PARSE_WARNING, "Desired null suffix %s not defined.", name); } } @@ -2301,12 +2521,14 @@ Suff_SetNull(name) *----------------------------------------------------------------------- */ void -Suff_Init () +Suff_Init(void) { - sufflist = Lst_Init (FALSE); + sufflist = Lst_Init(FALSE); +#ifdef CLEANUP suffClean = Lst_Init(FALSE); - srclist = Lst_Init (FALSE); - transforms = Lst_Init (FALSE); +#endif + srclist = Lst_Init(FALSE); + transforms = Lst_Init(FALSE); sNum = 0; /* @@ -2314,15 +2536,15 @@ Suff_Init () * actually go on the suffix list or everyone will think that's its * suffix. */ - emptySuff = suffNull = (Suff *) emalloc (sizeof (Suff)); + emptySuff = suffNull = bmake_malloc(sizeof(Suff)); - suffNull->name = strdup (""); + suffNull->name = bmake_strdup(""); suffNull->nameLen = 0; - suffNull->searchPath = Lst_Init (FALSE); + 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->children = Lst_Init(FALSE); + suffNull->parents = Lst_Init(FALSE); + suffNull->ref = Lst_Init(FALSE); suffNull->sNum = sNum++; suffNull->flags = SUFF_NULL; suffNull->refCount = 1; @@ -2344,92 +2566,88 @@ Suff_Init () */ void -Suff_End() +Suff_End(void) { +#ifdef CLEANUP Lst_Destroy(sufflist, SuffFree); Lst_Destroy(suffClean, SuffFree); if (suffNull) SuffFree(suffNull); - Lst_Destroy(srclist, NOFREE); - Lst_Destroy(transforms, NOFREE); + Lst_Destroy(srclist, NULL); + Lst_Destroy(transforms, NULL); +#endif } /********************* DEBUGGING FUNCTIONS **********************/ -static int SuffPrintName(s, dummy) - ClientData s; - ClientData dummy; +static int SuffPrintName(void *s, void *dummy) { - printf ("%s ", ((Suff *) s)->name); + fprintf(debug_file, "%s ", ((Suff *)s)->name); return (dummy ? 0 : 0); } static int -SuffPrintSuff (sp, dummy) - ClientData sp; - ClientData dummy; +SuffPrintSuff(void *sp, void *dummy) { - Suff *s = (Suff *) sp; + Suff *s = (Suff *)sp; int flags; int flag; - printf ("# `%s' [%d] ", s->name, s->refCount); - + fprintf(debug_file, "# `%s' [%d] ", s->name, s->refCount); + flags = s->flags; if (flags) { - fputs (" (", stdout); + fputs(" (", debug_file); while (flags) { flag = 1 << (ffs(flags) - 1); flags &= ~flag; switch (flag) { case SUFF_NULL: - printf ("NULL"); + fprintf(debug_file, "NULL"); break; case SUFF_INCLUDE: - printf ("INCLUDE"); + fprintf(debug_file, "INCLUDE"); break; case SUFF_LIBRARY: - printf ("LIBRARY"); + fprintf(debug_file, "LIBRARY"); break; } - fputc(flags ? '|' : ')', stdout); + fputc(flags ? '|' : ')', debug_file); } } - fputc ('\n', stdout); - printf ("#\tTo: "); - Lst_ForEach (s->parents, SuffPrintName, (ClientData)0); - fputc ('\n', stdout); - printf ("#\tFrom: "); - Lst_ForEach (s->children, SuffPrintName, (ClientData)0); - fputc ('\n', stdout); - printf ("#\tSearch Path: "); - Dir_PrintPath (s->searchPath); - fputc ('\n', stdout); + 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 (tp, dummy) - ClientData tp; - ClientData dummy; +SuffPrintTrans(void *tp, void *dummy) { - GNode *t = (GNode *) tp; + GNode *t = (GNode *)tp; - printf ("%-16s: ", t->name); - Targ_PrintType (t->type); - fputc ('\n', stdout); - Lst_ForEach (t->commands, Targ_PrintCmd, (ClientData)0); - fputc ('\n', stdout); + 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() +Suff_PrintAll(void) { - printf ("#*** Suffixes:\n"); - Lst_ForEach (sufflist, SuffPrintSuff, (ClientData)0); + fprintf(debug_file, "#*** Suffixes:\n"); + Lst_ForEach(sufflist, SuffPrintSuff, NULL); - printf ("#*** Transformations:\n"); - Lst_ForEach (transforms, SuffPrintTrans, (ClientData)0); + fprintf(debug_file, "#*** Transformations:\n"); + Lst_ForEach(transforms, SuffPrintTrans, NULL); } diff --git a/targ.c b/targ.c new file mode 100644 index 000000000000..116eb7c65d3c --- /dev/null +++ b/targ.c @@ -0,0 +1,848 @@ +/* $NetBSD: targ.c,v 1.56 2010/11/25 21:31:09 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. + */ + +/* + * 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.56 2010/11/25 21:31:09 christos Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)targ.c 8.2 (Berkeley) 3/19/94"; +#else +__RCSID("$NetBSD: targ.c,v 1.56 2010/11/25 21:31:09 christos 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 +#include + +#include "make.h" +#include "hash.h" +#include "dir.h" + +static Lst allTargets; /* the list of all targets found so far */ +#ifdef CLEANUP +static Lst allGNs; /* List of all the GNodes */ +#endif +static Hash_Table targets; /* a hash table of same */ + +#define HTSIZE 191 /* initial size of hash table */ + +static int TargPrintOnlySrc(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 __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 __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 __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/trace.c b/trace.c new file mode 100644 index 000000000000..267177ff5618 --- /dev/null +++ b/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 +#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 + +#include +#include + +#include "make.h" +#include "job.h" +#include "trace.h" + +static FILE *trfile; +static pid_t trpid; +char *trwd; + +static const char *evname[] = { + "BEG", + "END", + "ERR", + "JOB", + "DON", + "INT", +}; + +void +Trace_Init(const char *pathname) +{ + char *p1; + if (pathname != NULL) { + trpid = getpid(); + trwd = Var_Value(".CURDIR", VAR_GLOBAL, &p1); + + trfile = fopen(pathname, "a"); + } +} + +void +Trace_Log(TrEvent event, Job *job) +{ + struct timeval rightnow; + + if (trfile == NULL) + return; + + gettimeofday(&rightnow, NULL); + + fprintf(trfile, "%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/trace.h b/trace.h new file mode 100644 index 000000000000..dc0fc6cc43aa --- /dev/null +++ b/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/unit-tests/Makefile.in b/unit-tests/Makefile.in new file mode 100644 index 000000000000..c0d94c38b1e4 --- /dev/null +++ b/unit-tests/Makefile.in @@ -0,0 +1,95 @@ +# $Id: Makefile.in,v 1.37 2011/10/01 20:30:30 sjg Exp $ +# +# $NetBSD: Makefile,v 1.33 2011/09/29 23:38:04 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 \ + 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 + +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/unit-tests/comment b/unit-tests/comment new file mode 100644 index 000000000000..7dd7dbbe28b5 --- /dev/null +++ b/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/unit-tests/cond1 b/unit-tests/cond1 new file mode 100644 index 000000000000..c877c3db31b8 --- /dev/null +++ b/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/unit-tests/doterror b/unit-tests/doterror new file mode 100644 index 000000000000..75d8920ac7e7 --- /dev/null +++ b/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/unit-tests/dotwait b/unit-tests/dotwait new file mode 100644 index 000000000000..43706afe64b8 --- /dev/null +++ b/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/unit-tests/error b/unit-tests/error new file mode 100644 index 000000000000..c0a1403a4008 --- /dev/null +++ b/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/unit-tests/export b/unit-tests/export new file mode 100644 index 000000000000..3e2ad9537f67 --- /dev/null +++ b/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/unit-tests/export-all b/unit-tests/export-all new file mode 100644 index 000000000000..a243fe3e35f7 --- /dev/null +++ b/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/unit-tests/forsubst b/unit-tests/forsubst new file mode 100644 index 000000000000..d3a7de1b38da --- /dev/null +++ b/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/unit-tests/hash b/unit-tests/hash new file mode 100644 index 000000000000..1ed84e776d09 --- /dev/null +++ b/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/unit-tests/misc b/unit-tests/misc new file mode 100644 index 000000000000..4ba3655f4fa0 --- /dev/null +++ b/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/unit-tests/moderrs b/unit-tests/moderrs new file mode 100644 index 000000000000..b8f78ce3836b --- /dev/null +++ b/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/unit-tests/modmatch b/unit-tests/modmatch new file mode 100644 index 000000000000..48a1befb58b6 --- /dev/null +++ b/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/unit-tests/modmisc b/unit-tests/modmisc new file mode 100644 index 000000000000..d562e46a5487 --- /dev/null +++ b/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/unit-tests/modorder b/unit-tests/modorder new file mode 100644 index 000000000000..68b66fb6226d --- /dev/null +++ b/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/unit-tests/modts b/unit-tests/modts new file mode 100644 index 000000000000..616bd8944f21 --- /dev/null +++ b/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/unit-tests/modword b/unit-tests/modword new file mode 100644 index 000000000000..39355d7bacfa --- /dev/null +++ b/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/unit-tests/phony-end b/unit-tests/phony-end new file mode 100644 index 000000000000..d61884cafc44 --- /dev/null +++ b/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/unit-tests/posix b/unit-tests/posix new file mode 100644 index 000000000000..48ed7a3cacd7 --- /dev/null +++ b/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/unit-tests/qequals b/unit-tests/qequals new file mode 100644 index 000000000000..e23078e5fd98 --- /dev/null +++ b/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/unit-tests/sysv b/unit-tests/sysv new file mode 100644 index 000000000000..9eedacba3b67 --- /dev/null +++ b/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/unit-tests/ternary b/unit-tests/ternary new file mode 100644 index 000000000000..77f834981c6c --- /dev/null +++ b/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/unit-tests/test.exp b/unit-tests/test.exp new file mode 100644 index 000000000000..cbb2912a081e --- /dev/null +++ b/unit-tests/test.exp @@ -0,0 +1,351 @@ +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 +.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=fu FOO=foo VAR= +two FU=bar FOO=goo VAR= +three FU=bar FOO=goo VAR= +four FU=bar FOO=goo VAR=Internal +five FU=bar FOO=goo VAR=Internal +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/unit-tests/unexport b/unit-tests/unexport new file mode 100644 index 000000000000..fb40d0ccf742 --- /dev/null +++ b/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/unit-tests/unexport-env b/unit-tests/unexport-env new file mode 100644 index 000000000000..f6a2ff998268 --- /dev/null +++ b/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/unit-tests/varcmd b/unit-tests/varcmd new file mode 100644 index 000000000000..a58e01430264 --- /dev/null +++ b/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=${FU} FOO=${FOO} VAR=${VAR}" + +one: show + @${.MAKE} -f ${MAKEFILE} FU=bar FOO=goo two + +two: show + @${.MAKE} -f ${MAKEFILE} three + +three: show + @${.MAKE} -f ${MAKEFILE} four + + +.ifmake four +VAR=Internal +.MAKEOVERRIDES+= VAR +.endif + +four: show + @${.MAKE} -f ${MAKEFILE} 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/usr.bin/make/Makefile b/usr.bin/make/Makefile deleted file mode 100644 index b6ae9fc12691..000000000000 --- a/usr.bin/make/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -# from: @(#)Makefile 5.2 (Berkeley) 12/28/90 -# $Id: Makefile,v 1.6 1994/06/30 05:33:39 cgd Exp $ - -PROG= make -CFLAGS+= -I${.CURDIR} -DPOSIX -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 var.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 -.PATH: ${.CURDIR}/lst.lib - -.if make(install) -SUBDIR+= PSD.doc -.endif - -.include diff --git a/usr.bin/make/Makefile.boot b/usr.bin/make/Makefile.boot deleted file mode 100644 index b8e8001096bf..000000000000 --- a/usr.bin/make/Makefile.boot +++ /dev/null @@ -1,36 +0,0 @@ -# $NetBSD: Makefile.boot,v 1.7 1996/08/30 17:59:37 thorpej Exp $ -# -# a very simple makefile... -# -# You only want to use this if you aren't running NetBSD. -# -# modify MACHINE and MACHINE_ARCH as appropriate for your target architecture -# -MACHINE=sun -MACHINE_ARCH=sparc -CFLAGS= -I. -DMACHINE=\"${MACHINE}\" -DMACHINE_ARCH=\"${MACHINE_ARCH}\" \ - -DMAKE_BOOTSTRAP -LIBS= - -OBJ=arch.o buf.o compat.o cond.o dir.o for.o hash.o job.o main.o make.o \ - parse.o str.o suff.o targ.o var.o util.o - -LIBOBJ= lst.lib/lstAppend.o lst.lib/lstAtEnd.o lst.lib/lstAtFront.o \ - lst.lib/lstClose.o lst.lib/lstConcat.o lst.lib/lstDatum.o \ - lst.lib/lstDeQueue.o lst.lib/lstDestroy.o lst.lib/lstDupl.o \ - lst.lib/lstEnQueue.o lst.lib/lstFind.o lst.lib/lstFindFrom.o \ - lst.lib/lstFirst.o lst.lib/lstForEach.o lst.lib/lstForEachFrom.o \ - lst.lib/lstInit.o lst.lib/lstInsert.o lst.lib/lstIsAtEnd.o \ - lst.lib/lstIsEmpty.o lst.lib/lstLast.o lst.lib/lstMember.o \ - lst.lib/lstNext.o lst.lib/lstOpen.o lst.lib/lstRemove.o \ - lst.lib/lstReplace.o lst.lib/lstSucc.o - -bmake: ${OBJ} ${LIBOBJ} -# @echo 'make of make and make.0 started.' - ${CC} ${CFLAGS} ${OBJ} ${LIBOBJ} -o bmake ${LIBS} - @ls -l $@ -# nroff -h -man make.1 > make.0 -# @echo 'make of make and make.0 completed.' - -clean: - rm -f ${OBJ} ${LIBOBJ} ${PORTOBJ} bmake diff --git a/usr.bin/make/bit.h b/usr.bin/make/bit.h deleted file mode 100644 index 53b3745a2515..000000000000 --- a/usr.bin/make/bit.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)bit.h 8.2 (Berkeley) 4/28/95 - */ - -/* - * bit.h -- - * - * Definition of macros for setting and clearing bits in an array - * of integers. - * - * It is assumed that "int" is 32 bits wide. - */ - -#ifndef _BIT -#define _BIT - -#include "sprite.h" - -#define BIT_NUM_BITS_PER_INT 32 -#define BIT_NUM_BITS_PER_BYTE 8 - -#define Bit_NumInts(numBits) \ - (((numBits)+BIT_NUM_BITS_PER_INT -1)/BIT_NUM_BITS_PER_INT) - -#define Bit_NumBytes(numBits) \ - (Bit_NumInts(numBits) * sizeof(int)) - -#define Bit_Alloc(numBits, bitArrayPtr) \ - bitArrayPtr = (int *)malloc((unsigned)Bit_NumBytes(numBits)); \ - Bit_Zero((numBits), (bitArrayPtr)) - -#define Bit_Free(bitArrayPtr) \ - free((char *)bitArrayPtr) - -#define Bit_Set(numBits, bitArrayPtr) \ - ((bitArrayPtr)[(numBits)/BIT_NUM_BITS_PER_INT] |= \ - (1 << ((numBits) % BIT_NUM_BITS_PER_INT))) - -#define Bit_IsSet(numBits, bitArrayPtr) \ - ((bitArrayPtr)[(numBits)/BIT_NUM_BITS_PER_INT] & \ - (1 << ((numBits) % BIT_NUM_BITS_PER_INT))) - -#define Bit_Clear(numBits, bitArrayPtr) \ - ((bitArrayPtr)[(numBits)/BIT_NUM_BITS_PER_INT] &= \ - ~(1 << ((numBits) % BIT_NUM_BITS_PER_INT))) - -#define Bit_IsClear(numBits, bitArrayPtr) \ - (!(Bit_IsSet((numBits), (bitArrayPtr)))) - -#define Bit_Copy(numBits, srcArrayPtr, destArrayPtr) \ - bcopy((char *)(srcArrayPtr), (char *)(destArrayPtr), \ - Bit_NumBytes(numBits)) - -#define Bit_Zero(numBits, bitArrayPtr) \ - bzero((char *)(bitArrayPtr), Bit_NumBytes(numBits)) - -extern int Bit_FindFirstSet(); -extern int Bit_FindFirstClear(); -extern Boolean Bit_Intersect(); -extern Boolean Bit_Union(); -extern Boolean Bit_AnySet(); -extern int *Bit_Expand(); - -#endif /* _BIT */ diff --git a/usr.bin/make/buf.c b/usr.bin/make/buf.c deleted file mode 100644 index 68a534db14bc..000000000000 --- a/usr.bin/make/buf.c +++ /dev/null @@ -1,436 +0,0 @@ -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char sccsid[] = "@(#)buf.c 8.2 (Berkeley) 4/28/95"; -#endif /* not lint */ - -/*- - * buf.c -- - * Functions for automatically-expanded buffers. - */ - -#include "sprite.h" -#include "make.h" -#include "buf.h" - -#ifndef max -#define max(a,b) ((a) > (b) ? (a) : (b)) -#endif - -/* - * BufExpand -- - * Expand the given buffer to hold the given number of additional - * bytes. - * Makes sure there's room for an extra NULL byte at the end of the - * buffer in case it holds a string. - */ -#define BufExpand(bp,nb) \ - if (bp->left < (nb)+1) {\ - int newSize = (bp)->size + max((nb)+1,BUF_ADD_INC); \ - Byte *newBuf = (Byte *) realloc((bp)->buffer, newSize); \ - \ - (bp)->inPtr = newBuf + ((bp)->inPtr - (bp)->buffer); \ - (bp)->outPtr = newBuf + ((bp)->outPtr - (bp)->buffer);\ - (bp)->buffer = newBuf;\ - (bp)->size = newSize;\ - (bp)->left = newSize - ((bp)->inPtr - (bp)->buffer);\ - } - -#define BUF_DEF_SIZE 256 /* Default buffer size */ -#define BUF_ADD_INC 256 /* Expansion increment when Adding */ -#define BUF_UNGET_INC 16 /* Expansion increment when Ungetting */ - -/*- - *----------------------------------------------------------------------- - * Buf_OvAddByte -- - * Add a single byte to the buffer. left is zero or negative. - * - * Results: - * None. - * - * Side Effects: - * The buffer may be expanded. - * - *----------------------------------------------------------------------- - */ -void -Buf_OvAddByte (bp, byte) - register Buffer bp; - int byte; -{ - int nbytes = 1; - bp->left = 0; - BufExpand (bp, nbytes); - - *bp->inPtr++ = byte; - bp->left--; - - /* - * Null-terminate - */ - *bp->inPtr = 0; -} - -/*- - *----------------------------------------------------------------------- - * Buf_AddBytes -- - * Add a number of bytes to the buffer. - * - * Results: - * None. - * - * Side Effects: - * Guess what? - * - *----------------------------------------------------------------------- - */ -void -Buf_AddBytes (bp, numBytes, bytesPtr) - register Buffer bp; - int numBytes; - Byte *bytesPtr; -{ - - BufExpand (bp, numBytes); - - memcpy (bp->inPtr, bytesPtr, numBytes); - bp->inPtr += numBytes; - bp->left -= numBytes; - - /* - * Null-terminate - */ - *bp->inPtr = 0; -} - -/*- - *----------------------------------------------------------------------- - * Buf_UngetByte -- - * Place the byte back at the beginning of the buffer. - * - * Results: - * SUCCESS if the byte was added ok. FAILURE if not. - * - * Side Effects: - * The byte is stuffed in the buffer and outPtr is decremented. - * - *----------------------------------------------------------------------- - */ -void -Buf_UngetByte (bp, byte) - register Buffer bp; - int byte; -{ - - if (bp->outPtr != bp->buffer) { - bp->outPtr--; - *bp->outPtr = byte; - } else if (bp->outPtr == bp->inPtr) { - *bp->inPtr = byte; - bp->inPtr++; - bp->left--; - *bp->inPtr = 0; - } else { - /* - * Yech. have to expand the buffer to stuff this thing in. - * We use a different expansion constant because people don't - * usually push back many bytes when they're doing it a byte at - * a time... - */ - int numBytes = bp->inPtr - bp->outPtr; - Byte *newBuf; - - newBuf = (Byte *)emalloc(bp->size + BUF_UNGET_INC); - memcpy ((char *)(newBuf+BUF_UNGET_INC), (char *)bp->outPtr, numBytes+1); - bp->outPtr = newBuf + BUF_UNGET_INC; - bp->inPtr = bp->outPtr + numBytes; - free ((char *)bp->buffer); - bp->buffer = newBuf; - bp->size += BUF_UNGET_INC; - bp->left = bp->size - (bp->inPtr - bp->buffer); - bp->outPtr -= 1; - *bp->outPtr = byte; - } -} - -/*- - *----------------------------------------------------------------------- - * Buf_UngetBytes -- - * Push back a series of bytes at the beginning of the buffer. - * - * Results: - * None. - * - * Side Effects: - * outPtr is decremented and the bytes copied into the buffer. - * - *----------------------------------------------------------------------- - */ -void -Buf_UngetBytes (bp, numBytes, bytesPtr) - register Buffer bp; - int numBytes; - Byte *bytesPtr; -{ - - if (bp->outPtr - bp->buffer >= numBytes) { - bp->outPtr -= numBytes; - memcpy (bp->outPtr, bytesPtr, numBytes); - } else if (bp->outPtr == bp->inPtr) { - Buf_AddBytes (bp, numBytes, bytesPtr); - } else { - int curNumBytes = bp->inPtr - bp->outPtr; - Byte *newBuf; - int newBytes = max(numBytes,BUF_UNGET_INC); - - newBuf = (Byte *)emalloc (bp->size + newBytes); - memcpy((char *)(newBuf+newBytes), (char *)bp->outPtr, curNumBytes+1); - bp->outPtr = newBuf + newBytes; - bp->inPtr = bp->outPtr + curNumBytes; - free ((char *)bp->buffer); - bp->buffer = newBuf; - bp->size += newBytes; - bp->left = bp->size - (bp->inPtr - bp->buffer); - bp->outPtr -= numBytes; - memcpy ((char *)bp->outPtr, (char *)bytesPtr, numBytes); - } -} - -/*- - *----------------------------------------------------------------------- - * Buf_GetByte -- - * Return the next byte from the buffer. Actually returns an integer. - * - * Results: - * Returns BUF_ERROR if there's no byte in the buffer, or the byte - * itself if there is one. - * - * Side Effects: - * outPtr is incremented and both outPtr and inPtr will be reset if - * the buffer is emptied. - * - *----------------------------------------------------------------------- - */ -int -Buf_GetByte (bp) - register Buffer bp; -{ - int res; - - if (bp->inPtr == bp->outPtr) { - return (BUF_ERROR); - } else { - res = (int) *bp->outPtr; - bp->outPtr += 1; - if (bp->outPtr == bp->inPtr) { - bp->outPtr = bp->inPtr = bp->buffer; - bp->left = bp->size; - *bp->inPtr = 0; - } - return (res); - } -} - -/*- - *----------------------------------------------------------------------- - * Buf_GetBytes -- - * Extract a number of bytes from the buffer. - * - * Results: - * The number of bytes gotten. - * - * Side Effects: - * The passed array is overwritten. - * - *----------------------------------------------------------------------- - */ -int -Buf_GetBytes (bp, numBytes, bytesPtr) - register Buffer bp; - int numBytes; - Byte *bytesPtr; -{ - - if (bp->inPtr - bp->outPtr < numBytes) { - numBytes = bp->inPtr - bp->outPtr; - } - memcpy (bytesPtr, bp->outPtr, numBytes); - bp->outPtr += numBytes; - - if (bp->outPtr == bp->inPtr) { - bp->outPtr = bp->inPtr = bp->buffer; - bp->left = bp->size; - *bp->inPtr = 0; - } - return (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 (bp, numBytesPtr) - register Buffer bp; - int *numBytesPtr; -{ - - if (numBytesPtr != (int *)NULL) { - *numBytesPtr = bp->inPtr - bp->outPtr; - } - - return (bp->outPtr); -} - -/*- - *----------------------------------------------------------------------- - * Buf_Discard -- - * Throw away bytes in a buffer. - * - * Results: - * None. - * - * Side Effects: - * The bytes are discarded. - * - *----------------------------------------------------------------------- - */ -void -Buf_Discard (bp, numBytes) - register Buffer bp; - int numBytes; -{ - - if (bp->inPtr - bp->outPtr <= numBytes) { - bp->inPtr = bp->outPtr = bp->buffer; - bp->left = bp->size; - *bp->inPtr = 0; - } else { - bp->outPtr += numBytes; - } -} - -/*- - *----------------------------------------------------------------------- - * Buf_Size -- - * Returns the number of bytes in the given buffer. Doesn't include - * the null-terminating byte. - * - * Results: - * The number of bytes. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -int -Buf_Size (buf) - Buffer buf; -{ - return (buf->inPtr - buf->outPtr); -} - -/*- - *----------------------------------------------------------------------- - * Buf_Init -- - * Initialize a buffer. If no initial size is given, a reasonable - * default is used. - * - * Results: - * A buffer to be given to other functions in this library. - * - * Side Effects: - * The buffer is created, the space allocated and pointers - * initialized. - * - *----------------------------------------------------------------------- - */ -Buffer -Buf_Init (size) - int size; /* Initial size for the buffer */ -{ - Buffer bp; /* New Buffer */ - - bp = (Buffer)emalloc(sizeof(*bp)); - - if (size <= 0) { - size = BUF_DEF_SIZE; - } - bp->left = bp->size = size; - bp->buffer = (Byte *)emalloc(size); - bp->inPtr = bp->outPtr = bp->buffer; - *bp->inPtr = 0; - - return (bp); -} - -/*- - *----------------------------------------------------------------------- - * Buf_Destroy -- - * Nuke a buffer and all its resources. - * - * Results: - * None. - * - * Side Effects: - * The buffer is freed. - * - *----------------------------------------------------------------------- - */ -void -Buf_Destroy (buf, freeData) - Buffer buf; /* Buffer to destroy */ - Boolean freeData; /* TRUE if the data should be destroyed as well */ -{ - - if (freeData) { - free ((char *)buf->buffer); - } - free ((char *)buf); -} diff --git a/usr.bin/make/buf.h b/usr.bin/make/buf.h deleted file mode 100644 index 33313b8ff0ea..000000000000 --- a/usr.bin/make/buf.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)buf.h 8.2 (Berkeley) 4/28/95 - */ - -/*- - * buf.h -- - * Header for users of the buf library. - */ - -#ifndef _BUF_H -#define _BUF_H - -#include "sprite.h" - -typedef char Byte; - -typedef struct Buffer { - int size; /* Current size of the buffer */ - int left; /* Space left (== size - (inPtr - buffer)) */ - Byte *buffer; /* The buffer itself */ - Byte *inPtr; /* Place to write to */ - Byte *outPtr; /* Place to read from */ -} *Buffer; - -/* Buf_AddByte adds a single byte to a buffer. */ -#define Buf_AddByte(bp, byte) \ - (void) (--(bp)->left <= 0 ? Buf_OvAddByte(bp, byte), 1 : \ - (*(bp)->inPtr++ = (byte), *(bp)->inPtr = 0), 1) - -#define BUF_ERROR 256 - -void Buf_OvAddByte __P((Buffer, int)); -void Buf_AddBytes __P((Buffer, int, Byte *)); -void Buf_UngetByte __P((Buffer, int)); -void Buf_UngetBytes __P((Buffer, int, Byte *)); -int Buf_GetByte __P((Buffer)); -int Buf_GetBytes __P((Buffer, int, Byte *)); -Byte *Buf_GetAll __P((Buffer, int *)); -void Buf_Discard __P((Buffer, int)); -int Buf_Size __P((Buffer)); -Buffer Buf_Init __P((int)); -void Buf_Destroy __P((Buffer, Boolean)); - -#endif /* _BUF_H */ diff --git a/usr.bin/make/cond.c b/usr.bin/make/cond.c deleted file mode 100644 index 6ea0d6a3d476..000000000000 --- a/usr.bin/make/cond.c +++ /dev/null @@ -1,1252 +0,0 @@ -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char sccsid[] = "@(#)cond.c 8.3 (Berkeley) 4/28/95"; -#endif /* not lint */ - -/*- - * cond.c -- - * Functions to handle conditionals in a makefile. - * - * Interface: - * Cond_Eval Evaluate the conditional in the passed line. - * - */ - -#include -#include -#include "make.h" -#include "hash.h" -#include "dir.h" -#include "buf.h" - -/* - * The parsing of conditional expressions is based on this grammar: - * E -> F || E - * E -> F - * F -> T && F - * F -> T - * T -> defined(variable) - * T -> make(target) - * T -> exists(file) - * T -> empty(varspec) - * T -> target(name) - * T -> symbol - * T -> $(varspec) op value - * T -> $(varspec) == "string" - * T -> $(varspec) != "string" - * T -> ( E ) - * T -> ! T - * op -> == | != | > | < | >= | <= - * - * 'symbol' is some other symbol to which the default function (condDefProc) - * is applied. - * - * Tokens are scanned from the 'condExpr' string. The scanner (CondToken) - * will return And for '&' and '&&', Or for '|' and '||', Not for '!', - * LParen for '(', RParen for ')' and will evaluate the other terminal - * symbols, using either the default function or the function given in the - * terminal, and return the result as either True or False. - * - * All Non-Terminal functions (CondE, CondF and CondT) return Err on error. - */ -typedef enum { - And, Or, Not, True, False, LParen, RParen, EndOfFile, None, Err -} Token; - -/*- - * Structures to handle elegantly the different forms of #if's. The - * last two fields are stored in condInvert and condDefProc, respectively. - */ -static int CondGetArg __P((char **, char **, char *, Boolean)); -static Boolean CondDoDefined __P((int, char *)); -static int CondStrMatch __P((ClientData, ClientData)); -static Boolean CondDoMake __P((int, char *)); -static Boolean CondDoExists __P((int, char *)); -static Boolean CondDoTarget __P((int, char *)); -static Boolean CondCvtArg __P((char *, double *)); -static Token CondToken __P((Boolean)); -static Token CondT __P((Boolean)); -static Token CondF __P((Boolean)); -static Token CondE __P((Boolean)); - -static struct If { - char *form; /* Form of if */ - int formlen; /* Length of form */ - Boolean doNot; /* TRUE if default function should be negated */ - Boolean (*defProc)(); /* Default function to apply */ -} ifs[] = { - { "ifdef", 5, FALSE, CondDoDefined }, - { "ifndef", 6, TRUE, CondDoDefined }, - { "ifmake", 6, FALSE, CondDoMake }, - { "ifnmake", 7, TRUE, CondDoMake }, - { "if", 2, FALSE, CondDoDefined }, - { (char *)0, 0, FALSE, (Boolean (*)())0 } -}; - -static Boolean condInvert; /* Invert the default function */ -static Boolean (*condDefProc)(); /* Default function to apply */ -static char *condExpr; /* The expression to parse */ -static Token condPushBack=None; /* Single push-back token used in - * parsing */ - -#define MAXIF 30 /* greatest depth of #if'ing */ - -static Boolean condStack[MAXIF]; /* Stack of conditionals's values */ -static int condTop = MAXIF; /* Top-most conditional */ -static int skipIfLevel=0; /* Depth of skipped conditionals */ -static Boolean skipLine = FALSE; /* Whether the parse module is skipping - * lines */ - -/*- - *----------------------------------------------------------------------- - * CondPushBack -- - * Push back the most recent token read. We only need one level of - * this, so the thing is just stored in 'condPushback'. - * - * Results: - * None. - * - * Side Effects: - * condPushback is overwritten. - * - *----------------------------------------------------------------------- - */ -static void -CondPushBack (t) - Token t; /* Token to push back into the "stream" */ -{ - condPushBack = t; -} - -/*- - *----------------------------------------------------------------------- - * CondGetArg -- - * Find the argument of a built-in function. - * - * Results: - * The length of the argument and the address of the argument. - * - * Side Effects: - * The pointer is set to point to the closing parenthesis of the - * function call. - * - *----------------------------------------------------------------------- - */ -static int -CondGetArg (linePtr, argPtr, func, parens) - char **linePtr; - char **argPtr; - char *func; - Boolean parens; /* TRUE if arg should be bounded by parens */ -{ - register char *cp; - int argLen; - register Buffer buf; - - cp = *linePtr; - if (parens) { - while (*cp != '(' && *cp != '\0') { - cp++; - } - if (*cp == '(') { - cp++; - } - } - - if (*cp == '\0') { - /* - * No arguments whatsoever. Because 'make' and 'defined' aren't really - * "reserved words", we don't print a message. I think this is better - * than hitting the user with a warning message every time s/he uses - * the word 'make' or 'defined' at the beginning of a symbol... - */ - *argPtr = cp; - return (0); - } - - while (*cp == ' ' || *cp == '\t') { - cp++; - } - - /* - * Create a buffer for the argument and start it out at 16 characters - * long. Why 16? Why not? - */ - buf = Buf_Init(16); - - while ((strchr(" \t)&|", *cp) == (char *)NULL) && (*cp != '\0')) { - if (*cp == '$') { - /* - * Parse the variable spec and install it as part of the argument - * if it's valid. We tell Var_Parse to complain on an undefined - * variable, so we don't do it too. Nor do we return an error, - * though perhaps we should... - */ - char *cp2; - int len; - Boolean doFree; - - cp2 = Var_Parse(cp, VAR_CMD, TRUE, &len, &doFree); - - Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2); - if (doFree) { - free(cp2); - } - cp += len; - } else { - Buf_AddByte(buf, (Byte)*cp); - cp++; - } - } - - Buf_AddByte(buf, (Byte)'\0'); - *argPtr = (char *)Buf_GetAll(buf, &argLen); - Buf_Destroy(buf, FALSE); - - while (*cp == ' ' || *cp == '\t') { - cp++; - } - if (parens && *cp != ')') { - Parse_Error (PARSE_WARNING, "Missing closing parenthesis for %s()", - func); - return (0); - } else if (parens) { - /* - * Advance pointer past close parenthesis. - */ - cp++; - } - - *linePtr = cp; - return (argLen); -} - -/*- - *----------------------------------------------------------------------- - * CondDoDefined -- - * Handle the 'defined' function for conditionals. - * - * Results: - * TRUE if the given variable is defined. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -static Boolean -CondDoDefined (argLen, arg) - int argLen; - char *arg; -{ - char savec = arg[argLen]; - char *p1; - Boolean result; - - arg[argLen] = '\0'; - if (Var_Value (arg, VAR_CMD, &p1) != (char *)NULL) { - result = TRUE; - } else { - result = FALSE; - } - if (p1) - free(p1); - arg[argLen] = savec; - return (result); -} - -/*- - *----------------------------------------------------------------------- - * CondStrMatch -- - * Front-end for Str_Match so it returns 0 on match and non-zero - * on mismatch. Callback function for CondDoMake via Lst_Find - * - * Results: - * 0 if string matches pattern - * - * Side Effects: - * None - * - *----------------------------------------------------------------------- - */ -static int -CondStrMatch(string, pattern) - ClientData string; - ClientData pattern; -{ - return(!Str_Match((char *) string,(char *) pattern)); -} - -/*- - *----------------------------------------------------------------------- - * CondDoMake -- - * Handle the 'make' function for conditionals. - * - * Results: - * TRUE if the given target is being made. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -static Boolean -CondDoMake (argLen, arg) - int argLen; - char *arg; -{ - char savec = arg[argLen]; - Boolean result; - - arg[argLen] = '\0'; - if (Lst_Find (create, (ClientData)arg, CondStrMatch) == NILLNODE) { - result = FALSE; - } else { - result = TRUE; - } - arg[argLen] = savec; - return (result); -} - -/*- - *----------------------------------------------------------------------- - * CondDoExists -- - * See if the given file exists. - * - * Results: - * TRUE if the file exists and FALSE if it does not. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -static Boolean -CondDoExists (argLen, arg) - int argLen; - char *arg; -{ - char savec = arg[argLen]; - Boolean result; - char *path; - - arg[argLen] = '\0'; - path = Dir_FindFile(arg, dirSearchPath); - if (path != (char *)NULL) { - result = TRUE; - free(path); - } else { - result = FALSE; - } - arg[argLen] = savec; - return (result); -} - -/*- - *----------------------------------------------------------------------- - * CondDoTarget -- - * See if the given node exists and is an actual target. - * - * Results: - * TRUE if the node exists as a target and FALSE if it does not. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -static Boolean -CondDoTarget (argLen, arg) - int argLen; - char *arg; -{ - char savec = arg[argLen]; - Boolean result; - GNode *gn; - - arg[argLen] = '\0'; - gn = Targ_FindNode(arg, TARG_NOCREATE); - if ((gn != NILGNODE) && !OP_NOP(gn->type)) { - result = TRUE; - } else { - result = FALSE; - } - arg[argLen] = savec; - return (result); -} - - -/*- - *----------------------------------------------------------------------- - * CondCvtArg -- - * Convert the given number into a double. If the number begins - * with 0x, it is interpreted as a hexadecimal integer - * and converted to a double from there. All other strings just have - * strtod called on them. - * - * Results: - * Sets 'value' to double value of string. - * Returns true if the string was a valid number, false o.w. - * - * Side Effects: - * Can change 'value' even if string is not a valid number. - * - * - *----------------------------------------------------------------------- - */ -static Boolean -CondCvtArg(str, value) - register char *str; - double *value; -{ - if ((*str == '0') && (str[1] == 'x')) { - register long i; - - for (str += 2, i = 0; *str; str++) { - int x; - if (isdigit((unsigned char) *str)) - x = *str - '0'; - else if (isxdigit((unsigned char) *str)) - x = 10 + *str - isupper((unsigned char) *str) ? 'A' : 'a'; - else - return FALSE; - i = (i << 4) + x; - } - *value = (double) i; - return TRUE; - } - else { - char *eptr; - *value = strtod(str, &eptr); - return *eptr == '\0'; - } -} - -/*- - *----------------------------------------------------------------------- - * CondToken -- - * Return the next token from the input. - * - * Results: - * A Token for the next lexical token in the stream. - * - * Side Effects: - * condPushback will be set back to None if it is used. - * - *----------------------------------------------------------------------- - */ -static Token -CondToken(doEval) - Boolean doEval; -{ - Token t; - - if (condPushBack == None) { - while (*condExpr == ' ' || *condExpr == '\t') { - condExpr++; - } - switch (*condExpr) { - case '(': - t = LParen; - condExpr++; - break; - case ')': - t = RParen; - condExpr++; - break; - case '|': - if (condExpr[1] == '|') { - condExpr++; - } - condExpr++; - t = Or; - break; - case '&': - if (condExpr[1] == '&') { - condExpr++; - } - condExpr++; - t = And; - break; - case '!': - t = Not; - condExpr++; - break; - case '\n': - case '\0': - t = EndOfFile; - break; - case '$': { - char *lhs; - char *rhs; - char *op; - int varSpecLen; - Boolean doFree; - - /* - * Parse the variable spec and skip over it, saving its - * value in lhs. - */ - t = Err; - lhs = Var_Parse(condExpr, VAR_CMD, doEval,&varSpecLen,&doFree); - if (lhs == var_Error) { - /* - * Even if !doEval, we still report syntax errors, which - * is what getting var_Error back with !doEval means. - */ - return(Err); - } - condExpr += varSpecLen; - - if (!isspace((unsigned char) *condExpr) && - strchr("!=><", *condExpr) == NULL) { - Buffer buf; - char *cp; - - buf = Buf_Init(0); - - for (cp = lhs; *cp; cp++) - Buf_AddByte(buf, (Byte)*cp); - - if (doFree) - free(lhs); - - for (;*condExpr && !isspace((unsigned char) *condExpr); - condExpr++) - Buf_AddByte(buf, (Byte)*condExpr); - - Buf_AddByte(buf, (Byte)'\0'); - lhs = (char *)Buf_GetAll(buf, &varSpecLen); - Buf_Destroy(buf, FALSE); - - doFree = TRUE; - } - - /* - * Skip whitespace to get to the operator - */ - while (isspace((unsigned char) *condExpr)) - condExpr++; - - /* - * Make sure the operator is a valid one. If it isn't a - * known relational operator, pretend we got a - * != 0 comparison. - */ - op = condExpr; - switch (*condExpr) { - case '!': - case '=': - case '<': - case '>': - if (condExpr[1] == '=') { - condExpr += 2; - } else { - condExpr += 1; - } - break; - default: - op = "!="; - rhs = "0"; - - goto do_compare; - } - while (isspace((unsigned char) *condExpr)) { - condExpr++; - } - if (*condExpr == '\0') { - Parse_Error(PARSE_WARNING, - "Missing right-hand-side of operator"); - goto error; - } - rhs = condExpr; -do_compare: - if (*rhs == '"') { - /* - * Doing a string comparison. Only allow == and != for - * operators. - */ - char *string; - char *cp, *cp2; - int qt; - Buffer buf; - -do_string_compare: - if (((*op != '!') && (*op != '=')) || (op[1] != '=')) { - Parse_Error(PARSE_WARNING, - "String comparison operator should be either == or !="); - goto error; - } - - buf = Buf_Init(0); - qt = *rhs == '"' ? 1 : 0; - - for (cp = &rhs[qt]; - ((qt && (*cp != '"')) || - (!qt && strchr(" \t)", *cp) == NULL)) && - (*cp != '\0'); cp++) { - if ((*cp == '\\') && (cp[1] != '\0')) { - /* - * Backslash escapes things -- skip over next - * character, if it exists. - */ - cp++; - Buf_AddByte(buf, (Byte)*cp); - } else if (*cp == '$') { - int len; - Boolean freeIt; - - cp2 = Var_Parse(cp, VAR_CMD, doEval,&len, &freeIt); - if (cp2 != var_Error) { - Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2); - if (freeIt) { - free(cp2); - } - cp += len - 1; - } else { - Buf_AddByte(buf, (Byte)*cp); - } - } else { - Buf_AddByte(buf, (Byte)*cp); - } - } - - Buf_AddByte(buf, (Byte)0); - - string = (char *)Buf_GetAll(buf, (int *)0); - Buf_Destroy(buf, FALSE); - - if (DEBUG(COND)) { - printf("lhs = \"%s\", rhs = \"%s\", op = %.2s\n", - lhs, string, op); - } - /* - * Null-terminate rhs and perform the comparison. - * t is set to the result. - */ - if (*op == '=') { - t = strcmp(lhs, string) ? False : True; - } else { - t = strcmp(lhs, string) ? True : False; - } - free(string); - if (rhs == condExpr) { - if (!qt && *cp == ')') - condExpr = cp; - else - condExpr = cp + 1; - } - } else { - /* - * rhs is either a float or an integer. Convert both the - * lhs and the rhs to a double and compare the two. - */ - double left, right; - char *string; - - if (!CondCvtArg(lhs, &left)) - goto do_string_compare; - if (*rhs == '$') { - int len; - Boolean freeIt; - - string = Var_Parse(rhs, VAR_CMD, doEval,&len,&freeIt); - if (string == var_Error) { - right = 0.0; - } else { - if (!CondCvtArg(string, &right)) { - if (freeIt) - free(string); - goto do_string_compare; - } - if (freeIt) - free(string); - if (rhs == condExpr) - condExpr += len; - } - } else { - if (!CondCvtArg(rhs, &right)) - goto do_string_compare; - if (rhs == condExpr) { - /* - * Skip over the right-hand side - */ - while(!isspace((unsigned char) *condExpr) && - (*condExpr != '\0')) { - condExpr++; - } - } - } - - if (DEBUG(COND)) { - printf("left = %f, right = %f, op = %.2s\n", left, - right, op); - } - switch(op[0]) { - case '!': - if (op[1] != '=') { - Parse_Error(PARSE_WARNING, - "Unknown operator"); - goto error; - } - t = (left != right ? True : False); - break; - case '=': - if (op[1] != '=') { - Parse_Error(PARSE_WARNING, - "Unknown operator"); - goto error; - } - t = (left == right ? True : False); - break; - case '<': - if (op[1] == '=') { - t = (left <= right ? True : False); - } else { - t = (left < right ? True : False); - } - break; - case '>': - if (op[1] == '=') { - t = (left >= right ? True : False); - } else { - t = (left > right ? True : False); - } - break; - } - } -error: - if (doFree) - free(lhs); - break; - } - default: { - Boolean (*evalProc)(); - Boolean invert = FALSE; - char *arg; - int arglen; - - if (strncmp (condExpr, "defined", 7) == 0) { - /* - * Use CondDoDefined to evaluate the argument and - * CondGetArg to extract the argument from the 'function - * call'. - */ - evalProc = CondDoDefined; - condExpr += 7; - arglen = CondGetArg (&condExpr, &arg, "defined", TRUE); - if (arglen == 0) { - condExpr -= 7; - goto use_default; - } - } else if (strncmp (condExpr, "make", 4) == 0) { - /* - * Use CondDoMake to evaluate the argument and - * CondGetArg to extract the argument from the 'function - * call'. - */ - evalProc = CondDoMake; - condExpr += 4; - arglen = CondGetArg (&condExpr, &arg, "make", TRUE); - if (arglen == 0) { - condExpr -= 4; - goto use_default; - } - } else if (strncmp (condExpr, "exists", 6) == 0) { - /* - * Use CondDoExists to evaluate the argument and - * CondGetArg to extract the argument from the - * 'function call'. - */ - evalProc = CondDoExists; - condExpr += 6; - arglen = CondGetArg(&condExpr, &arg, "exists", TRUE); - if (arglen == 0) { - condExpr -= 6; - goto use_default; - } - } else if (strncmp(condExpr, "empty", 5) == 0) { - /* - * Use Var_Parse to parse the spec in parens and return - * True if the resulting string is empty. - */ - int length; - Boolean doFree; - char *val; - - condExpr += 5; - - for (arglen = 0; - condExpr[arglen] != '(' && condExpr[arglen] != '\0'; - arglen += 1) - continue; - - if (condExpr[arglen] != '\0') { - val = Var_Parse(&condExpr[arglen - 1], VAR_CMD, - doEval, &length, &doFree); - if (val == var_Error) { - t = Err; - } else { - /* - * A variable is empty when it just contains - * spaces... 4/15/92, christos - */ - char *p; - for (p = val; *p && isspace((unsigned char)*p); p++) - continue; - t = (*p == '\0') ? True : False; - } - if (doFree) { - free(val); - } - /* - * Advance condExpr to beyond the closing ). Note that - * we subtract one from arglen + length b/c length - * is calculated from condExpr[arglen - 1]. - */ - condExpr += arglen + length - 1; - } else { - condExpr -= 5; - goto use_default; - } - break; - } else if (strncmp (condExpr, "target", 6) == 0) { - /* - * Use CondDoTarget to evaluate the argument and - * CondGetArg to extract the argument from the - * 'function call'. - */ - evalProc = CondDoTarget; - condExpr += 6; - arglen = CondGetArg(&condExpr, &arg, "target", TRUE); - if (arglen == 0) { - condExpr -= 6; - goto use_default; - } - } else { - /* - * The symbol is itself the argument to the default - * function. We advance condExpr to the end of the symbol - * by hand (the next whitespace, closing paren or - * binary operator) and set to invert the evaluation - * function if condInvert is TRUE. - */ - use_default: - invert = condInvert; - evalProc = condDefProc; - arglen = CondGetArg(&condExpr, &arg, "", FALSE); - } - - /* - * Evaluate the argument using the set function. If invert - * is TRUE, we invert the sense of the function. - */ - t = (!doEval || (* evalProc) (arglen, arg) ? - (invert ? False : True) : - (invert ? True : False)); - free(arg); - break; - } - } - } else { - t = condPushBack; - condPushBack = None; - } - return (t); -} - -/*- - *----------------------------------------------------------------------- - * CondT -- - * Parse a single term in the expression. This consists of a terminal - * symbol or Not and a terminal symbol (not including the binary - * operators): - * T -> defined(variable) | make(target) | exists(file) | symbol - * T -> ! T | ( E ) - * - * Results: - * True, False or Err. - * - * Side Effects: - * Tokens are consumed. - * - *----------------------------------------------------------------------- - */ -static Token -CondT(doEval) - Boolean doEval; -{ - Token t; - - t = CondToken(doEval); - - if (t == EndOfFile) { - /* - * If we reached the end of the expression, the expression - * is malformed... - */ - t = Err; - } else if (t == LParen) { - /* - * T -> ( E ) - */ - t = CondE(doEval); - if (t != Err) { - if (CondToken(doEval) != RParen) { - t = Err; - } - } - } else if (t == Not) { - t = CondT(doEval); - if (t == True) { - t = False; - } else if (t == False) { - t = True; - } - } - return (t); -} - -/*- - *----------------------------------------------------------------------- - * CondF -- - * Parse a conjunctive factor (nice name, wot?) - * F -> T && F | T - * - * Results: - * True, False or Err - * - * Side Effects: - * Tokens are consumed. - * - *----------------------------------------------------------------------- - */ -static Token -CondF(doEval) - Boolean doEval; -{ - Token l, o; - - l = CondT(doEval); - if (l != Err) { - o = CondToken(doEval); - - if (o == And) { - /* - * F -> T && F - * - * If T is False, the whole thing will be False, but we have to - * parse the r.h.s. anyway (to throw it away). - * If T is True, the result is the r.h.s., be it an Err or no. - */ - if (l == True) { - l = CondF(doEval); - } else { - (void) CondF(FALSE); - } - } else { - /* - * F -> T - */ - CondPushBack (o); - } - } - return (l); -} - -/*- - *----------------------------------------------------------------------- - * CondE -- - * Main expression production. - * E -> F || E | F - * - * Results: - * True, False or Err. - * - * Side Effects: - * Tokens are, of course, consumed. - * - *----------------------------------------------------------------------- - */ -static Token -CondE(doEval) - Boolean doEval; -{ - Token l, o; - - l = CondF(doEval); - if (l != Err) { - o = CondToken(doEval); - - if (o == Or) { - /* - * E -> F || E - * - * A similar thing occurs for ||, except that here we make sure - * the l.h.s. is False before we bother to evaluate the r.h.s. - * Once again, if l is False, the result is the r.h.s. and once - * again if l is True, we parse the r.h.s. to throw it away. - */ - if (l == False) { - l = CondE(doEval); - } else { - (void) CondE(FALSE); - } - } else { - /* - * E -> F - */ - CondPushBack (o); - } - } - return (l); -} - -/*- - *----------------------------------------------------------------------- - * Cond_Eval -- - * Evaluate the conditional in the passed line. The line - * looks like this: - * # - * where is any of if, ifmake, ifnmake, ifdef, - * ifndef, elif, elifmake, elifnmake, elifdef, elifndef - * and consists of &&, ||, !, make(target), defined(variable) - * and parenthetical groupings thereof. - * - * Results: - * COND_PARSE if should parse lines after the conditional - * COND_SKIP if should skip lines after the conditional - * COND_INVALID if not a valid conditional. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -int -Cond_Eval (line) - char *line; /* Line to parse */ -{ - struct If *ifp; - Boolean isElse; - Boolean value = FALSE; - int level; /* Level at which to report errors. */ - - level = PARSE_FATAL; - - for (line++; *line == ' ' || *line == '\t'; line++) { - continue; - } - - /* - * Find what type of if we're dealing with. The result is left - * in ifp and isElse is set TRUE if it's an elif line. - */ - if (line[0] == 'e' && line[1] == 'l') { - line += 2; - isElse = TRUE; - } else if (strncmp (line, "endif", 5) == 0) { - /* - * End of a conditional section. If skipIfLevel is non-zero, that - * conditional was skipped, so lines following it should also be - * skipped. Hence, we return COND_SKIP. Otherwise, the conditional - * was read so succeeding lines should be parsed (think about it...) - * so we return COND_PARSE, unless this endif isn't paired with - * a decent if. - */ - if (skipIfLevel != 0) { - skipIfLevel -= 1; - return (COND_SKIP); - } else { - if (condTop == MAXIF) { - Parse_Error (level, "if-less endif"); - return (COND_INVALID); - } else { - skipLine = FALSE; - condTop += 1; - return (COND_PARSE); - } - } - } else { - isElse = FALSE; - } - - /* - * Figure out what sort of conditional it is -- what its default - * function is, etc. -- by looking in the table of valid "ifs" - */ - for (ifp = ifs; ifp->form != (char *)0; ifp++) { - if (strncmp (ifp->form, line, ifp->formlen) == 0) { - break; - } - } - - if (ifp->form == (char *) 0) { - /* - * Nothing fit. If the first word on the line is actually - * "else", it's a valid conditional whose value is the inverse - * of the previous if we parsed. - */ - if (isElse && (line[0] == 's') && (line[1] == 'e')) { - if (condTop == MAXIF) { - Parse_Error (level, "if-less else"); - return (COND_INVALID); - } else if (skipIfLevel == 0) { - value = !condStack[condTop]; - } else { - return (COND_SKIP); - } - } else { - /* - * Not a valid conditional type. No error... - */ - return (COND_INVALID); - } - } else { - if (isElse) { - if (condTop == MAXIF) { - Parse_Error (level, "if-less elif"); - return (COND_INVALID); - } else if (skipIfLevel != 0) { - /* - * If skipping this conditional, just ignore the whole thing. - * If we don't, the user might be employing a variable that's - * undefined, for which there's an enclosing ifdef that - * we're skipping... - */ - return(COND_SKIP); - } - } else if (skipLine) { - /* - * Don't even try to evaluate a conditional that's not an else if - * we're skipping things... - */ - skipIfLevel += 1; - return(COND_SKIP); - } - - /* - * Initialize file-global variables for parsing - */ - condDefProc = ifp->defProc; - condInvert = ifp->doNot; - - line += ifp->formlen; - - while (*line == ' ' || *line == '\t') { - line++; - } - - condExpr = line; - condPushBack = None; - - switch (CondE(TRUE)) { - case True: - if (CondToken(TRUE) == EndOfFile) { - value = TRUE; - break; - } - goto err; - /*FALLTHRU*/ - case False: - if (CondToken(TRUE) == EndOfFile) { - value = FALSE; - break; - } - /*FALLTHRU*/ - case Err: - err: - Parse_Error (level, "Malformed conditional (%s)", - line); - return (COND_INVALID); - default: - break; - } - } - if (!isElse) { - condTop -= 1; - } else if ((skipIfLevel != 0) || condStack[condTop]) { - /* - * If this is an else-type conditional, it should only take effect - * if its corresponding if was evaluated and FALSE. If its if was - * TRUE or skipped, we return COND_SKIP (and start skipping in case - * we weren't already), leaving the stack unmolested so later elif's - * don't screw up... - */ - skipLine = TRUE; - return (COND_SKIP); - } - - if (condTop < 0) { - /* - * This is the one case where we can definitely proclaim a fatal - * error. If we don't, we're hosed. - */ - Parse_Error (PARSE_FATAL, "Too many nested if's. %d max.", MAXIF); - return (COND_INVALID); - } else { - condStack[condTop] = value; - skipLine = !value; - return (value ? COND_PARSE : COND_SKIP); - } -} - -/*- - *----------------------------------------------------------------------- - * Cond_End -- - * Make sure everything's clean at the end of a makefile. - * - * Results: - * None. - * - * Side Effects: - * Parse_Error will be called if open conditionals are around. - * - *----------------------------------------------------------------------- - */ -void -Cond_End() -{ - if (condTop != MAXIF) { - Parse_Error(PARSE_FATAL, "%d open conditional%s", MAXIF-condTop, - MAXIF-condTop == 1 ? "" : "s"); - } - condTop = MAXIF; -} diff --git a/usr.bin/make/for.c b/usr.bin/make/for.c deleted file mode 100644 index 3ab3cba33dce..000000000000 --- a/usr.bin/make/for.c +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright (c) 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Christos Zoulas. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char sccsid[] = "@(#)for.c 8.2 (Berkeley) 4/28/95"; -#endif /* not lint */ - -/*- - * for.c -- - * Functions to handle loops in a makefile. - * - * Interface: - * For_Eval Evaluate the loop in the passed line. - * For_Run Run accumulated loop - * - */ - -#include -#include "make.h" -#include "hash.h" -#include "dir.h" -#include "buf.h" - -/* - * For statements are of the form: - * - * .for in - * ... - * .endfor - * - * The trick is to look for the matching end inside for for loop - * To do that, we count the current nesting level of the for loops. - * and the .endfor statements, accumulating all the statements between - * the initial .for loop and the matching .endfor; - * then we evaluate the for loop for each variable in the varlist. - */ - -static int forLevel = 0; /* Nesting level */ -static char *forVar; /* Iteration variable */ -static Buffer forBuf; /* Commands in loop */ -static Lst forLst; /* List of items */ - -/* - * State of a for loop. - */ -typedef struct _For { - Buffer buf; /* Unexpanded buffer */ - char* var; /* Index name */ - Lst lst; /* List of variables */ -} For; - -static int ForExec __P((ClientData, ClientData)); - - - - -/*- - *----------------------------------------------------------------------- - * For_Eval -- - * Evaluate the for loop in the passed line. The line - * looks like this: - * .for in - * - * Results: - * TRUE: We found a for loop, or we are inside a for loop - * FALSE: We did not find a for loop, or we found the end of the for - * for loop. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -int -For_Eval (line) - char *line; /* Line to parse */ -{ - char *ptr = line, *sub, *wrd; - int level; /* Level at which to report errors. */ - - level = PARSE_FATAL; - - - if (forLevel == 0) { - Buffer buf; - int varlen; - - for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++) - continue; - /* - * If we are not in a for loop quickly determine if the statement is - * a for. - */ - if (ptr[0] != 'f' || ptr[1] != 'o' || ptr[2] != 'r' || - !isspace((unsigned char) ptr[3])) - return FALSE; - ptr += 3; - - /* - * we found a for loop, and now we are going to parse it. - */ - while (*ptr && isspace((unsigned char) *ptr)) - ptr++; - - /* - * Grab the variable - */ - buf = Buf_Init(0); - for (wrd = ptr; *ptr && !isspace((unsigned char) *ptr); ptr++) - continue; - Buf_AddBytes(buf, ptr - wrd, (Byte *) wrd); - - forVar = (char *) Buf_GetAll(buf, &varlen); - if (varlen == 0) { - Parse_Error (level, "missing variable in for"); - return 0; - } - Buf_Destroy(buf, FALSE); - - while (*ptr && isspace((unsigned char) *ptr)) - ptr++; - - /* - * Grab the `in' - */ - if (ptr[0] != 'i' || ptr[1] != 'n' || - !isspace((unsigned char) ptr[2])) { - Parse_Error (level, "missing `in' in for"); - printf("%s\n", ptr); - return 0; - } - ptr += 3; - - while (*ptr && isspace((unsigned char) *ptr)) - ptr++; - - /* - * Make a list with the remaining words - */ - forLst = Lst_Init(FALSE); - buf = Buf_Init(0); - sub = Var_Subst(NULL, ptr, VAR_GLOBAL, FALSE); - -#define ADDWORD() \ - Buf_AddBytes(buf, ptr - wrd, (Byte *) wrd), \ - Buf_AddByte(buf, (Byte) '\0'), \ - Lst_AtEnd(forLst, (ClientData) Buf_GetAll(buf, &varlen)), \ - Buf_Destroy(buf, FALSE) - - for (ptr = sub; *ptr && isspace((unsigned char) *ptr); ptr++) - continue; - - for (wrd = ptr; *ptr; ptr++) - if (isspace((unsigned char) *ptr)) { - ADDWORD(); - buf = Buf_Init(0); - while (*ptr && isspace((unsigned char) *ptr)) - ptr++; - wrd = ptr--; - } - if (DEBUG(FOR)) - (void) fprintf(stderr, "For: Iterator %s List %s\n", forVar, sub); - if (ptr - wrd > 0) - ADDWORD(); - else - Buf_Destroy(buf, TRUE); - free((Address) sub); - - forBuf = Buf_Init(0); - forLevel++; - return 1; - } - else if (*ptr == '.') { - - for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++) - continue; - - if (strncmp(ptr, "endfor", 6) == 0 && - (isspace((unsigned char) ptr[6]) || !ptr[6])) { - if (DEBUG(FOR)) - (void) fprintf(stderr, "For: end for %d\n", forLevel); - if (--forLevel < 0) { - Parse_Error (level, "for-less endfor"); - return 0; - } - } - else if (strncmp(ptr, "for", 3) == 0 && - isspace((unsigned char) ptr[3])) { - forLevel++; - if (DEBUG(FOR)) - (void) fprintf(stderr, "For: new loop %d\n", forLevel); - } - } - - if (forLevel != 0) { - Buf_AddBytes(forBuf, strlen(line), (Byte *) line); - Buf_AddByte(forBuf, (Byte) '\n'); - return 1; - } - else { - return 0; - } -} - -/*- - *----------------------------------------------------------------------- - * ForExec -- - * Expand the for loop for this index and push it in the Makefile - * - * Results: - * None. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -static int -ForExec(namep, argp) - ClientData namep; - ClientData argp; -{ - char *name = (char *) namep; - For *arg = (For *) argp; - int len; - Var_Set(arg->var, name, VAR_GLOBAL); - if (DEBUG(FOR)) - (void) fprintf(stderr, "--- %s = %s\n", arg->var, name); - Parse_FromString(Var_Subst(arg->var, (char *) Buf_GetAll(arg->buf, &len), - VAR_GLOBAL, FALSE)); - Var_Delete(arg->var, VAR_GLOBAL); - - return 0; -} - - -/*- - *----------------------------------------------------------------------- - * For_Run -- - * Run the for loop, immitating the actions of an include file - * - * Results: - * None. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -void -For_Run() -{ - For arg; - - if (forVar == NULL || forBuf == NULL || forLst == NULL) - return; - arg.var = forVar; - arg.buf = forBuf; - arg.lst = forLst; - forVar = NULL; - forBuf = NULL; - forLst = NULL; - - Lst_ForEach(arg.lst, ForExec, (ClientData) &arg); - - free((Address)arg.var); - Lst_Destroy(arg.lst, (void (*) __P((ClientData))) free); - Buf_Destroy(arg.buf, TRUE); -} diff --git a/usr.bin/make/job.c b/usr.bin/make/job.c deleted file mode 100644 index 166fd974797a..000000000000 --- a/usr.bin/make/job.c +++ /dev/null @@ -1,2668 +0,0 @@ -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char sccsid[] = "@(#)job.c 8.3 (Berkeley) 4/28/95"; -#endif /* not lint */ - -/*- - * job.c -- - * handle the creation etc. of our child processes. - * - * Interface: - * Job_Make Start the creation of the given target. - * - * Job_CatchChildren Check for and handle the termination of any - * children. This must be called reasonably - * frequently to keep the whole make going at - * a decent clip, since job table entries aren't - * removed until their process is caught this way. - * Its single argument is TRUE if the function - * should block waiting for a child to terminate. - * - * Job_CatchOutput Print any output our children have produced. - * Should also be called fairly frequently to - * keep the user informed of what's going on. - * If no output is waiting, it will block for - * a time given by the SEL_* constants, below, - * or until output is ready. - * - * Job_Init Called to intialize this module. in addition, - * any commands attached to the .BEGIN target - * are executed before this function returns. - * Hence, the makefile must have been parsed - * before this function is called. - * - * Job_Full Return TRUE if the job table is filled. - * - * Job_Empty Return TRUE if the job table is completely - * empty. - * - * Job_ParseShell Given the line following a .SHELL target, parse - * the line as a shell specification. Returns - * FAILURE if the spec was incorrect. - * - * Job_End 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "make.h" -#include "hash.h" -#include "dir.h" -#include "job.h" -#include "pathnames.h" - -extern int errno; - -/* - * 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 */ - - -/* - * post-make command processing. The node postCommands is really just the - * .END target but we keep it around to avoid having to search for it - * all the time. - */ -static GNode *postCommands; /* node containing commands to execute when - * everything else is done */ -static int numCommands; /* The number of commands actually printed - * for a target. Should this number be - * 0, no shell will be executed. */ - - -/* - * Return values from JobStart. - */ -#define JOB_RUNNING 0 /* Job is running */ -#define JOB_ERROR 1 /* Error in starting the job */ -#define JOB_FINISHED 2 /* The job is already finished */ -#define JOB_STOPPED 3 /* The job is stopped */ - -/* - * tfile is the name of a file into which all shell commands are put. It is - * used over by removing it before the child shell is executed. The XXXXX in - * the string are replaced by the pid of the make process in a 5-character - * field with leading zeroes. - */ -static char tfile[] = TMPPAT; - - -/* - * Descriptions for various shells. - */ -static Shell shells[] = { - /* - * CSH description. The csh can do echo control by playing - * with the setting of the 'echo' shell variable. Sadly, - * however, it is unable to do error control nicely. - */ -{ - "csh", - TRUE, "unset verbose", "set verbose", "unset verbose", 10, - FALSE, "echo \"%s\"\n", "csh -c \"%s || exit 0\"", - "v", "e", -}, - /* - * SH description. Echo control is also possible and, under - * sun UNIX anyway, one can even control error checking. - */ -{ - "sh", - TRUE, "set -", "set -v", "set -", 5, - FALSE, "echo \"%s\"\n", "sh -c '%s || exit 0'\n", - "v", "e", -}, - /* - * UNKNOWN. - */ -{ - (char *)0, - FALSE, (char *)0, (char *)0, (char *)0, 0, - FALSE, (char *)0, (char *)0, - (char *)0, (char *)0, -} -}; -static Shell *commandShell = &shells[DEFSHELL];/* this is the shell to - * which we pass all - * commands in the Makefile. - * It is set by the - * Job_ParseShell function */ -static char *shellPath = (char *) NULL, /* full pathname of - * executable image */ - *shellName; /* last component of shell */ - - -static int maxJobs; /* The most children we can run at once */ -static int maxLocal; /* The most local ones we can have */ -int nJobs; /* The number of children currently running */ -int nLocal; /* The number of local children */ -Lst jobs; /* The structures that describe them */ -Boolean jobFull; /* Flag to tell when the job table is full. It - * is set TRUE when (1) the total number of - * running jobs equals the maximum allowed or - * (2) a job can only be run locally, but - * nLocal equals maxLocal */ -#ifndef RMT_WILL_WATCH -static fd_set outputs; /* Set of descriptors of pipes connected to - * the output channels of children */ -#endif - -GNode *lastNode; /* The node for which output was most recently - * produced. */ -char *targFmt; /* Format string to use to head output from a - * job when it's not the most-recent job heard - * from */ -#define TARG_FMT "--- %s ---\n" /* Default format */ - -/* - * When JobStart attempts to run a job remotely but can't, and isn't allowed - * to run the job locally, or when Job_CatchChildren detects a job that has - * been migrated home, the job is placed on the stoppedJobs queue to be run - * when the next job finishes. - */ -Lst stoppedJobs; /* Lst of Job structures describing - * jobs that were stopped due to concurrency - * limits or migration home */ - - -#if defined(USE_PGRP) && defined(SYSV) -#define KILL(pid,sig) killpg (-(pid),(sig)) -#else -# if defined(USE_PGRP) -#define KILL(pid,sig) killpg ((pid),(sig)) -# else -#define KILL(pid,sig) kill ((pid),(sig)) -# endif -#endif - -static int JobCondPassSig __P((ClientData, ClientData)); -static void JobPassSig __P((int)); -static int JobCmpPid __P((ClientData, ClientData)); -static int JobPrintCommand __P((ClientData, ClientData)); -static int JobSaveCommand __P((ClientData, ClientData)); -static void JobFinish __P((Job *, union wait)); -static void JobExec __P((Job *, char **)); -static void JobMakeArgv __P((Job *, char **)); -static void JobRestart __P((Job *)); -static int JobStart __P((GNode *, int, Job *)); -static void JobDoOutput __P((Job *, Boolean)); -static Shell *JobMatchShell __P((char *)); -static void JobInterrupt __P((int)); - -/*- - *----------------------------------------------------------------------- - * JobCondPassSig -- - * Pass a signal to a job if the job is remote or if USE_PGRP - * is defined. - * - * Results: - * === 0 - * - * Side Effects: - * None, except the job may bite it. - * - *----------------------------------------------------------------------- - */ -static int -JobCondPassSig(jobp, signop) - ClientData jobp; /* Job to biff */ - ClientData signop; /* Signal to send it */ -{ - Job *job = (Job *) jobp; - int signo = *(int *) signop; -#ifdef RMT_WANTS_SIGNALS - if (job->flags & JOB_REMOTE) { - (void)Rmt_Signal(job, signo); - } else { - KILL(job->pid, signo); - } -#else - /* - * Assume that sending the signal to job->pid will signal any remote - * job as well. - */ - KILL(job->pid, signo); -#endif - return(0); -} - -/*- - *----------------------------------------------------------------------- - * JobPassSig -- - * Pass a signal on to all remote jobs and to all local jobs if - * USE_PGRP is defined, then die ourselves. - * - * Results: - * None. - * - * Side Effects: - * We die by the same signal. - * - *----------------------------------------------------------------------- - */ -static void -JobPassSig(signo) - int signo; /* The signal number we've received */ -{ - int mask; - - Lst_ForEach(jobs, JobCondPassSig, (ClientData)(long)signo); - - /* - * Deal with proper cleanup based on the signal received. We only run - * the .INTERRUPT target if the signal was in fact an interrupt. The other - * three termination signals are more of a "get out *now*" command. - */ - if (signo == SIGINT) { - JobInterrupt(TRUE); - } else if ((signo == SIGHUP) || (signo == SIGTERM) || (signo == SIGQUIT)) { - JobInterrupt(FALSE); - } - - /* - * Leave gracefully if SIGQUIT, rather than core dumping. - */ - if (signo == SIGQUIT) { - Finish(0); - } - - /* - * 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. - */ - mask = sigblock(0); - (void) sigsetmask(~0 & ~(1 << (signo-1))); - signal(signo, SIG_DFL); - - kill(getpid(), signo); - - signo = SIGCONT; - Lst_ForEach(jobs, JobCondPassSig, (ClientData) &signo); - - sigsetmask(mask); - signal(signo, JobPassSig); - -} - -/*- - *----------------------------------------------------------------------- - * JobCmpPid -- - * Compare the pid of the job with the given pid and return 0 if they - * are equal. This function is called from Job_CatchChildren via - * Lst_Find to find the job descriptor of the finished job. - * - * Results: - * 0 if the pid's match - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ -static int -JobCmpPid (job, pid) - ClientData job; /* job to examine */ - ClientData pid; /* process id desired */ -{ - return ( *(int *) pid - ((Job *) job)->pid); -} - -/*- - *----------------------------------------------------------------------- - * JobPrintCommand -- - * Put out another command for the given job. If the command starts - * with an @ or a - we process it specially. In the former case, - * so long as the -s and -n flags weren't given to make, we stick - * a shell-specific echoOff command in the script. In the latter, - * we ignore errors for the entire job, unless the shell has error - * control. - * If the command is just "..." we take all future commands for this - * job to be commands to be executed once the entire graph has been - * made and return non-zero to signal that the end of the commands - * was reached. These commands are later attached to the postCommands - * node and executed by Job_End when all things are done. - * This function is called from JobStart via Lst_ForEach. - * - * Results: - * Always 0, unless the command was "..." - * - * Side Effects: - * If the command begins with a '-' and the shell has no error control, - * the JOB_IGNERR flag is set in the job descriptor. - * If the command is "..." and we're not ignoring such things, - * tailCmds is set to the successor node of the cmd. - * numCommands is incremented if the command is actually printed. - *----------------------------------------------------------------------- - */ -static int -JobPrintCommand (cmdp, jobp) - ClientData cmdp; /* command string to print */ - ClientData jobp; /* job for which to print it */ -{ - Boolean noSpecials; /* true if we shouldn't worry about - * inserting special commands into - * the input stream. */ - Boolean shutUp = FALSE; /* true if we put a no echo command - * into the command file */ - Boolean errOff = FALSE; /* true if we turned error checking - * off before printing the command - * and need to turn it back on */ - char *cmdTemplate; /* Template to use when printing the - * command */ - char *cmdStart; /* Start of expanded command */ - LstNode cmdNode; /* Node for replacing the command */ - char *cmd = (char *) cmdp; - Job *job = (Job *) jobp; - - noSpecials = (noExecute && ! (job->node->type & OP_MAKE)); - - if (strcmp (cmd, "...") == 0) { - job->node->type |= OP_SAVE_CMDS; - if ((job->flags & JOB_IGNDOTS) == 0) { - job->tailCmds = Lst_Succ (Lst_Member (job->node->commands, - (ClientData)cmd)); - return (1); - } - return (0); - } - -#define DBPRINTF(fmt, arg) if (DEBUG(JOB)) printf (fmt, arg); fprintf (job->cmdFILE, fmt, arg) - - numCommands += 1; - - /* - * For debugging, we replace each command with the result of expanding - * the variables in the command. - */ - cmdNode = Lst_Member (job->node->commands, (ClientData)cmd); - cmdStart = cmd = Var_Subst (NULL, cmd, job->node, FALSE); - Lst_Replace (cmdNode, (ClientData)cmdStart); - - cmdTemplate = "%s\n"; - - /* - * Check for leading @' and -'s to control echoing and error checking. - */ - while (*cmd == '@' || *cmd == '-') { - if (*cmd == '@') { - shutUp = TRUE; - } else { - errOff = TRUE; - } - cmd++; - } - - while (isspace((unsigned char) *cmd)) - cmd++; - - if (shutUp) { - if (! (job->flags & JOB_SILENT) && !noSpecials && - commandShell->hasEchoCtl) { - DBPRINTF ("%s\n", commandShell->echoOff); - } else { - shutUp = FALSE; - } - } - - if (errOff) { - if ( ! (job->flags & JOB_IGNERR) && !noSpecials) { - if (commandShell->hasErrCtl) { - /* - * we don't want the error-control commands showing - * up either, so we turn off echoing while executing - * them. We could put another field in the shell - * structure to tell JobDoOutput to look for this - * string too, but why make it any more complex than - * it already is? - */ - if (! (job->flags & JOB_SILENT) && !shutUp && - commandShell->hasEchoCtl) { - DBPRINTF ("%s\n", commandShell->echoOff); - DBPRINTF ("%s\n", commandShell->ignErr); - DBPRINTF ("%s\n", commandShell->echoOn); - } else { - DBPRINTF ("%s\n", commandShell->ignErr); - } - } else if (commandShell->ignErr && - (*commandShell->ignErr != '\0')) - { - /* - * The shell has no error control, so we need to be - * weird to get it to ignore any errors from the command. - * If echoing is turned on, we turn it off and use the - * errCheck template to echo the command. Leave echoing - * off so the user doesn't see the weirdness we go through - * to ignore errors. Set cmdTemplate to use the weirdness - * instead of the simple "%s\n" template. - */ - if (! (job->flags & JOB_SILENT) && !shutUp && - commandShell->hasEchoCtl) { - DBPRINTF ("%s\n", commandShell->echoOff); - DBPRINTF (commandShell->errCheck, cmd); - shutUp = TRUE; - } - cmdTemplate = commandShell->ignErr; - /* - * The error ignoration (hee hee) is already taken care - * of by the ignErr template, so pretend error checking - * is still on. - */ - errOff = FALSE; - } else { - errOff = FALSE; - } - } else { - errOff = FALSE; - } - } - - DBPRINTF (cmdTemplate, cmd); - - if (errOff) { - /* - * If echoing is already off, there's no point in issuing the - * echoOff command. Otherwise we issue it and pretend it was on - * for the whole command... - */ - if (!shutUp && !(job->flags & JOB_SILENT) && commandShell->hasEchoCtl){ - DBPRINTF ("%s\n", commandShell->echoOff); - shutUp = TRUE; - } - DBPRINTF ("%s\n", commandShell->errCheck); - } - if (shutUp) { - DBPRINTF ("%s\n", commandShell->echoOn); - } - return (0); -} - -/*- - *----------------------------------------------------------------------- - * JobSaveCommand -- - * Save a command to be executed when everything else is done. - * Callback function for JobFinish... - * - * Results: - * Always returns 0 - * - * Side Effects: - * The command is tacked onto the end of postCommands's commands list. - * - *----------------------------------------------------------------------- - */ -static int -JobSaveCommand (cmd, gn) - ClientData cmd; - ClientData gn; -{ - cmd = (ClientData) Var_Subst (NULL, (char *) cmd, (GNode *) gn, FALSE); - (void)Lst_AtEnd (postCommands->commands, cmd); - return (0); -} - -/*- - *----------------------------------------------------------------------- - * JobFinish -- - * Do final processing for the given job including updating - * parents and starting new jobs as available/necessary. Note - * that we pay no attention to the JOB_IGNERR flag here. - * This is because when we're called because of a noexecute flag - * or something, jstat.w_status is 0 and when called from - * Job_CatchChildren, the status is zeroed if it s/b ignored. - * - * Results: - * None - * - * Side Effects: - * Some nodes may be put on the toBeMade queue. - * Final commands for the job are placed on postCommands. - * - * If we got an error and are aborting (aborting == ABORT_ERROR) and - * the job list is now empty, we are done for the day. - * If we recognized an error (errors !=0), we set the aborting flag - * to ABORT_ERROR so no more jobs will be started. - *----------------------------------------------------------------------- - */ -/*ARGSUSED*/ -static void -JobFinish (job, status) - Job *job; /* job to finish */ - union wait status; /* sub-why job went away */ -{ - Boolean done; - - if ((WIFEXITED(status) && - (((status.w_retcode != 0) && !(job->flags & JOB_IGNERR)))) || - (WIFSIGNALED(status) && (status.w_termsig != SIGCONT))) - { - /* - * 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... - */ - if (usePipes) { -#ifdef RMT_WILL_WATCH - Rmt_Ignore(job->inPipe); -#else - FD_CLR(job->inPipe, &outputs); -#endif /* RMT_WILL_WATCH */ - if (job->outPipe != job->inPipe) { - (void)close (job->outPipe); - } - JobDoOutput (job, TRUE); - (void)close (job->inPipe); - } else { - (void)close (job->outFd); - JobDoOutput (job, TRUE); - } - - if (job->cmdFILE != NULL && job->cmdFILE != stdout) { - fclose(job->cmdFILE); - } - done = TRUE; - } else if (WIFEXITED(status) && status.w_retcode != 0) { - /* - * 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. Note we don't - * want to close down any of the streams until we know we're at the - * end. - */ - done = TRUE; - } else { - /* - * No need to close things down or anything. - */ - done = FALSE; - } - - if (done || - WIFSTOPPED(status) || - (WIFSIGNALED(status) && (status.w_termsig == SIGCONT)) || - DEBUG(JOB)) - { - FILE *out; - - if (!usePipes && (job->flags & JOB_IGNERR)) { - /* - * If output is going to a file and this job is ignoring - * errors, arrange to have the exit status sent to the - * output file as well. - */ - out = fdopen (job->outFd, "w"); - } else { - out = stdout; - } - - if (WIFEXITED(status)) { - if (status.w_retcode != 0) { - if (usePipes && job->node != lastNode) { - fprintf (out, targFmt, job->node->name); - lastNode = job->node; - } - fprintf (out, "*** Error code %d%s\n", status.w_retcode, - (job->flags & JOB_IGNERR) ? " (ignored)" : ""); - - if (job->flags & JOB_IGNERR) { - status.w_status = 0; - } - } else if (DEBUG(JOB)) { - if (usePipes && job->node != lastNode) { - fprintf (out, targFmt, job->node->name); - lastNode = job->node; - } - fprintf (out, "*** Completed successfully\n"); - } - } else if (WIFSTOPPED(status)) { - if (usePipes && job->node != lastNode) { - fprintf (out, targFmt, job->node->name); - lastNode = job->node; - } - if (! (job->flags & JOB_REMIGRATE)) { - fprintf (out, "*** Stopped -- signal %d\n", status.w_stopsig); - } - job->flags |= JOB_RESUME; - (void)Lst_AtEnd(stoppedJobs, (ClientData)job); - fflush(out); - return; - } else if (status.w_termsig == SIGCONT) { - /* - * If the beastie has continued, shift the Job from the stopped - * list to the running one (or re-stop it if concurrency is - * exceeded) and go and get another child. - */ - if (job->flags & (JOB_RESUME|JOB_REMIGRATE|JOB_RESTART)) { - if (usePipes && job->node != lastNode) { - fprintf (out, targFmt, job->node->name); - lastNode = job->node; - } - fprintf (out, "*** Continued\n"); - } - if (! (job->flags & JOB_CONTINUING)) { - JobRestart(job); - } else { - Lst_AtEnd(jobs, (ClientData)job); - nJobs += 1; - if (! (job->flags & JOB_REMOTE)) { - nLocal += 1; - } - if (nJobs == maxJobs) { - jobFull = TRUE; - if (DEBUG(JOB)) { - printf("Job queue is full.\n"); - } - } - } - fflush(out); - return; - } else { - if (usePipes && job->node != lastNode) { - fprintf (out, targFmt, job->node->name); - lastNode = job->node; - } - fprintf (out, "*** Signal %d\n", status.w_termsig); - } - - fflush (out); - } - - /* - * Now handle the -B-mode stuff. If the beast still isn't finished, - * try and restart the job on the next command. If JobStart says it's - * ok, it's ok. If there's an error, this puppy is done. - */ - if ((status.w_status == 0) && - !Lst_IsAtEnd (job->node->commands)) - { - switch (JobStart (job->node, - job->flags & JOB_IGNDOTS, - job)) - { - case JOB_RUNNING: - done = FALSE; - break; - case JOB_ERROR: - done = TRUE; - status.w_retcode = 1; - break; - case JOB_FINISHED: - /* - * If we got back a JOB_FINISHED code, JobStart has already - * called Make_Update and freed the job descriptor. We set - * done to false here to avoid fake cycles and double frees. - * JobStart needs to do the update so we can proceed up the - * graph when given the -n flag.. - */ - done = FALSE; - break; - } - } else { - done = TRUE; - } - - - if (done && - (aborting != ABORT_ERROR) && - (aborting != ABORT_INTERRUPT) && - (status.w_status == 0)) - { - /* - * As long as we aren't aborting and the job didn't return a non-zero - * status that we shouldn't ignore, we call Make_Update to update - * the parents. In addition, any saved commands for the node are placed - * on the .END target. - */ - if (job->tailCmds != NILLNODE) { - Lst_ForEachFrom (job->node->commands, job->tailCmds, - JobSaveCommand, - (ClientData)job->node); - } - job->node->made = MADE; - Make_Update (job->node); - free((Address)job); - } else if (status.w_status) { - errors += 1; - free((Address)job); - } - - while (!errors && !jobFull && !Lst_IsEmpty(stoppedJobs)) { - JobRestart((Job *)Lst_DeQueue(stoppedJobs)); - } - - /* - * Set aborting if any error. - */ - if (errors && !keepgoing && (aborting != ABORT_INTERRUPT)) { - /* - * If we found any errors in this batch of children and the -k flag - * wasn't given, we set the aborting flag so no more jobs get - * started. - */ - aborting = ABORT_ERROR; - } - - if ((aborting == ABORT_ERROR) && Job_Empty()) { - /* - * If we are aborting and the job table is now empty, we finish. - */ - (void) unlink (tfile); - Finish (errors); - } -} - -/*- - *----------------------------------------------------------------------- - * Job_Touch -- - * Touch the given target. Called by JobStart when the -t flag was - * given - * - * Results: - * None - * - * Side Effects: - * The data modification of the file is changed. In addition, if the - * file did not exist, it is created. - *----------------------------------------------------------------------- - */ -void -Job_Touch (gn, silent) - GNode *gn; /* the node of the file to touch */ - Boolean silent; /* TRUE if should not print messages */ -{ - int streamID; /* ID of stream opened to do the touch */ - struct timeval times[2]; /* Times for utimes() call */ - - if (gn->type & (OP_JOIN|OP_USE|OP_EXEC|OP_OPTIONAL)) { - /* - * .JOIN, .USE, .ZEROTIME and .OPTIONAL targets are "virtual" targets - * and, as such, shouldn't really be created. - */ - return; - } - - if (!silent) { - printf ("touch %s\n", gn->name); - } - - if (noExecute) { - 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[0].tv_sec = times[1].tv_sec = now; - times[0].tv_usec = times[1].tv_usec = 0; - if (utimes(file, times) < 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) { - lseek(streamID, 0L, L_SET); - write(streamID, &c, 1); - } - - (void)close (streamID); - } else - printf("*** couldn't touch %s: %s", file, strerror(errno)); - } - } -} - -/*- - *----------------------------------------------------------------------- - * Job_CheckCommands -- - * Make sure the given node has all the commands it needs. - * - * Results: - * TRUE if the commands list is/was ok. - * - * Side Effects: - * The node will have commands from the .DEFAULT rule added to it - * if it needs them. - *----------------------------------------------------------------------- - */ -Boolean -Job_CheckCommands (gn, abortProc) - GNode *gn; /* The target whose commands need - * verifying */ - void (*abortProc) __P((char *, ...)); - /* Function to abort with message */ -{ - if (OP_NOP(gn->type) && Lst_IsEmpty (gn->commands) && - (gn->type & OP_LIB) == 0) { - /* - * No commands. Look for .DEFAULT rule from which we might infer - * commands - */ - if ((DEFAULT != NILGNODE) && !Lst_IsEmpty(DEFAULT->commands)) { - char *p1; - /* - * Make only looks for a .DEFAULT if the node was never the - * target of an operator, so that's what we do too. If - * a .DEFAULT was given, we substitute its commands for gn's - * commands and set the IMPSRC variable to be the target's name - * The DEFAULT node acts like a transformation rule, in that - * gn also inherits any attributes or sources attached to - * .DEFAULT itself. - */ - Make_HandleUse(DEFAULT, gn); - Var_Set (IMPSRC, Var_Value (TARGET, gn, &p1), gn); - if (p1) - free(p1); - } else if (Dir_MTime (gn) == 0) { - /* - * The node wasn't the target of an operator we have no .DEFAULT - * rule to go on and the target doesn't already exist. There's - * nothing more we can do for this branch. If the -k flag wasn't - * given, we stop in our tracks, otherwise we just don't update - * this node's parents so they never get examined. - */ - if (gn->type & OP_OPTIONAL) { - printf ("make: don't know how to make %s (ignored)\n", - gn->name); - } else if (keepgoing) { - printf ("make: don't know how to make %s (continuing)\n", - gn->name); - return (FALSE); - } else { - (*abortProc) ("make: don't know how to make %s. Stop", - gn->name); - return(FALSE); - } - } - } - return (TRUE); -} -#ifdef RMT_WILL_WATCH -/*- - *----------------------------------------------------------------------- - * JobLocalInput -- - * Handle a pipe becoming readable. Callback function for Rmt_Watch - * - * Results: - * None - * - * Side Effects: - * JobDoOutput is called. - * - *----------------------------------------------------------------------- - */ -/*ARGSUSED*/ -static void -JobLocalInput(stream, job) - int stream; /* Stream that's ready (ignored) */ - Job *job; /* Job to which the stream belongs */ -{ - JobDoOutput(job, FALSE); -} -#endif /* RMT_WILL_WATCH */ - -/*- - *----------------------------------------------------------------------- - * JobExec -- - * Execute the shell for the given job. Called from JobStart and - * JobRestart. - * - * Results: - * None. - * - * Side Effects: - * A shell is executed, outputs is altered and the Job structure added - * to the job table. - * - *----------------------------------------------------------------------- - */ -static void -JobExec(job, argv) - Job *job; /* Job to execute */ - char **argv; -{ - int cpid; /* ID of new child */ - - if (DEBUG(JOB)) { - int i; - - printf("Running %s %sly\n", job->node->name, - job->flags&JOB_REMOTE?"remote":"local"); - printf("\tCommand: "); - for (i = 0; argv[i] != (char *)NULL; i++) { - printf("%s ", argv[i]); - } - printf("\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_FIRST) && - !(job->flags & JOB_SILENT)) - { - printf(targFmt, job->node->name); - lastNode = job->node; - } - -#ifdef RMT_NO_EXEC - if (job->flags & JOB_REMOTE) { - goto jobExecFinish; - } -#endif /* RMT_NO_EXEC */ - - if ((cpid = vfork()) == -1) { - Punt ("Cannot fork"); - } else if (cpid == 0) { - - /* - * Must duplicate the input stream down to the child's input and - * reset it to the beginning (again). Since the stream was marked - * close-on-exec, we must clear that bit in the new input. - */ - (void) dup2(fileno(job->cmdFILE), 0); - fcntl(0, F_SETFD, 0); - lseek(0, 0, L_SET); - - if (usePipes) { - /* - * Set up the child's output to be routed through the pipe - * we've created for it. - */ - (void) dup2 (job->outPipe, 1); - } else { - /* - * We're capturing output in a file, so we duplicate the - * descriptor to the temporary file into the standard - * output. - */ - (void) dup2 (job->outFd, 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. - */ - fcntl(1, F_SETFD, 0); - (void) dup2 (1, 2); - -#ifdef USE_PGRP - /* - * We want to switch the child into a different process family so - * we can kill it and all its descendants in one fell swoop, - * by killing its process family, but not commit suicide. - */ - - (void) setpgrp(0, getpid()); -#endif USE_PGRP - - (void) execv (shellPath, argv); - (void) write (2, "Could not execute shell\n", - sizeof ("Could not execute shell")); - _exit (1); - } else { - job->pid = cpid; - - if (usePipes && (job->flags & JOB_FIRST) ) { - /* - * The first time a job is run for a node, we set the current - * position in the buffer to the beginning and mark another - * stream to watch in the outputs mask - */ - job->curPos = 0; - -#ifdef RMT_WILL_WATCH - Rmt_Watch(job->inPipe, JobLocalInput, job); -#else - FD_SET(job->inPipe, &outputs); -#endif /* RMT_WILL_WATCH */ - } - - if (job->flags & JOB_REMOTE) { - job->rmtID = 0; - } else { - nLocal += 1; - /* - * XXX: Used to not happen if CUSTOMS. Why? - */ - if (job->cmdFILE != stdout) { - fclose(job->cmdFILE); - job->cmdFILE = NULL; - } - } - } - -#ifdef RMT_NO_EXEC -jobExecFinish: -#endif - /* - * Now the job is actually running, add it to the table. - */ - nJobs += 1; - (void)Lst_AtEnd (jobs, (ClientData)job); - if (nJobs == maxJobs) { - jobFull = TRUE; - } -} - -/*- - *----------------------------------------------------------------------- - * JobMakeArgv -- - * Create the argv needed to execute the shell for a given job. - * - * - * Results: - * - * Side Effects: - * - *----------------------------------------------------------------------- - */ -static void -JobMakeArgv(job, argv) - Job *job; - char **argv; -{ - int argc; - static char args[10]; /* For merged arguments */ - - argv[0] = shellName; - argc = 1; - - if ((commandShell->exit && (*commandShell->exit != '-')) || - (commandShell->echo && (*commandShell->echo != '-'))) - { - /* - * At least one of the flags doesn't have a minus before it, so - * merge them together. Have to do this because the *(&(@*#*&#$# - * Bourne shell thinks its second argument is a file to source. - * Grrrr. Note the ten-character limitation on the combined arguments. - */ - (void)sprintf(args, "-%s%s", - ((job->flags & JOB_IGNERR) ? "" : - (commandShell->exit ? commandShell->exit : "")), - ((job->flags & JOB_SILENT) ? "" : - (commandShell->echo ? commandShell->echo : ""))); - - if (args[1]) { - argv[argc] = args; - argc++; - } - } else { - if (!(job->flags & JOB_IGNERR) && commandShell->exit) { - argv[argc] = commandShell->exit; - argc++; - } - if (!(job->flags & JOB_SILENT) && commandShell->echo) { - argv[argc] = commandShell->echo; - argc++; - } - } - argv[argc] = (char *)NULL; -} - -/*- - *----------------------------------------------------------------------- - * JobRestart -- - * Restart a job that stopped for some reason. - * - * Results: - * None. - * - * Side Effects: - * jobFull will be set if the job couldn't be run. - * - *----------------------------------------------------------------------- - */ -static void -JobRestart(job) - Job *job; /* Job to restart */ -{ - if (job->flags & JOB_REMIGRATE) { - if (DEBUG(JOB)) { - printf("Remigrating %x\n", job->pid); - } - if (nLocal != maxLocal) { - /* - * Job cannot be remigrated, but there's room on the local - * machine, so resume the job and note that another - * local job has started. - */ - if (DEBUG(JOB)) { - printf("resuming on local machine\n"); - } - KILL(job->pid, SIGCONT); - nLocal +=1; - job->flags &= ~(JOB_REMIGRATE|JOB_RESUME); - } else { - /* - * Job cannot be restarted. Mark the table as full and - * place the job back on the list of stopped jobs. - */ - if (DEBUG(JOB)) { - printf("holding\n"); - } - (void)Lst_AtFront(stoppedJobs, (ClientData)job); - jobFull = TRUE; - if (DEBUG(JOB)) { - printf("Job queue is full.\n"); - } - return; - } - - (void)Lst_AtEnd(jobs, (ClientData)job); - nJobs += 1; - if (nJobs == maxJobs) { - jobFull = TRUE; - if (DEBUG(JOB)) { - printf("Job queue is full.\n"); - } - } - } else if (job->flags & JOB_RESTART) { - /* - * Set up the control arguments to the shell. This is based on the - * flags set earlier for this job. If the JOB_IGNERR flag is clear, - * the 'exit' flag of the commandShell is used to cause it to exit - * upon receiving an error. If the JOB_SILENT flag is clear, the - * 'echo' flag of the commandShell is used to get it to start echoing - * as soon as it starts processing commands. - */ - char *argv[4]; - - JobMakeArgv(job, argv); - - if (DEBUG(JOB)) { - printf("Restarting %s...", job->node->name); - } - if (((nLocal >= maxLocal) && ! (job->flags & JOB_SPECIAL))) { - /* - * Can't be exported and not allowed to run locally -- put it - * back on the hold queue and mark the table full - */ - if (DEBUG(JOB)) { - printf("holding\n"); - } - (void)Lst_AtFront(stoppedJobs, (ClientData)job); - jobFull = TRUE; - if (DEBUG(JOB)) { - printf("Job queue is full.\n"); - } - return; - } else { - /* - * Job may be run locally. - */ - if (DEBUG(JOB)) { - printf("running locally\n"); - } - job->flags &= ~JOB_REMOTE; - } - JobExec(job, argv); - } else { - /* - * The job has stopped and needs to be restarted. Why it stopped, - * we don't know... - */ - if (DEBUG(JOB)) { - printf("Resuming %s...", job->node->name); - } - if (((job->flags & JOB_REMOTE) || - (nLocal < maxLocal) || - (((job->flags & JOB_SPECIAL)) && - (maxLocal == 0))) && - (nJobs != maxJobs)) - { - /* - * If the job is remote, it's ok to resume it as long as the - * maximum concurrency won't be exceeded. If it's local and - * we haven't reached the local concurrency limit already (or the - * job must be run locally and maxLocal is 0), it's also ok to - * resume it. - */ - Boolean error; - extern int errno; - union wait status; - -#ifdef RMT_WANTS_SIGNALS - if (job->flags & JOB_REMOTE) { - error = !Rmt_Signal(job, SIGCONT); - } else -#endif /* RMT_WANTS_SIGNALS */ - error = (KILL(job->pid, SIGCONT) != 0); - - if (!error) { - /* - * Make sure the user knows we've continued the beast and - * actually put the thing in the job table. - */ - job->flags |= JOB_CONTINUING; - status.w_termsig = SIGCONT; - JobFinish(job, status); - - job->flags &= ~(JOB_RESUME|JOB_CONTINUING); - if (DEBUG(JOB)) { - printf("done\n"); - } - } else { - Error("couldn't resume %s: %s", - job->node->name, strerror(errno)); - status.w_status = 0; - status.w_retcode = 1; - JobFinish(job, status); - } - } else { - /* - * Job cannot be restarted. Mark the table as full and - * place the job back on the list of stopped jobs. - */ - if (DEBUG(JOB)) { - printf("table full\n"); - } - (void)Lst_AtFront(stoppedJobs, (ClientData)job); - jobFull = TRUE; - if (DEBUG(JOB)) { - printf("Job queue is full.\n"); - } - } - } -} - -/*- - *----------------------------------------------------------------------- - * JobStart -- - * Start a target-creation process going for the target described - * by the graph node gn. - * - * Results: - * JOB_ERROR if there was an error in the commands, JOB_FINISHED - * if there isn't actually anything left to do for the job and - * JOB_RUNNING if the job has been started. - * - * Side Effects: - * A new Job node is created and added to the list of running - * jobs. PMake is forked and a child shell created. - *----------------------------------------------------------------------- - */ -static int -JobStart (gn, flags, previous) - GNode *gn; /* target to create */ - int flags; /* flags for the job to override normal ones. - * e.g. JOB_SPECIAL or JOB_IGNDOTS */ - Job *previous; /* The previous Job structure for this node, - * if any. */ -{ - register Job *job; /* new job descriptor */ - char *argv[4]; /* Argument vector to shell */ - static int jobno = 0; /* job number of catching output in a file */ - Boolean cmdsOK; /* true if the nodes commands were all right */ - Boolean local; /* Set true if the job was run locally */ - Boolean noExec; /* Set true if we decide not to run the job */ - - if (previous != (Job *)NULL) { - previous->flags &= ~ (JOB_FIRST|JOB_IGNERR|JOB_SILENT|JOB_REMOTE); - job = previous; - } else { - job = (Job *) emalloc (sizeof (Job)); - if (job == (Job *)NULL) { - Punt("JobStart out of memory"); - } - flags |= JOB_FIRST; - } - - job->node = gn; - job->tailCmds = NILLNODE; - - /* - * Set the initial value of the flags for this job based on the global - * ones and the node's attributes... Any flags supplied by the caller - * are also added to the field. - */ - job->flags = 0; - if (Targ_Ignore (gn)) { - job->flags |= JOB_IGNERR; - } - if (Targ_Silent (gn)) { - job->flags |= JOB_SILENT; - } - job->flags |= flags; - - /* - * Check the commands now so any attributes from .DEFAULT have a chance - * to migrate to the node - */ - if (job->flags & JOB_FIRST) { - cmdsOK = Job_CheckCommands(gn, Error); - } else { - cmdsOK = TRUE; - } - - /* - * 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) || (!noExecute && !touchFlag)) { - /* - * We're serious here, but if the commands were bogus, we're - * also dead... - */ - if (!cmdsOK) { - DieHorribly(); - } - - job->cmdFILE = fopen (tfile, "w+"); - if (job->cmdFILE == (FILE *) NULL) { - Punt ("Could not open %s", tfile); - } - fcntl(fileno(job->cmdFILE), F_SETFD, 1); - /* - * Send the commands to the command file, flush all its buffers then - * rewind and remove the thing. - */ - noExec = FALSE; - - /* - * used to be backwards; replace when start doing multiple commands - * per shell. - */ - if (compatMake) { - /* - * Be compatible: If this is the first time for this node, - * verify its commands are ok and open the commands list for - * sequential access by later invocations of JobStart. - * Once that is done, we take the next command off the list - * and print it to the command file. If the command was an - * ellipsis, note that there's nothing more to execute. - */ - if ((job->flags&JOB_FIRST) && (Lst_Open(gn->commands) != SUCCESS)){ - cmdsOK = FALSE; - } else { - LstNode ln = Lst_Next (gn->commands); - - if ((ln == NILLNODE) || - JobPrintCommand ((char *)Lst_Datum (ln), job)) - { - noExec = TRUE; - Lst_Close (gn->commands); - } - if (noExec && !(job->flags & JOB_FIRST)) { - /* - * If we're not going to execute anything, the job - * is done and we need to close down the various - * file descriptors we've opened for output, then - * call JobDoOutput to catch the final characters or - * send the file to the screen... Note that the i/o streams - * are only open if this isn't the first job. - * Note also that this could not be done in - * Job_CatchChildren b/c it wasn't clear if there were - * more commands to execute or not... - */ - if (usePipes) { -#ifdef RMT_WILL_WATCH - Rmt_Ignore(job->inPipe); -#else - FD_CLR(job->inPipe, &outputs); -#endif - if (job->outPipe != job->inPipe) { - (void)close (job->outPipe); - } - JobDoOutput (job, TRUE); - (void)close (job->inPipe); - } else { - (void)close (job->outFd); - JobDoOutput (job, TRUE); - } - } - } - } else { - /* - * We can do all the commands at once. hooray for sanity - */ - numCommands = 0; - Lst_ForEach (gn->commands, JobPrintCommand, (ClientData)job); - - /* - * If we didn't print out any commands to the shell script, - * there's not much point in executing the shell, is there? - */ - if (numCommands == 0) { - noExec = TRUE; - } - } - } else if (noExecute) { - /* - * 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) { - printf (targFmt, gn->name); - lastNode = gn; - } - job->cmdFILE = stdout; - /* - * Only print the commands if they're ok, but don't die if they're - * not -- just let the user know they're bad and keep going. It - * doesn't do any harm in this case and may do some good. - */ - if (cmdsOK) { - Lst_ForEach(gn->commands, JobPrintCommand, (ClientData)job); - } - /* - * Don't execute the shell, thank you. - */ - noExec = TRUE; - } else { - /* - * Just touch the target and note that no shell should be executed. - * Set cmdFILE to stdout to make life easier. Check the commands, too, - * but don't die if they're no good -- it does no harm to keep working - * up the graph. - */ - job->cmdFILE = stdout; - Job_Touch (gn, job->flags&JOB_SILENT); - noExec = TRUE; - } - - /* - * If we're not supposed to execute a shell, don't. - */ - if (noExec) { - /* - * Unlink and close the command file if we opened one - */ - if (job->cmdFILE != stdout) { - (void) unlink (tfile); - fclose(job->cmdFILE); - } else { - fflush (stdout); - } - - /* - * We only want to work our way up the graph if we aren't here because - * the commands for the job were no good. - */ - if (cmdsOK) { - if (aborting == 0) { - if (job->tailCmds != NILLNODE) { - Lst_ForEachFrom(job->node->commands, job->tailCmds, - JobSaveCommand, - (ClientData)job->node); - } - Make_Update(job->node); - } - free((Address)job); - return(JOB_FINISHED); - } else { - free((Address)job); - return(JOB_ERROR); - } - } else { - fflush (job->cmdFILE); - (void) unlink (tfile); - } - - /* - * Set up the control arguments to the shell. This is based on the flags - * set earlier for this job. - */ - JobMakeArgv(job, argv); - - /* - * If we're using pipes to catch output, create the pipe by which we'll - * get the shell's output. If we're using files, print out that we're - * starting a job and then set up its temporary-file name. This is just - * tfile with two extra digits tacked on -- jobno. - */ - if (job->flags & JOB_FIRST) { - if (usePipes) { - int fd[2]; - (void) pipe(fd); - job->inPipe = fd[0]; - job->outPipe = fd[1]; - (void)fcntl (job->inPipe, F_SETFD, 1); - (void)fcntl (job->outPipe, F_SETFD, 1); - } else { - printf ("Remaking `%s'\n", gn->name); - fflush (stdout); - sprintf (job->outFile, "%s%02d", tfile, jobno); - jobno = (jobno + 1) % 100; - job->outFd = open(job->outFile,O_WRONLY|O_CREAT|O_APPEND,0600); - (void)fcntl (job->outFd, F_SETFD, 1); - } - } - - local = TRUE; - - if (local && (((nLocal >= maxLocal) && - !(job->flags & JOB_SPECIAL) && - (maxLocal != 0)))) - { - /* - * The job can only be run locally, but we've hit the limit of - * local concurrency, so put the job on hold until some other job - * finishes. Note that the special jobs (.BEGIN, .INTERRUPT and .END) - * may be run locally even when the local limit has been reached - * (e.g. when maxLocal == 0), though they will be exported if at - * all possible. - */ - jobFull = TRUE; - - if (DEBUG(JOB)) { - printf("Can only run job locally.\n"); - } - job->flags |= JOB_RESTART; - (void)Lst_AtEnd(stoppedJobs, (ClientData)job); - } else { - if ((nLocal >= maxLocal) && local) { - /* - * If we're running this job locally as a special case (see above), - * at least say the table is full. - */ - jobFull = TRUE; - if (DEBUG(JOB)) { - printf("Local job queue is full.\n"); - } - } - JobExec(job, argv); - } - return(JOB_RUNNING); -} - -/*- - *----------------------------------------------------------------------- - * JobDoOutput -- - * This function is called at different times depending on - * whether the user has specified that output is to be collected - * via pipes or temporary files. In the former case, we are called - * whenever there is something to read on the pipe. We collect more - * output from the given job and store it in the job's outBuf. If - * this makes up a line, we print it tagged by the job's identifier, - * as necessary. - * If output has been collected in a temporary file, we open the - * file and read it line by line, transfering it to our own - * output channel until the file is empty. At which point we - * remove the temporary file. - * In both cases, however, we keep our figurative eye out for the - * 'noPrint' line for the shell from which the output came. If - * we recognize a line, we don't print it. If the command is not - * alone on the line (the character after it is not \0 or \n), we - * do print whatever follows it. - * - * Results: - * None - * - * Side Effects: - * curPos may be shifted as may the contents of outBuf. - *----------------------------------------------------------------------- - */ -static void -JobDoOutput (job, finish) - register Job *job; /* the job whose output needs printing */ - Boolean finish; /* TRUE if this is the last time we'll be - * called for this job */ -{ - Boolean gotNL = FALSE; /* true if got a newline */ - register int nr; /* number of bytes read */ - register int i; /* auxiliary index into outBuf */ - register int max; /* limit for i (end of current data) */ - int nRead; /* (Temporary) number of bytes read */ - - FILE *oFILE; /* Stream pointer to shell's output file */ - char inLine[132]; - - - if (usePipes) { - /* - * Read as many bytes as will fit in the buffer. - */ -end_loop: - - nRead = read (job->inPipe, &job->outBuf[job->curPos], - JOB_BUFSIZE - job->curPos); - if (nRead < 0) { - if (DEBUG(JOB)) { - perror("JobDoOutput(piperead)"); - } - nr = 0; - } else { - nr = nRead; - } - - /* - * If we hit the end-of-file (the job is dead), we must flush its - * remaining output, so pretend we read a newline if there's any - * output remaining in the buffer. - * Also clear the 'finish' flag so we stop looping. - */ - if ((nr == 0) && (job->curPos != 0)) { - job->outBuf[job->curPos] = '\n'; - nr = 1; - finish = FALSE; - } else if (nr == 0) { - finish = FALSE; - } - - /* - * Look for the last newline in the bytes we just got. If there is - * one, break out of the loop with 'i' as its index and gotNL set - * TRUE. - */ - max = job->curPos + nr; - for (i = job->curPos + nr - 1; i >= job->curPos; i--) { - if (job->outBuf[i] == '\n') { - gotNL = TRUE; - break; - } else if (job->outBuf[i] == '\0') { - /* - * Why? - */ - job->outBuf[i] = ' '; - } - } - - if (!gotNL) { - job->curPos += nr; - if (job->curPos == JOB_BUFSIZE) { - /* - * If we've run out of buffer space, we have no choice - * but to print the stuff. sigh. - */ - gotNL = TRUE; - i = job->curPos; - } - } - if (gotNL) { - /* - * 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, preceeded - * 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) { - register char *cp, *ecp; - - cp = job->outBuf; - if (commandShell->noPrint) { - ecp = Str_FindSubstring(job->outBuf, - commandShell->noPrint); - while (ecp != (char *)NULL) { - if (cp != ecp) { - *ecp = '\0'; - if (job->node != lastNode) { - printf (targFmt, job->node->name); - 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. - */ - printf ("%s", cp); - } - cp = ecp + commandShell->noPLen; - if (cp != &job->outBuf[i]) { - /* - * 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 { - break; - } - } - } - - /* - * There's still more in that thar buffer. This time, though, - * we know there's no newline at the end, so we add one of - * our own free will. - */ - if (*cp != '\0') { - if (job->node != lastNode) { - printf (targFmt, job->node->name); - lastNode = job->node; - } - printf ("%s\n", cp); - } - - fflush (stdout); - } - if (i < max - 1) { - /* shift the remaining characters down */ - memcpy ( job->outBuf, &job->outBuf[i + 1], max - (i + 1)); - job->curPos = max - (i + 1); - - } else { - /* - * We have written everything out, so we just start over - * from the start of the buffer. No copying. No nothing. - */ - job->curPos = 0; - } - } - if (finish) { - /* - * If the finish flag is true, we must loop until we hit - * end-of-file on the pipe. This is guaranteed to happen eventually - * since the other end of the pipe is now closed (we closed it - * explicitly and the child has exited). When we do get an EOF, - * finish will be set FALSE and we'll fall through and out. - */ - goto end_loop; - } - } else { - /* - * We've been called to retrieve the output of the job from the - * temporary file where it's been squirreled away. This consists of - * opening the file, reading the output line by line, being sure not - * to print the noPrint line for the shell we used, then close and - * remove the temporary file. Very simple. - * - * Change to read in blocks and do FindSubString type things as for - * pipes? That would allow for "@echo -n..." - */ - oFILE = fopen (job->outFile, "r"); - if (oFILE != (FILE *) NULL) { - printf ("Results of making %s:\n", job->node->name); - while (fgets (inLine, sizeof(inLine), oFILE) != NULL) { - register char *cp, *ecp, *endp; - - cp = inLine; - endp = inLine + strlen(inLine); - if (endp[-1] == '\n') { - *--endp = '\0'; - } - if (commandShell->noPrint) { - ecp = Str_FindSubstring(cp, commandShell->noPrint); - while (ecp != (char *)NULL) { - if (cp != ecp) { - *ecp = '\0'; - /* - * 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. - */ - printf ("%s", cp); - } - 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 { - break; - } - } - } - - /* - * 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') { - printf ("%s\n", cp); - } - } - fclose (oFILE); - (void) unlink (job->outFile); - } - } - fflush(stdout); -} - -/*- - *----------------------------------------------------------------------- - * Job_CatchChildren -- - * Handle the exit of a child. Called from Make_Make. - * - * Results: - * none. - * - * Side Effects: - * The job descriptor is removed from the list of children. - * - * Notes: - * We do waits, blocking or not, according to the wisdom of our - * caller, until there are no more children to report. For each - * job, call JobFinish to finish things off. This will take care of - * putting jobs on the stoppedJobs queue. - * - *----------------------------------------------------------------------- - */ -void -Job_CatchChildren (block) - Boolean block; /* TRUE if should block on the wait. */ -{ - int pid; /* pid of dead child */ - register Job *job; /* job descriptor for dead child */ - LstNode jnode; /* list element for finding job */ - union wait status; /* Exit/termination status */ - - /* - * Don't even bother if we know there's no one around. - */ - if (nLocal == 0) { - return; - } - - while ((pid = wait3((int *)&status, (block?0:WNOHANG)|WUNTRACED, - (struct rusage *)0)) > 0) - { - if (DEBUG(JOB)) - printf("Process %d exited or stopped.\n", pid); - - - jnode = Lst_Find (jobs, (ClientData)&pid, JobCmpPid); - - if (jnode == NILLNODE) { - if (WIFSIGNALED(status) && (status.w_termsig == SIGCONT)) { - jnode = Lst_Find(stoppedJobs, (ClientData) &pid, JobCmpPid); - if (jnode == NILLNODE) { - Error("Resumed child (%d) not in table", pid); - continue; - } - job = (Job *)Lst_Datum(jnode); - (void)Lst_Remove(stoppedJobs, jnode); - } else { - Error ("Child (%d) not in table?", pid); - continue; - } - } else { - job = (Job *) Lst_Datum (jnode); - (void)Lst_Remove (jobs, jnode); - nJobs -= 1; - if (jobFull && DEBUG(JOB)) { - printf("Job queue is no longer full.\n"); - } - jobFull = FALSE; - nLocal -= 1; - } - - JobFinish (job, status); - } -} - -/*- - *----------------------------------------------------------------------- - * Job_CatchOutput -- - * Catch the output from our children, if we're using - * pipes do so. Otherwise just block time until we get a - * signal (most likely a SIGCHLD) since there's no point in - * just spinning when there's nothing to do and the reaping - * of a child can wait for a while. - * - * Results: - * None - * - * Side Effects: - * Output is read from pipes if we're piping. - * ----------------------------------------------------------------------- - */ -void -Job_CatchOutput () -{ - int nfds; - struct timeval timeout; - fd_set readfds; - register LstNode ln; - register Job *job; -#ifdef RMT_WILL_WATCH - int pnJobs; /* Previous nJobs */ -#endif - - fflush(stdout); -#ifdef RMT_WILL_WATCH - pnJobs = nJobs; - - /* - * It is possible for us to be called with nJobs equal to 0. This happens - * if all the jobs finish and a job that is stopped cannot be run - * locally (eg if maxLocal is 0) and cannot be exported. The job will - * be placed back on the stoppedJobs queue, Job_Empty() will return false, - * Make_Run will call us again when there's nothing for which to wait. - * nJobs never changes, so we loop forever. Hence the check. It could - * be argued that we should sleep for a bit so as not to swamp the - * exportation system with requests. Perhaps we should. - * - * NOTE: IT IS THE RESPONSIBILITY OF Rmt_Wait TO CALL Job_CatchChildren - * IN A TIMELY FASHION TO CATCH ANY LOCALLY RUNNING JOBS THAT EXIT. - * It may use the variable nLocal to determine if it needs to call - * Job_CatchChildren (if nLocal is 0, there's nothing for which to - * wait...) - */ - while (nJobs != 0 && pnJobs == nJobs) { - Rmt_Wait(); - } -#else - if (usePipes) { - readfds = outputs; - timeout.tv_sec = SEL_SEC; - timeout.tv_usec = SEL_USEC; - - if ((nfds = select (FD_SETSIZE, &readfds, (fd_set *) 0, (fd_set *) 0, &timeout)) < 0) - { - return; - } else { - if (Lst_Open (jobs) == FAILURE) { - Punt ("Cannot open job table"); - } - while (nfds && (ln = Lst_Next (jobs)) != NILLNODE) { - job = (Job *) Lst_Datum (ln); - if (FD_ISSET(job->inPipe, &readfds)) { - JobDoOutput (job, FALSE); - nfds -= 1; - } - } - Lst_Close (jobs); - } - } -#endif /* RMT_WILL_WATCH */ -} - -/*- - *----------------------------------------------------------------------- - * Job_Make -- - * Start the creation of a target. Basically a front-end for - * JobStart used by the Make module. - * - * Results: - * None. - * - * Side Effects: - * Another job is started. - * - *----------------------------------------------------------------------- - */ -void -Job_Make (gn) - GNode *gn; -{ - (void)JobStart (gn, 0, (Job *)NULL); -} - -/*- - *----------------------------------------------------------------------- - * Job_Init -- - * Initialize the process module - * - * Results: - * none - * - * Side Effects: - * lists and counters are initialized - *----------------------------------------------------------------------- - */ -void -Job_Init (maxproc, maxlocal) - int maxproc; /* the greatest number of jobs which may be - * running at one time */ - int maxlocal; /* the greatest number of local jobs which may - * be running at once. */ -{ - GNode *begin; /* node for commands to do at the very start */ - - sprintf (tfile, "/tmp/make%05d", getpid()); - - jobs = Lst_Init (FALSE); - stoppedJobs = Lst_Init(FALSE); - maxJobs = maxproc; - maxLocal = maxlocal; - nJobs = 0; - nLocal = 0; - jobFull = FALSE; - - aborting = 0; - errors = 0; - - lastNode = NILGNODE; - - if (maxJobs == 1) { - /* - * If only one job can run at a time, there's no need for a banner, - * no is there? - */ - targFmt = ""; - } else { - targFmt = TARG_FMT; - } - - if (shellPath == (char *) NULL) { - /* - * The user didn't specify a shell to use, so we are using the - * default one... Both the absolute path and the last component - * must be set. The last component is taken from the 'name' field - * of the default shell description pointed-to by commandShell. - * All default shells are located in _PATH_DEFSHELLDIR. - */ - shellName = commandShell->name; - shellPath = str_concat (_PATH_DEFSHELLDIR, shellName, STR_ADDSLASH); - } - - if (commandShell->exit == (char *)NULL) { - commandShell->exit = ""; - } - if (commandShell->echo == (char *)NULL) { - commandShell->echo = ""; - } - - /* - * Catch the four signals that POSIX specifies if they aren't ignored. - * JobPassSig will take care of calling JobInterrupt if appropriate. - */ - if (signal (SIGINT, SIG_IGN) != SIG_IGN) { - signal (SIGINT, JobPassSig); - } - if (signal (SIGHUP, SIG_IGN) != SIG_IGN) { - signal (SIGHUP, JobPassSig); - } - if (signal (SIGQUIT, SIG_IGN) != SIG_IGN) { - signal (SIGQUIT, JobPassSig); - } - if (signal (SIGTERM, SIG_IGN) != SIG_IGN) { - signal (SIGTERM, JobPassSig); - } - /* - * There are additional signals that need to be caught and passed if - * either the export system wants to be told directly of signals or if - * we're giving each job its own process group (since then it won't get - * signals from the terminal driver as we own the terminal) - */ -#if defined(RMT_WANTS_SIGNALS) || defined(USE_PGRP) - if (signal (SIGTSTP, SIG_IGN) != SIG_IGN) { - signal (SIGTSTP, JobPassSig); - } - if (signal (SIGTTOU, SIG_IGN) != SIG_IGN) { - signal (SIGTTOU, JobPassSig); - } - if (signal (SIGTTIN, SIG_IGN) != SIG_IGN) { - signal (SIGTTIN, JobPassSig); - } - if (signal (SIGWINCH, SIG_IGN) != SIG_IGN) { - signal (SIGWINCH, JobPassSig); - } -#endif - - begin = Targ_FindNode (".BEGIN", TARG_NOCREATE); - - if (begin != NILGNODE) { - JobStart (begin, JOB_SPECIAL, (Job *)0); - while (nJobs) { - Job_CatchOutput(); -#ifndef RMT_WILL_WATCH - Job_CatchChildren (!usePipes); -#endif /* RMT_WILL_WATCH */ - } - } - postCommands = Targ_FindNode (".END", TARG_CREATE); -} - -/*- - *----------------------------------------------------------------------- - * Job_Full -- - * See if the job table is full. It is considered full if it is OR - * if we are in the process of aborting OR if we have - * reached/exceeded our local quota. This prevents any more jobs - * from starting up. - * - * Results: - * TRUE if the job table is full, FALSE otherwise - * Side Effects: - * None. - *----------------------------------------------------------------------- - */ -Boolean -Job_Full () -{ - return (aborting || jobFull); -} - -/*- - *----------------------------------------------------------------------- - * Job_Empty -- - * See if the job table is empty. Because the local concurrency may - * be set to 0, it is possible for the job table to become empty, - * while the list of stoppedJobs remains non-empty. In such a case, - * we want to restart as many jobs as we can. - * - * Results: - * TRUE if it is. FALSE if it ain't. - * - * Side Effects: - * None. - * - * ----------------------------------------------------------------------- - */ -Boolean -Job_Empty () -{ - if (nJobs == 0) { - if (!Lst_IsEmpty(stoppedJobs) && !aborting) { - /* - * The job table is obviously not full if it has no jobs in - * it...Try and restart the stopped jobs. - */ - jobFull = FALSE; - while (!jobFull && !Lst_IsEmpty(stoppedJobs)) { - JobRestart((Job *)Lst_DeQueue(stoppedJobs)); - } - return(FALSE); - } else { - return(TRUE); - } - } else { - return(FALSE); - } -} - -/*- - *----------------------------------------------------------------------- - * JobMatchShell -- - * Find a matching shell in 'shells' given its final component. - * - * Results: - * A pointer to the Shell structure. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -static Shell * -JobMatchShell (name) - char *name; /* Final component of shell path */ -{ - register Shell *sh; /* Pointer into shells table */ - Shell *match; /* Longest-matching shell */ - register char *cp1, - *cp2; - char *eoname; - - eoname = name + strlen (name); - - match = (Shell *) NULL; - - for (sh = shells; sh->name != NULL; sh++) { - for (cp1 = eoname - strlen (sh->name), cp2 = sh->name; - *cp1 != '\0' && *cp1 == *cp2; - cp1++, cp2++) { - continue; - } - if (*cp1 != *cp2) { - continue; - } else if (match == (Shell *) NULL || - strlen (match->name) < strlen (sh->name)) { - match = sh; - } - } - return (match == (Shell *) NULL ? sh : match); -} - -/*- - *----------------------------------------------------------------------- - * Job_ParseShell -- - * Parse a shell specification and set up commandShell, shellPath - * and shellName appropriately. - * - * Results: - * FAILURE if the specification was incorrect. - * - * Side Effects: - * commandShell points to a Shell structure (either predefined or - * created from the shell spec), shellPath is the full path of the - * shell described by commandShell, while shellName is just the - * final component of shellPath. - * - * Notes: - * A shell specification consists of a .SHELL target, with dependency - * operator, followed by a series of blank-separated words. Double - * quotes can be used to use blanks in words. A backslash escapes - * anything (most notably a double-quote and a space) and - * provides the functionality it does in C. Each word consists of - * keyword and value separated by an equal sign. There should be no - * unnecessary spaces in the word. The keywords are as follows: - * name Name of shell. - * path Location of shell. Overrides "name" if given - * quiet Command to turn off echoing. - * echo Command to turn echoing on - * filter Result of turning off echoing that shouldn't be - * printed. - * echoFlag Flag to turn echoing on at the start - * errFlag Flag to turn error checking on at the start - * hasErrCtl True if shell has error checking control - * check Command to turn on error checking if hasErrCtl - * is TRUE or template of command to echo a command - * for which error checking is off if hasErrCtl is - * FALSE. - * ignore Command to turn off error checking if hasErrCtl - * is TRUE or template of command to execute a - * command so as to ignore any errors it returns if - * hasErrCtl is FALSE. - * - *----------------------------------------------------------------------- - */ -ReturnStatus -Job_ParseShell (line) - char *line; /* The shell spec */ -{ - char **words; - int wordCount; - register char **argv; - register int argc; - char *path; - Shell newShell; - Boolean fullSpec = FALSE; - - while (isspace (*line)) { - line++; - } - words = brk_string (line, &wordCount, TRUE); - - memset ((Address)&newShell, 0, sizeof(newShell)); - - /* - * Parse the specification by keyword - */ - for (path = (char *)NULL, argc = wordCount - 1, argv = words + 1; - argc != 0; - argc--, argv++) { - if (strncmp (*argv, "path=", 5) == 0) { - path = &argv[0][5]; - } else if (strncmp (*argv, "name=", 5) == 0) { - newShell.name = &argv[0][5]; - } else { - if (strncmp (*argv, "quiet=", 6) == 0) { - newShell.echoOff = &argv[0][6]; - } else if (strncmp (*argv, "echo=", 5) == 0) { - newShell.echoOn = &argv[0][5]; - } else if (strncmp (*argv, "filter=", 7) == 0) { - newShell.noPrint = &argv[0][7]; - newShell.noPLen = strlen(newShell.noPrint); - } else if (strncmp (*argv, "echoFlag=", 9) == 0) { - newShell.echo = &argv[0][9]; - } else if (strncmp (*argv, "errFlag=", 8) == 0) { - newShell.exit = &argv[0][8]; - } else if (strncmp (*argv, "hasErrCtl=", 10) == 0) { - char c = argv[0][10]; - newShell.hasErrCtl = !((c != 'Y') && (c != 'y') && - (c != 'T') && (c != 't')); - } else if (strncmp (*argv, "check=", 6) == 0) { - newShell.errCheck = &argv[0][6]; - } else if (strncmp (*argv, "ignore=", 7) == 0) { - newShell.ignErr = &argv[0][7]; - } else { - Parse_Error (PARSE_FATAL, "Unknown keyword \"%s\"", - *argv); - return (FAILURE); - } - fullSpec = TRUE; - } - } - - if (path == (char *)NULL) { - /* - * If no path was given, the user wants one of the pre-defined shells, - * yes? So we find the one s/he wants with the help of JobMatchShell - * and set things up the right way. shellPath will be set up by - * Job_Init. - */ - if (newShell.name == (char *)NULL) { - Parse_Error (PARSE_FATAL, "Neither path nor name specified"); - return (FAILURE); - } else { - commandShell = JobMatchShell (newShell.name); - shellName = newShell.name; - } - } else { - /* - * The user provided a path. If s/he gave nothing else (fullSpec is - * FALSE), try and find a matching shell in the ones we know of. - * Else we just take the specification at its word and copy it - * to a new location. In either case, we need to record the - * path the user gave for the shell. - */ - shellPath = path; - path = strrchr (path, '/'); - if (path == (char *)NULL) { - path = shellPath; - } else { - path += 1; - } - if (newShell.name != (char *)NULL) { - shellName = newShell.name; - } else { - shellName = path; - } - if (!fullSpec) { - commandShell = JobMatchShell (shellName); - } else { - commandShell = (Shell *) emalloc(sizeof(Shell)); - *commandShell = newShell; - } - } - - if (commandShell->echoOn && commandShell->echoOff) { - commandShell->hasEchoCtl = TRUE; - } - - if (!commandShell->hasErrCtl) { - if (commandShell->errCheck == (char *)NULL) { - commandShell->errCheck = ""; - } - if (commandShell->ignErr == (char *)NULL) { - commandShell->ignErr = "%s\n"; - } - } - - /* - * Do not free up the words themselves, since they might be in use by the - * shell specification... - */ - free (words); - return SUCCESS; -} - -/*- - *----------------------------------------------------------------------- - * JobInterrupt -- - * Handle the receipt of an interrupt. - * - * Results: - * None - * - * Side Effects: - * All children are killed. Another job will be started if the - * .INTERRUPT target was given. - *----------------------------------------------------------------------- - */ -static void -JobInterrupt (runINTERRUPT) - int runINTERRUPT; /* Non-zero if commands for the .INTERRUPT - * target should be executed */ -{ - LstNode ln; /* element in job table */ - Job *job; /* job descriptor in that element */ - GNode *interrupt; /* the node describing the .INTERRUPT target */ - - aborting = ABORT_INTERRUPT; - - (void)Lst_Open (jobs); - while ((ln = Lst_Next (jobs)) != NILLNODE) { - job = (Job *) Lst_Datum (ln); - - if (!Targ_Precious (job->node)) { - char *file = (job->node->path == (char *)NULL ? - job->node->name : - job->node->path); - struct stat st; - if (!noExecute && lstat(file, &st) != -1 && !S_ISDIR(st.st_mode) && - unlink(file) != -1) { - Error ("*** %s removed", file); - } - } -#ifdef RMT_WANTS_SIGNALS - if (job->flags & JOB_REMOTE) { - /* - * If job is remote, let the Rmt module do the killing. - */ - if (!Rmt_Signal(job, SIGINT)) { - /* - * If couldn't kill the thing, finish it out now with an - * error code, since no exit report will come in likely. - */ - union wait status; - - status.w_status = 0; - status.w_retcode = 1; - JobFinish(job, status); - } - } else if (job->pid) { - KILL(job->pid, SIGINT); - } -#else - if (job->pid) { - KILL(job->pid, SIGINT); - } -#endif /* RMT_WANTS_SIGNALS */ - } - Lst_Close (jobs); - - if (runINTERRUPT && !touchFlag) { - interrupt = Targ_FindNode (".INTERRUPT", TARG_NOCREATE); - if (interrupt != NILGNODE) { - ignoreErrors = FALSE; - - JobStart (interrupt, JOB_IGNDOTS, (Job *)0); - while (nJobs) { - Job_CatchOutput(); -#ifndef RMT_WILL_WATCH - Job_CatchChildren (!usePipes); -#endif /* RMT_WILL_WATCH */ - } - } - } - (void) unlink (tfile); - exit (0); -} - -/* - *----------------------------------------------------------------------- - * Job_End -- - * Do final processing such as the running of the commands - * attached to the .END target. - * - * Results: - * Number of errors reported. - * - * Side Effects: - * The process' temporary file (tfile) is removed if it still - * existed. - *----------------------------------------------------------------------- - */ -int -Job_End () -{ - if (postCommands != NILGNODE && !Lst_IsEmpty (postCommands->commands)) { - if (errors) { - Error ("Errors reported so .END ignored"); - } else { - JobStart (postCommands, JOB_SPECIAL | JOB_IGNDOTS, - (Job *)0); - - while (nJobs) { - Job_CatchOutput(); -#ifndef RMT_WILL_WATCH - Job_CatchChildren (!usePipes); -#endif /* RMT_WILL_WATCH */ - } - } - } - (void) unlink (tfile); - return(errors); -} - -/*- - *----------------------------------------------------------------------- - * Job_Wait -- - * Waits for all running jobs to finish and returns. Sets 'aborting' - * to ABORT_WAIT to prevent other jobs from starting. - * - * Results: - * None. - * - * Side Effects: - * Currently running jobs finish. - * - *----------------------------------------------------------------------- - */ -void -Job_Wait() -{ - aborting = ABORT_WAIT; - while (nJobs != 0) { - Job_CatchOutput(); -#ifndef RMT_WILL_WATCH - Job_CatchChildren(!usePipes); -#endif /* RMT_WILL_WATCH */ - } - aborting = 0; -} - -/*- - *----------------------------------------------------------------------- - * Job_AbortAll -- - * Abort all currently running jobs without handling output or anything. - * This function is to be called only in the event of a major - * error. Most definitely NOT to be called from JobInterrupt. - * - * Results: - * None - * - * Side Effects: - * All children are killed, not just the firstborn - *----------------------------------------------------------------------- - */ -void -Job_AbortAll () -{ - LstNode ln; /* element in job table */ - Job *job; /* the job descriptor in that element */ - int foo; - - aborting = ABORT_ERROR; - - if (nJobs) { - - (void)Lst_Open (jobs); - while ((ln = Lst_Next (jobs)) != NILLNODE) { - job = (Job *) Lst_Datum (ln); - - /* - * kill the child process with increasingly drastic signals to make - * darn sure it's dead. - */ -#ifdef RMT_WANTS_SIGNALS - if (job->flags & JOB_REMOTE) { - Rmt_Signal(job, SIGINT); - Rmt_Signal(job, SIGKILL); - } else { - KILL(job->pid, SIGINT); - KILL(job->pid, SIGKILL); - } -#else - KILL(job->pid, SIGINT); - KILL(job->pid, SIGKILL); -#endif /* RMT_WANTS_SIGNALS */ - } - } - - /* - * Catch as many children as want to report in at first, then give up - */ - while (wait3(&foo, WNOHANG, (struct rusage *)0) > 0) - continue; - (void) unlink (tfile); -} diff --git a/usr.bin/make/job.h b/usr.bin/make/job.h deleted file mode 100644 index 5feb43fb6e43..000000000000 --- a/usr.bin/make/job.h +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)job.h 8.2 (Berkeley) 4/28/95 - */ - -/*- - * job.h -- - * Definitions pertaining to the running of jobs in parallel mode. - * Exported from job.c for the use of remote-execution modules. - */ -#ifndef _JOB_H_ -#define _JOB_H_ - -#define TMPPAT "/tmp/makeXXXXX" - -/* - * The SEL_ constants determine the maximum amount of time spent in select - * before coming out to see if a child has finished. SEL_SEC is the number of - * seconds and SEL_USEC is the number of micro-seconds - */ -#define SEL_SEC 0 -#define SEL_USEC 500000 - - -/*- - * Job Table definitions. - * - * Each job has several things associated with it: - * 1) The process id of the child shell - * 2) The graph node describing the target being made by this job - * 3) A LstNode for the first command to be saved after the job - * completes. This is NILLNODE if there was no "..." in the job's - * commands. - * 4) An FILE* for writing out the commands. This is only - * used before the job is actually started. - * 5) A union of things used for handling the shell's output. Different - * parts of the union are used based on the value of the usePipes - * flag. If it is true, the output is being caught via a pipe and - * the descriptors of our pipe, an array in which output is line - * buffered and the current position in that buffer are all - * maintained for each job. If, on the other hand, usePipes is false, - * the output is routed to a temporary file and all that is kept - * is the name of the file and the descriptor open to the file. - * 6) An identifier provided by and for the exclusive use of the - * Rmt module. - * 7) A word of flags which determine how the module handles errors, - * echoing, etc. for the job - * - * The job "table" is kept as a linked Lst in 'jobs', with the number of - * active jobs maintained in the 'nJobs' variable. At no time will this - * exceed the value of 'maxJobs', initialized by the Job_Init function. - * - * When a job is finished, the Make_Update function is called on each of the - * parents of the node which was just remade. This takes care of the upward - * traversal of the dependency graph. - */ -#define JOB_BUFSIZE 1024 -typedef struct Job { - int pid; /* The child's process ID */ - GNode *node; /* The target the child is making */ - LstNode tailCmds; /* The node of the first command to be - * saved when the job has been run */ - FILE *cmdFILE; /* When creating the shell script, this is - * where the commands go */ - int rmtID; /* ID returned from Rmt module */ - short flags; /* Flags to control treatment of job */ -#define JOB_IGNERR 0x001 /* Ignore non-zero exits */ -#define JOB_SILENT 0x002 /* no output */ -#define JOB_SPECIAL 0x004 /* Target is a special one. i.e. run it locally - * if we can't export it and maxLocal is 0 */ -#define JOB_IGNDOTS 0x008 /* Ignore "..." lines when processing - * commands */ -#define JOB_REMOTE 0x010 /* Job is running remotely */ -#define JOB_FIRST 0x020 /* Job is first job for the node */ -#define JOB_REMIGRATE 0x040 /* Job needs to be remigrated */ -#define JOB_RESTART 0x080 /* Job needs to be completely restarted */ -#define JOB_RESUME 0x100 /* Job needs to be resumed b/c it stopped, - * for some reason */ -#define JOB_CONTINUING 0x200 /* We are in the process of resuming this job. - * Used to avoid infinite recursion between - * JobFinish and JobRestart */ - union { - struct { - int op_inPipe; /* Input side of pipe associated - * with job's output channel */ - int op_outPipe; /* Output side of pipe associated with - * job's output channel */ - char op_outBuf[JOB_BUFSIZE + 1]; - /* Buffer for storing the output of the - * job, line by line */ - int op_curPos; /* Current position in op_outBuf */ - } o_pipe; /* data used when catching the output via - * a pipe */ - struct { - char of_outFile[sizeof(TMPPAT)+2]; - /* Name of file to which shell output - * was rerouted */ - int of_outFd; /* Stream open to the output - * file. Used to funnel all - * from a single job to one file - * while still allowing - * multiple shell invocations */ - } o_file; /* Data used when catching the output in - * a temporary file */ - } output; /* Data for tracking a shell's output */ -} Job; - -#define outPipe output.o_pipe.op_outPipe -#define inPipe output.o_pipe.op_inPipe -#define outBuf output.o_pipe.op_outBuf -#define curPos output.o_pipe.op_curPos -#define outFile output.o_file.of_outFile -#define outFd output.o_file.of_outFd - - -/*- - * Shell Specifications: - * Each shell type has associated with it the following information: - * 1) The string which must match the last character of the shell name - * for the shell to be considered of this type. The longest match - * wins. - * 2) A command to issue to turn off echoing of command lines - * 3) A command to issue to turn echoing back on again - * 4) What the shell prints, and its length, when given the echo-off - * command. This line will not be printed when received from the shell - * 5) A boolean to tell if the shell has the ability to control - * error checking for individual commands. - * 6) The string to turn this checking on. - * 7) The string to turn it off. - * 8) The command-flag to give to cause the shell to start echoing - * commands right away. - * 9) The command-flag to cause the shell to Lib_Exit when an error is - * detected in one of the commands. - * - * Some special stuff goes on if a shell doesn't have error control. In such - * a case, errCheck becomes a printf template for echoing the command, - * should echoing be on and ignErr becomes another printf template for - * executing the command while ignoring the return status. If either of these - * strings is empty when hasErrCtl is FALSE, the command will be executed - * anyway as is and if it causes an error, so be it. - */ -typedef struct Shell { - char *name; /* the name of the shell. For Bourne and C - * shells, this is used only to find the - * shell description when used as the single - * source of a .SHELL target. For user-defined - * shells, this is the full path of the shell. - */ - Boolean hasEchoCtl; /* True if both echoOff and echoOn defined */ - char *echoOff; /* command to turn off echo */ - char *echoOn; /* command to turn it back on again */ - char *noPrint; /* command to skip when printing output from - * shell. This is usually the command which - * was executed to turn off echoing */ - int noPLen; /* length of noPrint command */ - Boolean hasErrCtl; /* set if can control error checking for - * individual commands */ - char *errCheck; /* string to turn error checking on */ - char *ignErr; /* string to turn off error checking */ - /* - * command-line flags - */ - char *echo; /* echo commands */ - char *exit; /* exit on error */ -} Shell; - - -extern char *targFmt; /* Format string for banner that separates - * output from multiple jobs. Contains a - * single %s where the name of the node being - * made should be put. */ -extern GNode *lastNode; /* Last node for which a banner was printed. - * If Rmt module finds it necessary to print - * a banner, it should set this to the node - * for which the banner was printed */ -extern int nJobs; /* Number of jobs running (local and remote) */ -extern int nLocal; /* Number of jobs running locally */ -extern Lst jobs; /* List of active job descriptors */ -extern Lst stoppedJobs; /* List of jobs that are stopped or didn't - * quite get started */ -extern Boolean jobFull; /* Non-zero if no more jobs should/will start*/ - - -void Job_Touch __P((GNode *, Boolean)); -Boolean Job_CheckCommands __P((GNode *, void (*abortProc )(char *, ...))); -void Job_CatchChildren __P((Boolean)); -void Job_CatchOutput __P((void)); -void Job_Make __P((GNode *)); -void Job_Init __P((int, int)); -Boolean Job_Full __P((void)); -Boolean Job_Empty __P((void)); -ReturnStatus Job_ParseShell __P((char *)); -int Job_End __P((void)); -void Job_Wait __P((void)); -void Job_AbortAll __P((void)); -void JobFlagForMigration __P((int)); - -#endif /* _JOB_H_ */ diff --git a/usr.bin/make/list.h b/usr.bin/make/list.h deleted file mode 100644 index 41b9a1930293..000000000000 --- a/usr.bin/make/list.h +++ /dev/null @@ -1,298 +0,0 @@ -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)list.h 8.2 (Berkeley) 4/28/95 - */ - -/* - * list.h -- - * - * Structures, macros, and routines exported by the List module. - */ - -#ifndef _LIST -#define _LIST - -#ifndef _SPRITE -#include "sprite.h" -#endif _SPRITE - -/* - * This module defines the list abstraction, which enables one to link - * together arbitrary data structures. Lists are doubly-linked and - * circular. A list contains a header followed by its real members, if - * any. (An empty list therefore consists of a single element, the - * header, whose nextPtr and prevPtr fields point to itself). To refer - * to a list as a whole, the user keeps a pointer to the header; that - * header is initialized by a call to List_Init(), which creates an empty - * list given a pointer to a List_Links structure (described below). - * - * The links are contained in a two-element structure called List_Links. - * A list joins List_Links records (that is, each List_Links structure - * points to other List_Links structures), but if the List_Links is the - * first field within a larger structure, then the larger structures are - * effectively linked together as follows: - * - * header - * (List_Links) first elt. second elt. - * ----------------- ----------------- ----------------- - * ..-> | nextPtr | ----> | List_Links | ----> | List_Links |----.. - * | - - - - - - - | | | | | - * ..-- | prevPtr | <---- | | <---- | |<---.. - * ----------------- - --- --- --- - - --- --- --- - - * | rest of | | rest of | - * | structure | | structure | - * | | | | - * | ... | | ... | - * ----------------- ----------------- - * - * It is possible to link structures through List_Links fields that are - * not at the beginning of the larger structure, but it is then necessary - * to perform pointer arithmetic to find the beginning of the larger - * structure, given a pointer to some point within it. - * - * A typical structure might be something like: - * - * typedef struct { - * List_Links links; - * char ch; - * integer flags; - * } EditChar; - * - * Before an element is inserted in a list for the first time, it must - * be initialized by calling the macro List_InitElement(). - */ - - -/* - * data structure for lists - */ - -typedef struct List_Links { - struct List_Links *prevPtr; - struct List_Links *nextPtr; -} List_Links; - -/* - * procedures - */ - -void List_Init(); /* initialize a header to a list */ -void List_Insert(); /* insert an element into a list */ -void List_Remove(); /* remove an element from a list */ -void List_Move(); /* move an element elsewhere in a list */ - -/* - * ---------------------------------------------------------------------------- - * - * List_InitElement -- - * - * Initialize a list element. Must be called before an element is first - * inserted into a list. - * - * ---------------------------------------------------------------------------- - */ -#define List_InitElement(elementPtr) \ - (elementPtr)->prevPtr = (List_Links *) NIL; \ - (elementPtr)->nextPtr = (List_Links *) NIL; - -/* - * Macros for stepping through or selecting parts of lists - */ - -/* - * ---------------------------------------------------------------------------- - * - * LIST_FORALL -- - * - * Macro to loop through a list and perform an operation on each member. - * - * Usage: LIST_FORALL(headerPtr, itemPtr) { - * / * - * * operation on itemPtr, which points to successive members - * * of the list - * * - * * It may be appropriate to first assign - * * foobarPtr = (Foobar *) itemPtr; - * * to refer to the entire Foobar structure. - * * / - * } - * - * Note: itemPtr must be a List_Links pointer variable, and headerPtr - * must evaluate to a pointer to a List_Links structure. - * - * ---------------------------------------------------------------------------- - */ - -#define LIST_FORALL(headerPtr, itemPtr) \ - for (itemPtr = List_First(headerPtr); \ - !List_IsAtEnd((headerPtr),itemPtr); \ - itemPtr = List_Next(itemPtr)) - -/* - * ---------------------------------------------------------------------------- - * - * List_IsEmpty -- - * - * Macro: Boolean value, TRUE if the given list does not contain any - * members. - * - * Usage: if (List_IsEmpty(headerPtr)) ... - * - * ---------------------------------------------------------------------------- - */ - -#define List_IsEmpty(headerPtr) \ - ((headerPtr) == (headerPtr)->nextPtr) - -/* - * ---------------------------------------------------------------------------- - * - * List_IsAtEnd -- - * - * Macro: Boolean value, TRUE if itemPtr is after the end of headerPtr - * (i.e., itemPtr is the header of the list). - * - * Usage: if (List_IsAtEnd(headerPtr, itemPtr)) ... - * - * ---------------------------------------------------------------------------- - */ - - -#define List_IsAtEnd(headerPtr, itemPtr) \ - ((itemPtr) == (headerPtr)) - - -/* - * ---------------------------------------------------------------------------- - * - * List_First -- - * - * Macro to return the first member in a list, which is the header if - * the list is empty. - * - * Usage: firstPtr = List_First(headerPtr); - * - * ---------------------------------------------------------------------------- - */ - -#define List_First(headerPtr) ((headerPtr)->nextPtr) - -/* - * ---------------------------------------------------------------------------- - * - * List_Last -- - * - * Macro to return the last member in a list, which is the header if - * the list is empty. - * - * Usage: lastPtr = List_Last(headerPtr); - * - * ---------------------------------------------------------------------------- - */ - -#define List_Last(headerPtr) ((headerPtr)->prevPtr) - -/* - * ---------------------------------------------------------------------------- - * - * List_Prev -- - * - * Macro to return the member preceding the given member in its list. - * If the given list member is the first element in the list, List_Prev - * returns the list header. - * - * Usage: prevPtr = List_Prev(itemPtr); - * - * ---------------------------------------------------------------------------- - */ - -#define List_Prev(itemPtr) ((itemPtr)->prevPtr) - -/* - * ---------------------------------------------------------------------------- - * - * List_Next -- - * - * Macro to return the member following the given member in its list. - * If the given list member is the last element in the list, List_Next - * returns the list header. - * - * Usage: nextPtr = List_Next(itemPtr); - * - * ---------------------------------------------------------------------------- - */ - -#define List_Next(itemPtr) ((itemPtr)->nextPtr) - - -/* - * ---------------------------------------------------------------------------- - * The List_Insert procedure takes two arguments. The first argument - * is a pointer to the structure to be inserted into a list, and - * the second argument is a pointer to the list member after which - * the new element is to be inserted. Macros are used to determine - * which existing member will precede the new one. - * - * The List_Move procedure takes a destination argument with the same - * semantics as List_Insert. - * - * The following macros define where to insert the new element - * in the list: - * - * LIST_AFTER(itemPtr) -- insert after itemPtr - * LIST_BEFORE(itemPtr) -- insert before itemPtr - * LIST_ATFRONT(headerPtr) -- insert at front of list - * LIST_ATREAR(headerPtr) -- insert at end of list - * - * For example, - * - * List_Insert(itemPtr, LIST_AFTER(otherPtr)); - * - * will insert itemPtr following otherPtr in the list containing otherPtr. - * ---------------------------------------------------------------------------- - */ - -#define LIST_AFTER(itemPtr) ((List_Links *) itemPtr) - -#define LIST_BEFORE(itemPtr) (((List_Links *) itemPtr)->prevPtr) - -#define LIST_ATFRONT(headerPtr) ((List_Links *) headerPtr) - -#define LIST_ATREAR(headerPtr) (((List_Links *) headerPtr)->prevPtr) - -#endif /* _LIST */ diff --git a/usr.bin/make/lst.lib/Makefile b/usr.bin/make/lst.lib/Makefile deleted file mode 100644 index 1e73eca40479..000000000000 --- a/usr.bin/make/lst.lib/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# $NetBSD: Makefile,v 1.3 1995/06/14 15:20:42 christos Exp $ - -OBJ=lstAppend.o lstDupl.o lstInit.o lstOpen.o lstAtEnd.o lstEnQueue.o \ - lstInsert.o lstAtFront.o lstIsAtEnd.o lstClose.o lstFind.o lstIsEmpty.o \ - lstRemove.o lstConcat.o lstFindFrom.o lstLast.o lstReplace.o lstFirst.o \ - lstDatum.o lstForEach.o lstMember.o lstSucc.o lstDeQueue.o \ - lstForEachFrom.o lstDestroy.o lstNext.o - -CFLAGS=-I.. -all: ${OBJ} diff --git a/usr.bin/make/main.c b/usr.bin/make/main.c deleted file mode 100644 index 8607fbb08bb6..000000000000 --- a/usr.bin/make/main.c +++ /dev/null @@ -1,954 +0,0 @@ -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char copyright[] = -"@(#) Copyright (c) 1988, 1989, 1990, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)main.c 8.4 (Berkeley) 4/28/95"; -#endif /* not lint */ - -/*- - * 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 occured, as passed to it, and - * exiting. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if __STDC__ -#include -#else -#include -#endif -#include "make.h" -#include "hash.h" -#include "dir.h" -#include "job.h" -#include "pathnames.h" - -#ifndef DEFMAXLOCAL -#define DEFMAXLOCAL DEFMAXJOBS -#endif DEFMAXLOCAL - -#define MAKEFLAGS ".MAKEFLAGS" - -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 */ -int maxJobs; /* -J argument */ -static int maxLocal; /* -L argument */ -Boolean compatMake; /* -B argument */ -Boolean debug; /* -d flag */ -Boolean noExecute; /* -n flag */ -Boolean keepgoing; /* -k flag */ -Boolean queryFlag; /* -q flag */ -Boolean touchFlag; /* -t flag */ -Boolean usePipes; /* !-P flag */ -Boolean ignoreErrors; /* -i flag */ -Boolean beSilent; /* -s flag */ -Boolean oldVars; /* variable substitution style */ -Boolean checkEnvFirst; /* -e flag */ -static Boolean jobsRunning; /* TRUE if the jobs might be running */ - -static Boolean ReadMakefile(); -static void usage(); - -static char *curdir; /* startup directory */ -static char *objdir; /* where we chdir'ed to */ - -/*- - * MainParseArgs -- - * Parse a given argument vector. Called from main() and from - * Main_ParseArgLine() when the .MAKEFLAGS target is used. - * - * XXX: Deal with command line overriding .MAKEFLAGS in makefile - * - * Results: - * None - * - * Side Effects: - * Various global and local flags will be set depending on the flags - * given - */ -static void -MainParseArgs(argc, argv) - int argc; - char **argv; -{ - extern int optind; - extern char *optarg; - int c; - - optind = 1; /* since we're called more than once */ -#ifdef notyet -# define OPTFLAGS "BD:I:L:PSd:ef:ij:knqrst" -#else -# define OPTFLAGS "D:I:d:ef:ij:knqrst" -#endif -rearg: while((c = getopt(argc, argv, OPTFLAGS)) != EOF) { - switch(c) { - case 'D': - Var_Set(optarg, "1", VAR_GLOBAL); - Var_Append(MAKEFLAGS, "-D", VAR_GLOBAL); - Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL); - break; - case 'I': - Parse_AddIncludeDir(optarg); - Var_Append(MAKEFLAGS, "-I", VAR_GLOBAL); - Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL); - break; -#ifdef notyet - case 'B': - compatMake = TRUE; - break; - case 'L': - maxLocal = atoi(optarg); - Var_Append(MAKEFLAGS, "-L", VAR_GLOBAL); - Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL); - break; - case 'P': - usePipes = FALSE; - Var_Append(MAKEFLAGS, "-P", VAR_GLOBAL); - break; - case 'S': - keepgoing = FALSE; - Var_Append(MAKEFLAGS, "-S", VAR_GLOBAL); - break; -#endif - case 'd': { - char *modules = optarg; - - for (; *modules; ++modules) - switch (*modules) { - case 'A': - debug = ~0; - break; - case 'a': - debug |= DEBUG_ARCH; - break; - case 'c': - debug |= DEBUG_COND; - break; - case 'd': - debug |= DEBUG_DIR; - break; - case 'f': - debug |= DEBUG_FOR; - break; - case 'g': - if (modules[1] == '1') { - debug |= DEBUG_GRAPH1; - ++modules; - } - else if (modules[1] == '2') { - debug |= DEBUG_GRAPH2; - ++modules; - } - break; - case 'j': - debug |= DEBUG_JOB; - break; - case 'm': - debug |= DEBUG_MAKE; - break; - case 's': - debug |= DEBUG_SUFF; - break; - case 't': - debug |= DEBUG_TARG; - break; - case 'v': - debug |= DEBUG_VAR; - break; - default: - (void)fprintf(stderr, - "make: illegal argument to d option -- %c\n", - *modules); - usage(); - } - Var_Append(MAKEFLAGS, "-d", VAR_GLOBAL); - Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL); - break; - } - case 'e': - checkEnvFirst = TRUE; - Var_Append(MAKEFLAGS, "-e", VAR_GLOBAL); - break; - case 'f': - (void)Lst_AtEnd(makefiles, (ClientData)optarg); - break; - case 'i': - ignoreErrors = TRUE; - Var_Append(MAKEFLAGS, "-i", VAR_GLOBAL); - break; - case 'j': - maxJobs = atoi(optarg); - Var_Append(MAKEFLAGS, "-j", VAR_GLOBAL); - Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL); - break; - case 'k': - keepgoing = TRUE; - Var_Append(MAKEFLAGS, "-k", VAR_GLOBAL); - break; - case 'n': - noExecute = TRUE; - Var_Append(MAKEFLAGS, "-n", VAR_GLOBAL); - break; - case 'q': - queryFlag = TRUE; - /* Kind of nonsensical, wot? */ - Var_Append(MAKEFLAGS, "-q", VAR_GLOBAL); - break; - case 'r': - noBuiltins = TRUE; - Var_Append(MAKEFLAGS, "-r", VAR_GLOBAL); - break; - case 's': - beSilent = TRUE; - Var_Append(MAKEFLAGS, "-s", VAR_GLOBAL); - break; - case 't': - touchFlag = TRUE; - Var_Append(MAKEFLAGS, "-t", VAR_GLOBAL); - break; - default: - case '?': - usage(); - } - } - - oldVars = TRUE; - - /* - * See if the rest of the arguments are variable assignments and - * perform them if so. Else take them to be targets and stuff them - * on the end of the "create" list. - */ - for (argv += optind, argc -= optind; *argv; ++argv, --argc) - if (Parse_IsVar(*argv)) - Parse_DoVar(*argv, VAR_CMD); - else { - if (!**argv) - Punt("illegal (null) argument."); - if (**argv == '-') { - if ((*argv)[1]) - optind = 0; /* -flag... */ - else - optind = 1; /* - */ - goto rearg; - } - (void)Lst_AtEnd(create, (ClientData)strdup(*argv)); - } -} - -/*- - * Main_ParseArgLine -- - * Used by the parse module when a .MFLAGS or .MAKEFLAGS target - * is encountered and by main() when reading the .MAKEFLAGS envariable. - * Takes a line of arguments and breaks it into its - * component words and passes those words and the number of them to the - * MainParseArgs function. - * The line should have all its leading whitespace removed. - * - * Results: - * None - * - * Side Effects: - * Only those that come from the various arguments. - */ -void -Main_ParseArgLine(line) - char *line; /* Line to fracture */ -{ - char **argv; /* Manufactured argument vector */ - int argc; /* Number of arguments in argv */ - - if (line == NULL) - return; - for (; *line == ' '; ++line) - continue; - if (!*line) - return; - - argv = brk_string(line, &argc, TRUE); - MainParseArgs(argc, argv); -} - -/*- - * main -- - * The main function, for obvious reasons. Initializes variables - * and a few modules, then parses the arguments give it in the - * environment and on the command line. Reads the system makefile - * followed by either Makefile, makefile or the file given by the - * -f argument. Sets the .MAKEFLAGS PMake variable based on all the - * flags it has received by then uses either the Make or the Compat - * module to create the initial list of targets. - * - * Results: - * If -q was given, exits -1 if anything was out-of-date. Else it exits - * 0. - * - * Side Effects: - * The program exits when done. Targets are created. etc. etc. etc. - */ -int -main(argc, argv) - int argc; - char **argv; -{ - Lst targs; /* target nodes to create -- passed to Make_Init */ - Boolean outOfDate = TRUE; /* FALSE if all targets up to date */ - struct stat sb, sa; - char *p, *p1, *path, *pwd, *getenv(), *getwd(); - char mdpath[MAXPATHLEN + 1]; - char obpath[MAXPATHLEN + 1]; - char cdpath[MAXPATHLEN + 1]; - struct utsname utsname; - char *machine = getenv("MACHINE"); - - /* - * Find where we are and take care of PWD for the automounter... - * All this code is so that we know where we are when we start up - * on a different machine with pmake. - */ - curdir = cdpath; - if (getcwd(curdir, MAXPATHLEN) == NULL) { - (void)fprintf(stderr, "make: %s.\n", strerror(errno)); - exit(2); - } - - if (stat(curdir, &sa) == -1) { - (void)fprintf(stderr, "make: %s: %s.\n", - curdir, strerror(errno)); - exit(2); - } - - if ((pwd = getenv("PWD")) != NULL) { - if (stat(pwd, &sb) == 0 && sa.st_ino == sb.st_ino && - sa.st_dev == sb.st_dev) - (void) strcpy(curdir, pwd); - } - - /* - * 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 while MACHINE is decided at run-time, - * MACHINE_ARCH is always known at compile time. - */ - if (!machine) { - if (uname(&utsname)) { - perror("make: uname"); - exit(2); - } - machine = utsname.machine; - } - - /* - * if the MAKEOBJDIR (or by default, the _PATH_OBJDIR) directory - * exists, change into it and build there. Once things are - * initted, have to add the original directory to the search path, - * and modify the paths for the Makefiles apropriately. The - * current directory is also placed as a variable for make scripts. - */ - if (!(path = getenv("MAKEOBJDIR"))) { - path = _PATH_OBJDIR; - (void) sprintf(mdpath, "%s.%s", path, machine); - } - else - (void) strncpy(mdpath, path, MAXPATHLEN + 1); - - if (stat(mdpath, &sb) == 0 && S_ISDIR(sb.st_mode)) { - - if (chdir(mdpath)) { - (void)fprintf(stderr, "make warning: %s: %s.\n", - mdpath, strerror(errno)); - objdir = curdir; - } - else { - if (mdpath[0] != '/') { - (void) sprintf(obpath, "%s/%s", curdir, mdpath); - objdir = obpath; - } - else - objdir = mdpath; - } - } - else { - if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) { - - if (chdir(path)) { - (void)fprintf(stderr, "make warning: %s: %s.\n", - path, strerror(errno)); - objdir = curdir; - } - else { - if (path[0] != '/') { - (void) sprintf(obpath, "%s/%s", curdir, - path); - objdir = obpath; - } - else - objdir = obpath; - } - } - else - objdir = curdir; - } - - setenv("PWD", objdir, 1); - - create = Lst_Init(FALSE); - makefiles = Lst_Init(FALSE); - beSilent = FALSE; /* Print commands as executed */ - ignoreErrors = FALSE; /* Pay attention to non-zero returns */ - noExecute = FALSE; /* Execute all commands */ - keepgoing = FALSE; /* Stop on error */ - allPrecious = FALSE; /* Remove targets when interrupted */ - queryFlag = FALSE; /* This is not just a check-run */ - noBuiltins = FALSE; /* Read the built-in rules */ - touchFlag = FALSE; /* Actually update targets */ - usePipes = TRUE; /* Catch child output in pipes */ - debug = 0; /* No debug verbosity, please. */ - jobsRunning = FALSE; - - maxJobs = DEFMAXJOBS; /* Set default max concurrency */ - maxLocal = DEFMAXLOCAL; /* Set default local max concurrency */ -#ifdef notyet - compatMake = FALSE; /* No compat mode */ -#else - compatMake = TRUE; /* No compat mode */ -#endif - - - /* - * Initialize the parsing, directory and variable modules to prepare - * for the reading of inclusion paths and variable settings on the - * command line - */ - Dir_Init(); /* Initialize directory structures so -I flags - * can be processed correctly */ - Parse_Init(); /* Need to initialize the paths of #include - * directories */ - Var_Init(); /* As well as the lists of variables for - * parsing arguments */ - str_init(); - if (objdir != curdir) - Dir_AddDir(dirSearchPath, curdir); - Var_Set(".CURDIR", curdir, VAR_GLOBAL); - Var_Set(".OBJDIR", objdir, VAR_GLOBAL); - - /* - * Initialize various variables. - * MAKE also gets this name, for compatibility - * .MAKEFLAGS gets set to the empty string just in case. - * MFLAGS also gets initialized empty, for compatibility. - */ - Var_Set("MAKE", argv[0], VAR_GLOBAL); - Var_Set(MAKEFLAGS, "", VAR_GLOBAL); - Var_Set("MFLAGS", "", VAR_GLOBAL); - Var_Set("MACHINE", machine, VAR_GLOBAL); -#ifdef MACHINE_ARCH - Var_Set("MACHINE_ARCH", MACHINE_ARCH, VAR_GLOBAL); -#endif - - /* - * First snag any flags out of the MAKE environment variable. - * (Note this is *not* MAKEFLAGS since /bin/make uses that and it's - * in a different format). - */ -#ifdef POSIX - Main_ParseArgLine(getenv("MAKEFLAGS")); -#else - Main_ParseArgLine(getenv("MAKE")); -#endif - - MainParseArgs(argc, argv); - - /* - * Initialize archive, target and suffix modules in preparation for - * parsing the makefile(s) - */ - Arch_Init(); - Targ_Init(); - Suff_Init(); - - DEFAULT = NILGNODE; - (void)time(&now); - - /* - * Set up the .TARGETS variable to contain the list of targets to be - * created. If none specified, make the variable empty -- the parser - * will fill the thing in with the default or .MAIN target. - */ - if (!Lst_IsEmpty(create)) { - LstNode ln; - - for (ln = Lst_First(create); ln != NILLNODE; - ln = Lst_Succ(ln)) { - char *name = (char *)Lst_Datum(ln); - - Var_Append(".TARGETS", name, VAR_GLOBAL); - } - } else - Var_Set(".TARGETS", "", VAR_GLOBAL); - - /* - * Read in the built-in rules first, followed by the specified makefile, - * if it was (makefile != (char *) NULL), or the default Makefile and - * makefile, in that order, if it wasn't. - */ - if (!noBuiltins && !ReadMakefile(_PATH_DEFSYSMK)) - Fatal("make: no system rules (%s).", _PATH_DEFSYSMK); - - if (!Lst_IsEmpty(makefiles)) { - LstNode ln; - - ln = Lst_Find(makefiles, (ClientData)NULL, ReadMakefile); - if (ln != NILLNODE) - Fatal("make: cannot open %s.", (char *)Lst_Datum(ln)); - } else if (!ReadMakefile("makefile")) - (void)ReadMakefile("Makefile"); - - (void)ReadMakefile(".depend"); - - Var_Append("MFLAGS", Var_Value(MAKEFLAGS, VAR_GLOBAL, &p1), VAR_GLOBAL); - if (p1) - free(p1); - - /* Install all the flags into the MAKE envariable. */ - if (((p = Var_Value(MAKEFLAGS, VAR_GLOBAL, &p1)) != NULL) && *p) -#ifdef POSIX - setenv("MAKEFLAGS", p, 1); -#else - setenv("MAKE", p, 1); -#endif - if (p1) - free(p1); - - /* - * For compatibility, look at the directories in the VPATH variable - * and add them to the search path, if the variable is defined. The - * variable's value is in the same format as the PATH envariable, i.e. - * ::... - */ - if (Var_Exists("VPATH", VAR_CMD)) { - char *vpath, *path, *cp, savec; - /* - * GCC stores string constants in read-only memory, but - * Var_Subst will want to write this thing, so store it - * in an array - */ - static char VPATH[] = "${VPATH}"; - - vpath = Var_Subst(NULL, VPATH, VAR_CMD, FALSE); - path = vpath; - do { - /* skip to end of directory */ - for (cp = path; *cp != ':' && *cp != '\0'; cp++) - continue; - /* Save terminator character so know when to stop */ - savec = *cp; - *cp = '\0'; - /* Add directory to search path */ - Dir_AddDir(dirSearchPath, path); - *cp = savec; - path = cp + 1; - } while (savec == ':'); - (void)free((Address)vpath); - } - - /* - * Now that all search paths have been read for suffixes et al, it's - * time to add the default search path to their lists... - */ - Suff_DoPaths(); - - /* print the initial graph, if the user requested it */ - if (DEBUG(GRAPH1)) - Targ_PrintGraph(1); - - /* - * 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); - -/* - * this was original amMake -- want to allow parallelism, so put this - * back in, eventually. - */ - 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) { - if (maxLocal == -1) - maxLocal = maxJobs; - Job_Init(maxJobs, maxLocal); - 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); - - Lst_Destroy(targs, NOFREE); - Lst_Destroy(makefiles, NOFREE); - Lst_Destroy(create, (void (*) __P((ClientData))) free); - - /* print the graph now it's been processed if the user requested it */ - if (DEBUG(GRAPH2)) - Targ_PrintGraph(2); - - Suff_End(); - Targ_End(); - Arch_End(); - str_end(); - Var_End(); - Parse_End(); - Dir_End(); - - if (queryFlag && outOfDate) - return(1); - else - return(0); -} - -/*- - * ReadMakefile -- - * Open and parse the given makefile. - * - * Results: - * TRUE if ok. FALSE if couldn't open file. - * - * Side Effects: - * lots - */ -static Boolean -ReadMakefile(fname) - char *fname; /* makefile to read */ -{ - extern Lst parseIncPath, sysIncPath; - FILE *stream; - char *name, path[MAXPATHLEN + 1]; - - if (!strcmp(fname, "-")) { - Parse_File("(stdin)", stdin); - Var_Set("MAKEFILE", "", VAR_GLOBAL); - } else { - if ((stream = fopen(fname, "r")) != NULL) - goto found; - /* if we've chdir'd, rebuild the path name */ - if (curdir != objdir && *fname != '/') { - (void)sprintf(path, "%s/%s", curdir, fname); - if ((stream = fopen(path, "r")) != NULL) { - fname = path; - goto found; - } - } - /* look in -I and system include directories. */ - name = Dir_FindFile(fname, parseIncPath); - if (!name) - name = Dir_FindFile(fname, sysIncPath); - if (!name || !(stream = fopen(name, "r"))) - return(FALSE); - fname = name; - /* - * set the MAKEFILE variable desired by System V fans -- the - * placement of the setting here means it gets set to the last - * makefile specified, as it is set by SysV make. - */ -found: Var_Set("MAKEFILE", fname, VAR_GLOBAL); - Parse_File(fname, stream); - (void)fclose(stream); - } - return(TRUE); -} - -/*- - * Error -- - * Print an error message given its format. - * - * Results: - * None. - * - * Side Effects: - * The message is printed. - */ -/* VARARGS */ -void -#if __STDC__ -Error(char *fmt, ...) -#else -Error(va_alist) - va_dcl -#endif -{ - va_list ap; -#if __STDC__ - va_start(ap, fmt); -#else - char *fmt; - - va_start(ap); - fmt = va_arg(ap, char *); -#endif - (void)vfprintf(stderr, fmt, ap); - va_end(ap); - (void)fprintf(stderr, "\n"); - (void)fflush(stderr); -} - -/*- - * Fatal -- - * Produce a Fatal error message. If jobs are running, waits for them - * to finish. - * - * Results: - * None - * - * Side Effects: - * The program exits - */ -/* VARARGS */ -void -#if __STDC__ -Fatal(char *fmt, ...) -#else -Fatal(va_alist) - va_dcl -#endif -{ - va_list ap; -#if __STDC__ - va_start(ap, fmt); -#else - char *fmt; - - va_start(ap); - fmt = va_arg(ap, char *); -#endif - if (jobsRunning) - Job_Wait(); - - (void)vfprintf(stderr, fmt, ap); - va_end(ap); - (void)fprintf(stderr, "\n"); - (void)fflush(stderr); - - if (DEBUG(GRAPH2)) - Targ_PrintGraph(2); - 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 -#if __STDC__ -Punt(char *fmt, ...) -#else -Punt(va_alist) - va_dcl -#endif -{ - va_list ap; -#if __STDC__ - va_start(ap, fmt); -#else - char *fmt; - - va_start(ap); - fmt = va_arg(ap, char *); -#endif - - (void)fprintf(stderr, "make: "); - (void)vfprintf(stderr, fmt, ap); - va_end(ap); - (void)fprintf(stderr, "\n"); - (void)fflush(stderr); - - DieHorribly(); -} - -/*- - * DieHorribly -- - * Exit without giving a message. - * - * Results: - * None - * - * Side Effects: - * A big one... - */ -void -DieHorribly() -{ - if (jobsRunning) - Job_AbortAll(); - if (DEBUG(GRAPH2)) - Targ_PrintGraph(2); - exit(2); /* Not 1, so -q can distinguish error */ -} - -/* - * Finish -- - * Called when aborting due to errors in child shell to signal - * abnormal exit. - * - * Results: - * None - * - * Side Effects: - * The program exits - */ -void -Finish(errors) - int errors; /* number of errors encountered in Make_Make */ -{ - Fatal("%d error%s", errors, errors == 1 ? "" : "s"); -} - -/* - * emalloc -- - * malloc, but die on error. - */ -char * -emalloc(len) - size_t len; -{ - char *p; - - if ((p = (char *) malloc(len)) == NULL) - enomem(); - return(p); -} - -/* - * enomem -- - * die when out of memory. - */ -void -enomem() -{ - (void)fprintf(stderr, "make: %s.\n", strerror(errno)); - exit(2); -} - -/* - * usage -- - * exit with usage message - */ -static void -usage() -{ - (void)fprintf(stderr, -"usage: make [-eiknqrst] [-D variable] [-d flags] [-f makefile ]\n\ - [-I directory] [-j max_jobs] [variable=value]\n"); - exit(2); -} - - -int -PrintAddr(a, b) - ClientData a; - ClientData b; -{ - printf("%lx ", (unsigned long) a); - return b ? 0 : 0; -} diff --git a/usr.bin/make/make.1 b/usr.bin/make/make.1 deleted file mode 100644 index b1392cc6e944..000000000000 --- a/usr.bin/make/make.1 +++ /dev/null @@ -1,890 +0,0 @@ -.\" Copyright (c) 1990, 1993 -.\" The Regents of the University of California. All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by the University of -.\" California, Berkeley and its contributors. -.\" 4. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" @(#)make.1 8.8 (Berkeley) 6/13/95 -.\" -.Dd June 13, 1995 -.Dt MAKE 1 -.Os -.Sh NAME -.Nm make -.Nd maintain program dependencies -.Sh SYNOPSIS -.Nm make -.Op Fl eiknqrstv -.Op Fl D Ar variable -.Op Fl d Ar flags -.Op Fl f Ar makefile -.Op Fl I Ar directory -.Bk -words -.Op Fl j Ar max_jobs -.Ek -.Op Ar variable=value -.Op Ar target ... -.Sh DESCRIPTION -.Nm Make -is a program designed to simplify the maintenance of other programs. -Its input is a list of specifications as to the files upon which programs -and other files depend. -If the file -.Ql Pa makefile -exists, it is read for this list of specifications. -If it does not exist, the file -.Ql Pa Makefile -is read. -If the file -.Ql Pa .depend -exists, it is read (see -.Xr mkdep 1) . -.Pp -This manual page is intended as a reference document only. -For a more thorough description of -.Nm make -and makefiles, please refer to -.%T "Make \- A Tutorial" . -.Pp -The options are as follows: -.Bl -tag -width Ds -.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 make -are to print debugging information. -.Ar Flags -is one or more of the following: -.Bl -tag -width Ds -.It Ar A -Print all possible debugging information; -equivalent to specifying all of the debugging flags. -.It Ar a -Print debugging information about archive searching and caching. -.It Ar c -Print debugging information about conditional evaluation. -.It Ar d -Print debugging information about directory searching and caching. -.It Ar "g1" -Print the input graph before making anything. -.It Ar "g2" -Print the input graph after making everything, or before exiting -on error. -.It Ar j -Print debugging information about running multiple shells. -.It Ar m -Print debugging information about making targets, including modification -dates. -.It Ar s -Print debugging information about suffix-transformation rules. -.It Ar t -Print debugging information about target list maintenance. -.It Ar v -Print debugging information about variable assignment. -.El -.It Fl e -Specify that environmental variables override macro assignments within -makefiles. -.It Fl f Ar makefile -Specify a makefile to read instead of the default -.Ql Pa makefile -and -.Ql Pa Makefile . -If -.Ar makefile -is -.Ql Fl , -standard input is read. -Multiple makefile's may be specified, and are read in the order specified. -.It Fl I Ar directory -Specify a directory in which to search for makefiles and included makefiles. -The system makefile directory 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 max_jobs -Specify the maximum number of jobs that -.Nm make -may have running at any one time. -.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 n -Display the commands that would have been executed, but do not actually -execute them. -.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 -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 Ar variable=value -Set the value of the variable -.Ar variable -to -.Ar value . -.El -.Pp -There are seven different types of lines in a makefile: file dependency -specifications, shell commands, variable assignments, include statements, -conditional directives, for loops, and comments. -.Pp -In general, lines may be continued from one line to the next by ending -them with a backslash -.Pq Ql \e . -The trailing newline character and initial whitespace on the following -line are compressed into a single space. -.Sh FILE DEPENDENCY SPECIFICATIONS -Dependency lines consist of one or more targets, an operator, and zero -or more sources. -This creates a relationship where the targets ``depend'' on the sources -and are usually created from them. -The exact relationship between the target and the source is determined -by the operator that separates them. -The three operators are as follows: -.Bl -tag -width flag -.It Ic \&: -A target is considered out-of-date if its modification time is less than -those of any of its sources. -Sources for a target accumulate over dependency lines when this operator -is used. -The target is removed if -.Nm make -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 make -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 make -is interrupted. -.El -.Pp -Targets and sources may contain the shell wildcard values -.Ql ? , -.Ql * , -.Ql [] -and -.Ql {} . -The values -.Ql ? , -.Ql * -and -.Ql [] -may only be used as part of the final -component of the target or source, and must be used to describe existing -files. -The value -.Ql {} -need not necessarily be used to describe existing files. -Expansion is in directory order, not alphabetically as done in the shell. -.Sh SHELL COMMANDS -Each target may have associated with it a series of shell commands, normally -used to create the target. -Each of the commands in this script -.Em must -be preceded by a tab. -While any target may appear on a dependency line, only one of these -dependencies may be followed by a creation script, unless the -.Ql Ic :: -operator is used. -.Pp -If the first or first two characters of the command line are -.Ql Ic @ -and/or -.Ql Ic \- , -the command is treated specially. -A -.Ql Ic @ -causes the command not to be echoed before it is executed. -A -.Ql Ic \- -causes any non-zero exit status of the command line to be ignored. -.Sh VARIABLE ASSIGNMENTS -Variables in make are much like variables in the shell, and, by tradition, -consist of all upper-case letters. -The five operators that can be used to assign values to variables are as -follows: -.Bl -tag -width Ds -.It Ic \&= -Assign the value to the variable. -Any previous value is overridden. -.It Ic \&+= -Append the value to the current value of the variable. -.It Ic \&?= -Assign the value to the variable if it is not already defined. -.It Ic \&:= -Assign with expansion, i.e. expand the value before assigning it -to the variable. -Normally, expansion is not done until the variable is referenced. -.It Ic \&!= -Expand the value and pass it to the shell for execution and assign -the result to the variable. -Any newlines in the result are replaced with spaces. -.El -.Pp -Any white-space before the assigned -.Ar value -is removed; if the value is being appended, a single space is inserted -between the previous contents of the variable and the appended value. -.Pp -Variables are expanded by surrounding the variable name with either -curly braces -.Pq Ql {} -or parentheses -.Pq Ql () -and preceding it with -a dollar sign -.Pq Ql \&$ . -If the variable name contains only a single letter, the surrounding -braces or parentheses are not required. -This shorter form is not recommended. -.Pp -Variable substitution occurs at two distinct times, depending on where -the variable is being used. -Variables in dependency lines are expanded as the line is read. -Variables in shell commands are expanded when the shell command is -executed. -.Pp -The four different classes of variables (in order of increasing precedence) -are: -.Bl -tag -width Ds -.It Environment variables -Variables defined as part of -.Nm make Ns 's -environment. -.It Global variables -Variables defined in the makefile or in included makefiles. -.It Command line variables -Variables defined as part of the command line. -.It Local variables -Variables that are defined specific to a certain target. -The seven local variables are as follows: -.Bl -tag -width ".ARCHIVE" -.It Va .ALLSRC -The list of all sources for this target; also known as -.Ql Va \&> . -.It Va .ARCHIVE -The name of the archive file. -.It Va .IMPSRC -The name/path of the source from which the target is to be transformed -(the ``implied'' source); also known as -.Ql Va \&< . -.It Va .MEMBER -The name of the archive member. -.It Va .OODATE -The list of sources for this target that were deemed out-of-date; also -known as -.Ql Va \&? . -.It Va .PREFIX -The file prefix of the file, containing only the file portion, no suffix -or preceding directory components; also known as -.Ql Va * . -.It Va .TARGET -The name of the target; also known as -.Ql Va @ . -.El -.Pp -The shorter forms -.Ql Va @ , -.Ql Va ? , -.Ql Va \&> -and -.Ql Va * -are permitted for backward -compatibility with historical makefiles and are not recommended. -The six variables -.Ql Va "@F" , -.Ql Va "@D" , -.Ql Va " -or -.Ql .include \*qfile\*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. -.Pp -Conditional expressions are also preceded by a single dot as the first -character of a line. -The possible conditionals are as follows: -.Bl -tag -width Ds -.It Ic .undef Ar variable -Un-define the specified global variable. -Only global variables may be un-defined. -.It Xo -.Ic \&.if -.Oo \&! Oc Ns Ar expression -.Op Ar operator expression ... -.Xc -Test the value of an expression. -.It Xo -.Ic .ifdef -.Oo \&! Oc Ns Ar variable -.Op Ar operator variable ... -.Xc -Test the value of a variable. -.It Xo -.Ic .ifndef -.Oo \&! Oc Ns Ar variable -.Op Ar operator variable ... -.Xc -Test the value of a variable. -.It Xo -.Ic .ifmake -.Oo \&! Oc Ns Ar target -.Op Ar operator target ... -.Xc -Test the target being built. -.It Xo -.Ic .ifnmake -.Oo \&! Oc Ar target -.Op Ar operator target ... -.Xc -Test the target being built. -.It Ic .else -Reverse the sense of the last conditional. -.It Xo -.Ic .elif -.Oo \&! Oc Ar expression -.Op Ar operator expression ... -.Xc -A combination of -.Ql Ic .else -followed by -.Ql Ic .if . -.It Xo -.Ic .elifdef -.Oo \&! Oc Ns Ar variable -.Op Ar operator variable ... -.Xc -A combination of -.Ql Ic .else -followed by -.Ql Ic .ifdef . -.It Xo -.Ic .elifndef -.Oo \&! Oc Ns Ar variable -.Op Ar operator variable ... -.Xc -A combination of -.Ql Ic .else -followed by -.Ql Ic .ifndef . -.It Xo -.Ic .elifmake -.Oo \&! Oc Ns Ar target -.Op Ar operator target ... -.Xc -A combination of -.Ql Ic .else -followed by -.Ql Ic .ifmake . -.It Xo -.Ic .elifnmake -.Oo \&! Oc Ns Ar target -.Op Ar operator target ... -.Xc -A combination of -.Ql Ic .else -followed by -.Ql Ic .ifnmake . -.It Ic .endif -End the body of the conditional. -.El -.Pp -The -.Ar operator -may be any one of the following: -.Bl -tag -width "Cm XX" -.It Cm \&|\&| -logical OR -.It Cm \&&& -Logical -.Tn AND ; -of higher precedence than -.Dq . -.El -.Pp -As in C, -.Nm make -will only evaluate a conditional as far as is necessary to determine -its value. -Parenthesis 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 \&&& . -.Pp -The value of -.Ar expression -may be any of the following: -.Bl -tag -width Ic 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 make 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. -.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. -.Pp -When -.Nm make -is evaluating one of these conditional expression, and it encounters -a word it doesn't recognize, either the ``make'' or ``defined'' -expression is applied to it, depending on the form of the conditional. -If the form is -.Ql Ic .ifdef -or -.Ql Ic .ifndef , -the ``defined'' expression -is applied. -Similarly, if the form is -.Ql Ic .ifmake -or -.Ql Ic .ifnmake , the ``make'' -expression is applied. -.Pp -If the conditional evaluates to true the parsing of the makefile continues -as before. -If it evaluates to false, the following lines are skipped. -In both cases this continues until a -.Ql Ic .else -or -.Ql Ic .endif -is found. -.Pp -For loops are typically used to apply a set of rules to a list of files. -The syntax of a for loop is: -.Bl -tag -width Ds -.It Xo -.Ic \&.for -.Ar variable -.Ic in -.Ar expression -.Xc -.It Xo - -.Xc -.It Xo -.Ic \&.endfor -.Xc -.El -After the for -.Ic expression -is evaluated, it is split into words. The -iteration -.Ic variable -is successively set to each word, and substituted in the -.Ic make-rules -inside the body of the for loop. -.Sh COMMENTS -Comments begin with a hash -.Pq Ql \&# -character, anywhere but in a shell -command line, and continue to the end of the line. -.Sh SPECIAL SOURCES -.Bl -tag -width Ic .IGNORE -.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 .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 make Ns 's . -.It Ic .NOTMAIN -Normally -.Nm make -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 make -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 -If this special target is present in a Makefile, all sources for the -target will considered to be `phony' targets. I.e. they don't refer -to real files, and they will always be considered out-of-date. This -is useful in case one wants to create a target called `install', in -a directory where a file `install' already exists. -.It Ic .PRECIOUS -When -.Nm make -is interrupted, it removes any partially made targets. -This source prevents the target from being removed. -.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 make 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. -.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 Ic .BEGIN -.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 make -can't figure out any other way to create. -Only the shell script is used. -The -.Ic .IMPSRC -variable of a target that inherits -.Ic .DEFAULT Ns 's -commands is set -to the target's own name. -.It Ic .END -Any command lines attached to this target are executed after everything -else is done. -.It Ic .IGNORE -Mark each of the sources with the -.Ic .IGNORE -attribute. -If no sources are specified, this is the equivalent of specifying the -.Fl i -option. -.It Ic .INTERRUPT -If -.Nm make -is interrupted, the commands for this target will be executed. -.It Ic .MAIN -If no target is specified when -.Nm make -is invoked, this target will be built. -.It Ic .MAKEFLAGS -This target provides a way to specify flags for -.Nm make -when the makefile is used. -The flags are as if typed to the shell, though the -.Fl f -option will have -no effect. -.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. -.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 .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 make . -If no sources are specified, any previous specified suffices are deleted. -.Sh ENVIRONMENT -.Nm Make -utilizes the following environment variables, if they exist: -.Ev MAKE , -.Ev MAKEFLAGS -and -.Ev MAKEOBJDIR . -.Sh FILES -.Bl -tag -width /usr/share/mk -compact -.It .depend -list of dependencies -.It Makefile -list of dependencies -.It makefile -list of dependencies -.It sys.mk -system makefile -.It /usr/share/mk -system makefile directory -.El -.Sh SEE ALSO -.Xr mkdep 1 -.Sh HISTORY -A -.Nm Make -command appeared in -.At v7 . diff --git a/usr.bin/make/make.c b/usr.bin/make/make.c deleted file mode 100644 index 6d17b2ed87f3..000000000000 --- a/usr.bin/make/make.c +++ /dev/null @@ -1,908 +0,0 @@ -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char sccsid[] = "@(#)make.c 8.3 (Berkeley) 6/13/95"; -#endif /* not lint */ - -/*- - * make.c -- - * The functions which perform the examination of targets and - * their suitability for creation - * - * Interface: - * Make_Run Initialize things for the module and recreate - * whatever needs recreating. Returns TRUE if - * work was (or would have been) done and FALSE - * otherwise. - * - * Make_Update Update all parents of a given child. Performs - * various bookkeeping chores like the updating - * of the cmtime field of the parent, filling - * of the IMPSRC context variable, etc. It will - * place the parent on the toBeMade queue if it - * should be. - * - * Make_TimeStamp Function to set the parent's cmtime field - * based on a child's modification time. - * - * Make_DoAllVar Set up the various local variables for a - * target, including the .ALLSRC variable, making - * sure that any variable that needs to exist - * at the very least has the empty value. - * - * Make_OODate Determine if a target is out-of-date. - * - * Make_HandleUse See if a child is a .USE node for a parent - * and perform the .USE actions if so. - */ - -#include "make.h" -#include "hash.h" -#include "dir.h" -#include "job.h" - -static Lst toBeMade; /* The current fringe of the graph. These - * are nodes which await examination by - * MakeOODate. It is added to by - * Make_Update and subtracted from by - * MakeStartJobs */ -static int numNodes; /* Number of nodes to be processed. If this - * is non-zero when Job_Empty() returns - * TRUE, there's a cycle in the graph */ - -static int MakeAddChild __P((ClientData, ClientData)); -static int MakeAddAllSrc __P((ClientData, ClientData)); -static int MakeTimeStamp __P((ClientData, ClientData)); -static int MakeHandleUse __P((ClientData, ClientData)); -static Boolean MakeStartJobs __P((void)); -static int MakePrintStatus __P((ClientData, ClientData)); -/*- - *----------------------------------------------------------------------- - * Make_TimeStamp -- - * Set the cmtime field of a parent node based on the mtime stamp in its - * child. Called from MakeOODate via Lst_ForEach. - * - * Results: - * Always returns 0. - * - * Side Effects: - * The cmtime of the parent node will be changed if the mtime - * field of the child is greater than it. - *----------------------------------------------------------------------- - */ -int -Make_TimeStamp (pgn, cgn) - GNode *pgn; /* the current parent */ - GNode *cgn; /* the child we've just examined */ -{ - if (cgn->mtime > pgn->cmtime) { - pgn->cmtime = cgn->mtime; - } - return (0); -} - -static int -MakeTimeStamp (pgn, cgn) - ClientData pgn; /* the current parent */ - ClientData cgn; /* the child we've just examined */ -{ - return Make_TimeStamp((GNode *) pgn, (GNode *) cgn); -} - -/*- - *----------------------------------------------------------------------- - * Make_OODate -- - * See if a given node is out of date with respect to its sources. - * Used by Make_Run when deciding which nodes to place on the - * toBeMade queue initially and by Make_Update to screen out USE and - * EXEC nodes. In the latter case, however, any other sort of node - * must be considered out-of-date since at least one of its children - * will have been recreated. - * - * Results: - * TRUE if the node is out of date. FALSE otherwise. - * - * Side Effects: - * The mtime field of the node and the cmtime field of its parents - * will/may be changed. - *----------------------------------------------------------------------- - */ -Boolean -Make_OODate (gn) - register GNode *gn; /* the node to check */ -{ - Boolean oodate; - - /* - * Certain types of targets needn't even be sought as their datedness - * doesn't depend on their modification time... - */ - if ((gn->type & (OP_JOIN|OP_USE|OP_EXEC|OP_PHONY)) == 0) { - (void) Dir_MTime (gn); - if (DEBUG(MAKE)) { - if (gn->mtime != 0) { - printf ("modified %s...", Targ_FmtTime(gn->mtime)); - } else { - printf ("non-existent..."); - } - } - } - - /* - * A target is remade in one of the following circumstances: - * its modification time is smaller than that of its youngest child - * and it would actually be run (has commands or type OP_NOP) - * it's the object of a force operator - * it has no children, was on the lhs of an operator and doesn't exist - * already. - * - * Libraries are only considered out-of-date if the archive module says - * they are. - * - * These weird rules are brought to you by Backward-Compatability and - * the strange people who wrote 'Make'. - */ - if (gn->type & OP_PHONY) { - /* - * A PHONY node is always out of date - */ - if (DEBUG(MAKE)) - printf("phony..."); - return TRUE; - } else if (gn->type & OP_USE) { - /* - * If the node is a USE node it is *never* out of date - * no matter *what*. - */ - if (DEBUG(MAKE)) { - printf(".USE node..."); - } - oodate = FALSE; - } else if (gn->type & OP_LIB) { - if (DEBUG(MAKE)) { - printf("library..."); - } - - /* - * always out of date if no children and :: target - */ - - oodate = Arch_LibOODate (gn) || - ((gn->cmtime == 0) && (gn->type & OP_DOUBLEDEP)); - } else if (gn->type & OP_JOIN) { - /* - * A target with the .JOIN attribute is only considered - * out-of-date if any of its children was out-of-date. - */ - if (DEBUG(MAKE)) { - printf(".JOIN node..."); - } - oodate = gn->childMade; - } else if (gn->type & (OP_FORCE|OP_EXEC)) { - /* - * A node which is the object of the force (!) operator or which has - * the .EXEC attribute is always considered out-of-date. - */ - if (DEBUG(MAKE)) { - if (gn->type & OP_FORCE) { - printf("! operator..."); - } else { - printf(".EXEC node..."); - } - } - oodate = TRUE; - } else if ((gn->mtime < gn->cmtime) || - ((gn->cmtime == 0) && - ((gn->mtime==0) || (gn->type & OP_DOUBLEDEP)))) - { - /* - * A node whose modification time is less than that of its - * youngest child or that has no children (cmtime == 0) and - * either doesn't exist (mtime == 0) or was the object of a - * :: operator is out-of-date. Why? Because that's the way Make does - * it. - */ - if (DEBUG(MAKE)) { - if (gn->mtime < gn->cmtime) { - printf("modified before source..."); - } else if (gn->mtime == 0) { - printf("non-existent and no sources..."); - } else { - printf(":: operator and no sources..."); - } - } - oodate = TRUE; - } else { -#if 0 - /* WHY? */ - if (DEBUG(MAKE)) { - printf("source %smade...", gn->childMade ? "" : "not "); - } - oodate = gn->childMade; -#else - oodate = FALSE; -#endif /* 0 */ - } - - /* - * If the target isn't out-of-date, the parents need to know its - * modification time. Note that targets that appear to be out-of-date - * but aren't, because they have no commands and aren't of type OP_NOP, - * have their mtime stay below their children's mtime to keep parents from - * thinking they're out-of-date. - */ - if (!oodate) { - Lst_ForEach (gn->parents, MakeTimeStamp, (ClientData)gn); - } - - return (oodate); -} - -/*- - *----------------------------------------------------------------------- - * MakeAddChild -- - * Function used by Make_Run to add a child to the list l. - * It will only add the child if its make field is FALSE. - * - * Results: - * Always returns 0 - * - * Side Effects: - * The given list is extended - *----------------------------------------------------------------------- - */ -static int -MakeAddChild (gnp, lp) - ClientData gnp; /* the node to add */ - ClientData lp; /* the list to which to add it */ -{ - GNode *gn = (GNode *) gnp; - Lst l = (Lst) lp; - if (!gn->make && !(gn->type & OP_USE)) { - (void)Lst_EnQueue (l, (ClientData)gn); - } - return (0); -} - -/*- - *----------------------------------------------------------------------- - * Make_HandleUse -- - * Function called by Make_Run and SuffApplyTransform on the downward - * pass to handle .USE and transformation nodes. A callback function - * for Lst_ForEach, it implements the .USE and transformation - * functionality by copying the node's commands, type flags - * and children to the parent node. Should be called before the - * children are enqueued to be looked at by MakeAddChild. - * - * A .USE node is much like an explicit transformation rule, except - * its commands are always added to the target node, even if the - * target already has commands. - * - * Results: - * returns 0. - * - * Side Effects: - * Children and commands may be added to the parent and the parent's - * type may be changed. - * - *----------------------------------------------------------------------- - */ -int -Make_HandleUse (cgn, pgn) - register GNode *cgn; /* The .USE node */ - register GNode *pgn; /* The target of the .USE node */ -{ - register GNode *gn; /* A child of the .USE node */ - register LstNode ln; /* An element in the children list */ - - if (cgn->type & (OP_USE|OP_TRANSFORM)) { - if ((cgn->type & OP_USE) || Lst_IsEmpty(pgn->commands)) { - /* - * .USE or transformation and target has no commands -- append - * the child's commands to the parent. - */ - (void) Lst_Concat (pgn->commands, cgn->commands, LST_CONCNEW); - } - - if (Lst_Open (cgn->children) == SUCCESS) { - while ((ln = Lst_Next (cgn->children)) != NILLNODE) { - gn = (GNode *)Lst_Datum (ln); - - if (Lst_Member (pgn->children, gn) == NILLNODE) { - (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_TRANSFORM); - - /* - * This child node is now "made", so we decrement the count of - * unmade children in the parent... We also remove the child - * from the parent's list to accurately reflect the number of decent - * children the parent has. This is used by Make_Run to decide - * whether to queue the parent or examine its children... - */ - if (cgn->type & OP_USE) { - pgn->unmade -= 1; - } - } - return (0); -} -static int -MakeHandleUse (pgn, cgn) - ClientData pgn; /* the current parent */ - ClientData cgn; /* the child we've just examined */ -{ - return Make_HandleUse((GNode *) pgn, (GNode *) cgn); -} - -/*- - *----------------------------------------------------------------------- - * Make_Update -- - * Perform update on the parents of a node. Used by JobFinish once - * a node has been dealt with and by MakeStartJobs if it finds an - * up-to-date node. - * - * Results: - * Always returns 0 - * - * Side Effects: - * The unmade field of pgn is decremented and pgn may be placed on - * the toBeMade queue if this field becomes 0. - * - * If the child was made, the parent's childMade field will be set true - * and its cmtime set to now. - * - * If the child wasn't made, the cmtime field of the parent will be - * altered if the child's mtime is big enough. - * - * Finally, if the child is the implied source for the parent, the - * parent's IMPSRC variable is set appropriately. - * - *----------------------------------------------------------------------- - */ -void -Make_Update (cgn) - register GNode *cgn; /* the child node */ -{ - register GNode *pgn; /* the parent node */ - register char *cname; /* the child's name */ - register LstNode ln; /* Element in parents and iParents lists */ - char *p1; - - cname = Var_Value (TARGET, cgn, &p1); - if (p1) - free(p1); - - /* - * If the child was actually made, see what its modification time is - * now -- some rules won't actually update the file. If the file still - * doesn't exist, make its mtime now. - */ - if (cgn->made != UPTODATE) { -#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 cgn->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(cgn->commands) || Lst_IsEmpty(cgn->children)) { - cgn->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 || (cgn->type & OP_SAVE_CMDS) || Dir_MTime(cgn) == 0) { - cgn->mtime = now; - } - if (DEBUG(MAKE)) { - printf("update time: %s\n", Targ_FmtTime(cgn->mtime)); - } -#endif - } - - if (Lst_Open (cgn->parents) == SUCCESS) { - while ((ln = Lst_Next (cgn->parents)) != NILLNODE) { - pgn = (GNode *)Lst_Datum (ln); - if (pgn->make) { - pgn->unmade -= 1; - - if ( ! (cgn->type & (OP_EXEC|OP_USE))) { - if (cgn->made == MADE) { - pgn->childMade = TRUE; - if (pgn->cmtime < cgn->mtime) { - pgn->cmtime = cgn->mtime; - } - } else { - (void)Make_TimeStamp (pgn, cgn); - } - } - if (pgn->unmade == 0) { - /* - * Queue the node up -- any unmade predecessors will - * be dealt with in MakeStartJobs. - */ - (void)Lst_EnQueue (toBeMade, (ClientData)pgn); - } else if (pgn->unmade < 0) { - Error ("Graph cycles through %s", pgn->name); - } - } - } - Lst_Close (cgn->parents); - } - /* - * Deal with successor nodes. If any is marked for making and has an unmade - * count of 0, has not been made and isn't in the examination queue, - * it means we need to place it in the queue as it restrained itself - * before. - */ - for (ln = Lst_First(cgn->successors); ln != NILLNODE; ln = Lst_Succ(ln)) { - GNode *succ = (GNode *)Lst_Datum(ln); - - if (succ->make && succ->unmade == 0 && succ->made == UNMADE && - Lst_Member(toBeMade, (ClientData)succ) == NILLNODE) - { - (void)Lst_EnQueue(toBeMade, (ClientData)succ); - } - } - - /* - * Set the .PREFIX and .IMPSRC variables for all the implied parents - * of this node. - */ - if (Lst_Open (cgn->iParents) == SUCCESS) { - char *p1; - char *cpref = Var_Value(PREFIX, cgn, &p1); - - while ((ln = Lst_Next (cgn->iParents)) != NILLNODE) { - pgn = (GNode *)Lst_Datum (ln); - if (pgn->make) { - Var_Set (IMPSRC, cname, pgn); - Var_Set (PREFIX, cpref, pgn); - } - } - if (p1) - free(p1); - Lst_Close (cgn->iParents); - } -} - -/*- - *----------------------------------------------------------------------- - * MakeAddAllSrc -- - * Add a child's name to the ALLSRC and OODATE variables of the given - * node. Called from Make_DoAllVar via Lst_ForEach. A child is added only - * if it has not been given the .EXEC, .USE or .INVISIBLE attributes. - * .EXEC and .USE children are very rarely going to be files, so... - * A child is added to the OODATE variable if its modification time is - * later than that of its parent, as defined by Make, except if the - * parent is a .JOIN node. In that case, it is only added to the OODATE - * variable if it was actually made (since .JOIN nodes don't have - * modification times, the comparison is rather unfair...).. - * - * Results: - * Always returns 0 - * - * Side Effects: - * The ALLSRC variable for the given node is extended. - *----------------------------------------------------------------------- - */ -static int -MakeAddAllSrc (cgnp, pgnp) - ClientData cgnp; /* The child to add */ - ClientData pgnp; /* The parent to whose ALLSRC variable it should be */ - /* added */ -{ - GNode *cgn = (GNode *) cgnp; - GNode *pgn = (GNode *) pgnp; - if ((cgn->type & (OP_EXEC|OP_USE|OP_INVISIBLE)) == 0) { - char *child; - char *p1; - - child = Var_Value(TARGET, cgn, &p1); - Var_Append (ALLSRC, child, pgn); - if (pgn->type & OP_JOIN) { - if (cgn->made == MADE) { - Var_Append(OODATE, child, pgn); - } - } else if ((pgn->mtime < cgn->mtime) || - (cgn->mtime >= now && cgn->made == MADE)) - { - /* - * It goes in the OODATE variable if the parent is younger than the - * child or if the child has been modified more recently than - * the start of the make. This is to keep pmake from getting - * confused if something else updates the parent after the - * make starts (shouldn't happen, I know, but sometimes it - * does). In such a case, if we've updated the kid, the parent - * is likely to have a modification time later than that of - * the kid and anything that relies on the OODATE variable will - * be hosed. - * - * XXX: This will cause all made children to go in the OODATE - * variable, even if they're not touched, if RECHECK isn't defined, - * since cgn->mtime is set to now in Make_Update. According to - * some people, this is good... - */ - Var_Append(OODATE, child, pgn); - } - if (p1) - free(p1); - } - return (0); -} - -/*- - *----------------------------------------------------------------------- - * Make_DoAllVar -- - * Set up the ALLSRC and OODATE variables. Sad to say, it must be - * done separately, rather than while traversing the graph. This is - * because Make defined OODATE to contain all sources whose modification - * times were later than that of the target, *not* those sources that - * were out-of-date. Since in both compatibility and native modes, - * the modification time of the parent isn't found until the child - * has been dealt with, we have to wait until now to fill in the - * variable. As for ALLSRC, the ordering is important and not - * guaranteed when in native mode, so it must be set here, too. - * - * Results: - * None - * - * Side Effects: - * The ALLSRC and OODATE variables of the given node is filled in. - * If the node is a .JOIN node, its TARGET variable will be set to - * match its ALLSRC variable. - *----------------------------------------------------------------------- - */ -void -Make_DoAllVar (gn) - GNode *gn; -{ - Lst_ForEach (gn->children, MakeAddAllSrc, (ClientData) gn); - - if (!Var_Exists (OODATE, gn)) { - Var_Set (OODATE, "", gn); - } - if (!Var_Exists (ALLSRC, gn)) { - Var_Set (ALLSRC, "", gn); - } - - if (gn->type & OP_JOIN) { - char *p1; - Var_Set (TARGET, Var_Value (ALLSRC, gn, &p1), gn); - if (p1) - free(p1); - } -} - -/*- - *----------------------------------------------------------------------- - * MakeStartJobs -- - * Start as many jobs as possible. - * - * Results: - * If the query flag was given to pmake, no job will be started, - * but as soon as an out-of-date target is found, this function - * returns TRUE. At all other times, this function returns FALSE. - * - * Side Effects: - * Nodes are removed from the toBeMade queue and job table slots - * are filled. - * - *----------------------------------------------------------------------- - */ -static Boolean -MakeStartJobs () -{ - register GNode *gn; - - while (!Job_Full() && !Lst_IsEmpty (toBeMade)) { - gn = (GNode *) Lst_DeQueue (toBeMade); - if (DEBUG(MAKE)) { - printf ("Examining %s...", gn->name); - } - /* - * Make sure any and all predecessors that are going to be made, - * have been. - */ - if (!Lst_IsEmpty(gn->preds)) { - LstNode ln; - - for (ln = Lst_First(gn->preds); ln != NILLNODE; ln = Lst_Succ(ln)){ - GNode *pgn = (GNode *)Lst_Datum(ln); - - if (pgn->make && pgn->made == UNMADE) { - if (DEBUG(MAKE)) { - printf("predecessor %s not made yet.\n", pgn->name); - } - break; - } - } - /* - * If ln isn't nil, there's a predecessor as yet unmade, so we - * just drop this node on the floor. When the node in question - * has been made, it will notice this node as being ready to - * make but as yet unmade and will place the node on the queue. - */ - if (ln != NILLNODE) { - continue; - } - } - - numNodes--; - if (Make_OODate (gn)) { - if (DEBUG(MAKE)) { - printf ("out-of-date\n"); - } - if (queryFlag) { - return (TRUE); - } - Make_DoAllVar (gn); - Job_Make (gn); - } else { - if (DEBUG(MAKE)) { - printf ("up-to-date\n"); - } - gn->made = UPTODATE; - if (gn->type & OP_JOIN) { - /* - * Even for an up-to-date .JOIN node, we need it to have its - * context variables so references to it get the correct - * value for .TARGET when building up the context variables - * of its parent(s)... - */ - Make_DoAllVar (gn); - } - - Make_Update (gn); - } - } - return (FALSE); -} - -/*- - *----------------------------------------------------------------------- - * MakePrintStatus -- - * Print the status of a top-level node, viz. it being up-to-date - * already or not created due to an error in a lower level. - * Callback function for Make_Run via Lst_ForEach. - * - * Results: - * Always returns 0. - * - * Side Effects: - * A message may be printed. - * - *----------------------------------------------------------------------- - */ -static int -MakePrintStatus(gnp, cyclep) - ClientData gnp; /* Node to examine */ - ClientData cyclep; /* True if gn->unmade being non-zero implies - * a cycle in the graph, not an error in an - * inferior */ -{ - GNode *gn = (GNode *) gnp; - Boolean cycle = *(Boolean *) cyclep; - if (gn->made == UPTODATE) { - printf ("`%s' is up to date.\n", gn->name); - } else if (gn->unmade != 0) { - if (cycle) { - Boolean t = TRUE; - /* - * If printing cycles and came to one that has unmade children, - * print out the cycle by recursing on its children. Note a - * cycle like: - * a : b - * b : c - * c : b - * will cause this to erroneously complain about a being in - * the cycle, but this is a good approximation. - */ - if (gn->made == CYCLE) { - Error("Graph cycles through `%s'", gn->name); - gn->made = ENDCYCLE; - Lst_ForEach(gn->children, MakePrintStatus, (ClientData) &t); - gn->made = UNMADE; - } else if (gn->made != ENDCYCLE) { - gn->made = CYCLE; - Lst_ForEach(gn->children, MakePrintStatus, (ClientData) &t); - } - } else { - printf ("`%s' not remade because of errors.\n", gn->name); - } - } - return (0); -} - -/*- - *----------------------------------------------------------------------- - * Make_Run -- - * Initialize the nodes to remake and the list of nodes which are - * ready to be made by doing a breadth-first traversal of the graph - * starting from the nodes in the given list. Once this traversal - * is finished, all the 'leaves' of the graph are in the toBeMade - * queue. - * Using this queue and the Job module, work back up the graph, - * calling on MakeStartJobs to keep the job table as full as - * possible. - * - * Results: - * TRUE if work was done. FALSE otherwise. - * - * Side Effects: - * The make field of all nodes involved in the creation of the given - * targets is set to 1. The toBeMade list is set to contain all the - * 'leaves' of these subgraphs. - *----------------------------------------------------------------------- - */ -Boolean -Make_Run (targs) - Lst targs; /* the initial list of targets */ -{ - register GNode *gn; /* a temporary pointer */ - register Lst examine; /* List of targets to examine */ - int errors; /* Number of errors the Job module reports */ - - toBeMade = Lst_Init (FALSE); - - examine = Lst_Duplicate(targs, NOCOPY); - numNodes = 0; - - /* - * Make an initial downward pass over the graph, marking nodes to be made - * as we go down. We call Suff_FindDeps to find where a node is and - * to get some children for it if it has none and also has no commands. - * If the node is a leaf, we stick it on the toBeMade queue to - * be looked at in a minute, otherwise we add its children to our queue - * and go on about our business. - */ - while (!Lst_IsEmpty (examine)) { - gn = (GNode *) Lst_DeQueue (examine); - - if (!gn->make) { - gn->make = TRUE; - numNodes++; - - /* - * Apply any .USE rules before looking for implicit dependencies - * to make sure everything has commands that should... - */ - Lst_ForEach (gn->children, MakeHandleUse, (ClientData)gn); - Suff_FindDeps (gn); - - if (gn->unmade != 0) { - Lst_ForEach (gn->children, MakeAddChild, (ClientData)examine); - } else { - (void)Lst_EnQueue (toBeMade, (ClientData)gn); - } - } - } - - Lst_Destroy (examine, NOFREE); - - if (queryFlag) { - /* - * We wouldn't do any work unless we could start some jobs in the - * next loop... (we won't actually start any, of course, this is just - * to see if any of the targets was out of date) - */ - return (MakeStartJobs()); - } else { - /* - * Initialization. At the moment, no jobs are running and until some - * get started, nothing will happen since the remaining upward - * traversal of the graph is performed by the routines in job.c upon - * the finishing of a job. So we fill the Job table as much as we can - * before going into our loop. - */ - (void) MakeStartJobs(); - } - - /* - * Main Loop: The idea here is that the ending of jobs will take - * care of the maintenance of data structures and the waiting for output - * will cause us to be idle most of the time while our children run as - * much as possible. Because the job table is kept as full as possible, - * the only time when it will be empty is when all the jobs which need - * running have been run, so that is the end condition of this loop. - * Note that the Job module will exit if there were any errors unless the - * keepgoing flag was given. - */ - while (!Job_Empty ()) { - Job_CatchOutput (); - Job_CatchChildren (!usePipes); - (void)MakeStartJobs(); - } - - errors = Job_End(); - - /* - * Print the final status of each target. E.g. if it wasn't made - * because some inferior reported an error. - */ - errors = ((errors == 0) && (numNodes != 0)); - Lst_ForEach(targs, MakePrintStatus, (ClientData) &errors); - - return (TRUE); -} diff --git a/usr.bin/make/nonints.h b/usr.bin/make/nonints.h deleted file mode 100644 index 039d4b967ed2..000000000000 --- a/usr.bin/make/nonints.h +++ /dev/null @@ -1,140 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)nonints.h 8.4 (Berkeley) 4/28/95 - */ - -/* arch.c */ -ReturnStatus Arch_ParseArchive __P((char **, Lst, GNode *)); -void Arch_Touch __P((GNode *)); -void Arch_TouchLib __P((GNode *)); -int Arch_MTime __P((GNode *)); -int Arch_MemMTime __P((GNode *)); -void Arch_FindLib __P((GNode *, Lst)); -Boolean Arch_LibOODate __P((GNode *)); -void Arch_Init __P((void)); -void Arch_End __P((void)); - -/* compat.c */ -void Compat_Run __P((Lst)); - -/* cond.c */ -int Cond_Eval __P((char *)); -void Cond_End __P((void)); - -/* for.c */ -int For_Eval __P((char *)); -void For_Run __P((void)); - -/* main.c */ -void Main_ParseArgLine __P((char *)); -int main __P((int, char **)); -void Error __P((char *, ...)); -void Fatal __P((char *, ...)); -void Punt __P((char *, ...)); -void DieHorribly __P((void)); -int PrintAddr __P((ClientData, ClientData)); -void Finish __P((int)); -char *emalloc __P((size_t)); -void enomem __P((void)); - -/* parse.c */ -void Parse_Error __P((int, char *, ...)); -Boolean Parse_AnyExport __P((void)); -Boolean Parse_IsVar __P((char *)); -void Parse_DoVar __P((char *, GNode *)); -void Parse_AddIncludeDir __P((char *)); -void Parse_File __P((char *, FILE *)); -void Parse_Init __P((void)); -void Parse_End __P((void)); -void Parse_FromString __P((char *)); -Lst Parse_MainName __P((void)); - -/* str.c */ -void str_init __P((void)); -void str_end __P((void)); -char *str_concat __P((char *, char *, int)); -char **brk_string __P((char *, int *, Boolean)); -char *Str_FindSubstring __P((char *, char *)); -int Str_Match __P((char *, char *)); -char *Str_SYSVMatch __P((char *, char *, int *len)); -void Str_SYSVSubst __P((Buffer, char *, char *, int)); - -/* suff.c */ -void Suff_ClearSuffixes __P((void)); -Boolean Suff_IsTransform __P((char *)); -GNode *Suff_AddTransform __P((char *)); -int Suff_EndTransform __P((ClientData, ClientData)); -void Suff_AddSuffix __P((char *)); -Lst Suff_GetPath __P((char *)); -void Suff_DoPaths __P((void)); -void Suff_AddInclude __P((char *)); -void Suff_AddLib __P((char *)); -void Suff_FindDeps __P((GNode *)); -void Suff_SetNull __P((char *)); -void Suff_Init __P((void)); -void Suff_End __P((void)); -void Suff_PrintAll __P((void)); - -/* targ.c */ -void Targ_Init __P((void)); -void Targ_End __P((void)); -GNode *Targ_NewGN __P((char *)); -GNode *Targ_FindNode __P((char *, int)); -Lst Targ_FindList __P((Lst, int)); -Boolean Targ_Ignore __P((GNode *)); -Boolean Targ_Silent __P((GNode *)); -Boolean Targ_Precious __P((GNode *)); -void Targ_SetMain __P((GNode *)); -int Targ_PrintCmd __P((ClientData, ClientData)); -char *Targ_FmtTime __P((time_t)); -void Targ_PrintType __P((int)); -void Targ_PrintGraph __P((int)); - -/* var.c */ -void Var_Delete __P((char *, GNode *)); -void Var_Set __P((char *, char *, GNode *)); -void Var_Append __P((char *, char *, GNode *)); -Boolean Var_Exists __P((char *, GNode *)); -char *Var_Value __P((char *, GNode *, char **)); -char *Var_Parse __P((char *, GNode *, Boolean, int *, Boolean *)); -char *Var_Subst __P((char *, char *, GNode *, Boolean)); -char *Var_GetTail __P((char *)); -char *Var_GetHead __P((char *)); -void Var_Init __P((void)); -void Var_End __P((void)); -void Var_Dump __P((GNode *)); diff --git a/usr.bin/make/parse.c b/usr.bin/make/parse.c deleted file mode 100644 index e4d8446279bd..000000000000 --- a/usr.bin/make/parse.c +++ /dev/null @@ -1,2661 +0,0 @@ -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char sccsid[] = "@(#)parse.c 8.6 (Berkeley) 6/13/95"; -#endif /* not lint */ - -/*- - * 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. - */ - -#if __STDC__ -#include -#else -#include -#endif -#include -#include -#include -#include -#include "make.h" -#include "hash.h" -#include "dir.h" -#include "job.h" -#include "buf.h" -#include "pathnames.h" - -/* - * These values are returned by ParseEOF to tell Parse_File whether to - * CONTINUE parsing, i.e. it had only reached the end of an include file, - * or if it's DONE. - */ -#define CONTINUE 1 -#define DONE 0 -static Lst targets; /* targets we're working on */ -static Lst targCmds; /* command lines for targets */ -static Boolean inLine; /* true if currently in a dependency - * line or its commands */ -typedef struct { - char *str; - char *ptr; -} PTR; - -static char *fname; /* name of current file (for errors) */ -static int lineno; /* line number in current file */ -static FILE *curFILE = NULL; /* current makefile */ - -static PTR *curPTR = NULL; /* current makefile */ - -static int fatals = 0; - -static GNode *mainNode; /* The main target to create. This is the - * first target on the first dependency - * line in the first makefile */ -/* - * Definitions for handling #include specifications - */ -typedef struct IFile { - char *fname; /* name of previous file */ - int lineno; /* saved line number */ - FILE * F; /* the open stream */ - PTR * p; /* the char pointer */ -} IFile; - -static Lst includes; /* stack of IFiles generated by - * #includes */ -Lst parseIncPath; /* list of directories for "..." includes */ -Lst sysIncPath; /* list of directories for <...> includes */ - -/*- - * specType contains the SPECial TYPE of the current target. It is - * Not if the target is unspecial. If it *is* special, however, the children - * are linked as children of the parent but not vice versa. This variable is - * set in ParseDoDependency - */ -typedef enum { - Begin, /* .BEGIN */ - Default, /* .DEFAULT */ - End, /* .END */ - Ignore, /* .IGNORE */ - Includes, /* .INCLUDES */ - Interrupt, /* .INTERRUPT */ - Libs, /* .LIBS */ - MFlags, /* .MFLAGS or .MAKEFLAGS */ - Main, /* .MAIN and we don't have anything user-specified to - * make */ - NoExport, /* .NOEXPORT */ - Not, /* Not special */ - NotParallel, /* .NOTPARALELL */ - Null, /* .NULL */ - Order, /* .ORDER */ - ExPath, /* .PATH */ - Precious, /* .PRECIOUS */ - Reserved, /* .RESERVED or .[A-Z]* */ - ExShell, /* .SHELL */ - Silent, /* .SILENT */ - SingleShell, /* .SINGLESHELL */ - Suffixes, /* .SUFFIXES */ - Attribute /* Generic attribute */ -} ParseSpecial; - -static ParseSpecial specType; - -/* - * Predecessor node for handling .ORDER. Initialized to NILGNODE when .ORDER - * seen, then set to each successive source on the line. - */ -static GNode *predecessor; - -/* - * The parseKeywords table is searched using binary search when deciding - * if a target or source is special. The 'spec' field is the ParseSpecial - * type of the keyword ("Not" if the keyword isn't special as a target) while - * the 'op' field is the operator to apply to the list of targets if the - * keyword is used as a source ("0" if the keyword isn't special as a source) - */ -static struct { - char *name; /* Name of keyword */ - ParseSpecial spec; /* Type when used as a target */ - int op; /* Operator when used as a source */ -} parseKeywords[] = { -#define DOT_BEGIN 0 -{ ".BEGIN", Begin, 0 }, -#define DOT_DEFAULT 1 -{ ".DEFAULT", Default, 0 }, -#define DOT_END 2 -{ ".END", End, 0 }, -#define DOT_EXEC 3 -{ ".EXEC", Attribute, OP_EXEC }, -#define DOT_IGNORE 4 -{ ".IGNORE", Ignore, OP_IGNORE }, -#define DOT_INCLUDES 5 -{ ".INCLUDES", Includes, 0 }, -#define DOT_INTERRUPT 6 -{ ".INTERRUPT", Interrupt, 0 }, -#define DOT_INVISIBLE 7 -{ ".INVISIBLE", Attribute, OP_INVISIBLE }, -#define DOT_JOIN 8 -{ ".JOIN", Attribute, OP_JOIN }, -#define DOT_LIBS 9 -{ ".LIBS", Libs, 0 }, -#define DOT_MAIN 10 -{ ".MAIN", Main, 0 }, -#define DOT_MAKE 11 -{ ".MAKE", Attribute, OP_MAKE }, -#define DOT_MAKEFLAGS 12 -{ ".MAKEFLAGS", MFlags, 0 }, -#define DOT_MFLAGS 13 -{ ".MFLAGS", MFlags, 0 }, -#define DOT_NOTMAIN 14 -{ ".NOTMAIN", Attribute, OP_NOTMAIN }, -#define DOT_NOTPARALLEL 15 -{ ".NOTPARALLEL", NotParallel, 0 }, -#define DOT_NULL 16 -{ ".NULL", Null, 0 }, -#define DOT_OPTIONAL 17 -{ ".OPTIONAL", Attribute, OP_OPTIONAL }, -#define DOT_ORDER 18 -{ ".ORDER", Order, 0 }, -#define DOT_PATH 19 -{ ".PATH", ExPath, 0 }, -#define DOT_PHONY 20 -{ ".PHONY", Attribute, OP_PHONY }, -#define DOT_PRECIOUS 21 -{ ".PRECIOUS", Precious, OP_PRECIOUS }, -#define DOT_RECURSIVE 22 -{ ".RECURSIVE", Attribute, OP_MAKE }, -#define DOT_RESERVED 23 -{ ".RESERVED", Reserved, 0 }, -#define DOT_SHELL 24 -{ ".SHELL", ExShell, 0 }, -#define DOT_SILENT 25 -{ ".SILENT", Silent, OP_SILENT }, -#define DOT_SINGLESHELL 26 -{ ".SINGLESHELL", SingleShell, 0 }, -#define DOT_SUFFIXES 27 -{ ".SUFFIXES", Suffixes, 0 }, -#define DOT_USE 28 -{ ".USE", Attribute, OP_USE }, -}; - -static int ParseFindKeyword __P((char *)); -static int ParseLinkSrc __P((ClientData, ClientData)); -static int ParseDoOp __P((ClientData, ClientData)); -static void ParseDoSrc __P((int, char *)); -static int ParseFindMain __P((ClientData, ClientData)); -static int ParseAddDir __P((ClientData, ClientData)); -static int ParseClearPath __P((ClientData, ClientData)); -static void ParseDoDependency __P((char *)); -static int ParseAddCmd __P((ClientData, ClientData)); -static int ParseReadc __P((void)); -static void ParseUnreadc __P((int)); -static void ParseHasCommands __P((ClientData)); -static void ParseDoInclude __P((char *)); -#ifdef SYSVINCLUDE -static void ParseTraditionalInclude __P((char *)); -#endif -static int ParseEOF __P((int)); -static char *ParseReadLine __P((void)); -static char *ParseSkipLine __P((int)); -static void ParseFinishLine __P((void)); - -/*- - *---------------------------------------------------------------------- - * ParseFindKeyword -- - * Look in the table of keywords for one matching the given string. - * - * Results: - * The index of the keyword, or -1 if it isn't there. - * - * Side Effects: - * None - *---------------------------------------------------------------------- - */ -static int -ParseFindKeyword (str) - char *str; /* String to find */ -{ - register int start, - end, - cur; - register 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); - - cur = 0; - for (++str; *str; str++) - if (!isupper((unsigned char) *str)) - break; - return *str ? -1 : DOT_RESERVED; -} - -/*- - * Parse_Error -- - * 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 */ -void -#if __STDC__ -Parse_Error(int type, char *fmt, ...) -#else -Parse_Error(va_alist) - va_dcl -#endif -{ - va_list ap; -#if __STDC__ - va_start(ap, fmt); -#else - int type; /* Error type (PARSE_WARNING, PARSE_FATAL) */ - char *fmt; - - va_start(ap); - type = va_arg(ap, int); - fmt = va_arg(ap, char *); -#endif - - (void)fprintf(stderr, "\"%s\", line %d: ", fname, lineno); - if (type == PARSE_WARNING) - (void)fprintf(stderr, "warning: "); - (void)vfprintf(stderr, fmt, ap); - va_end(ap); - (void)fprintf(stderr, "\n"); - (void)fflush(stderr); - if (type == PARSE_FATAL) - fatals += 1; -} - -/*- - *--------------------------------------------------------------------- - * 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. - * - * 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 (pgnp, cgnp) - ClientData pgnp; /* The parent node */ - ClientData cgnp; /* The child node */ -{ - GNode *pgn = (GNode *) pgnp; - GNode *cgn = (GNode *) cgnp; - if (Lst_Member (pgn->children, (ClientData)cgn) == NILLNODE) { - (void)Lst_AtEnd (pgn->children, (ClientData)cgn); - if (specType == Not) { - (void)Lst_AtEnd (cgn->parents, (ClientData)pgn); - } - pgn->unmade += 1; - } - return (0); -} - -/*- - *--------------------------------------------------------------------- - * ParseDoOp -- - * Apply the parsed operator to the given target node. Used in a - * Lst_ForEach call by ParseDoDependency once all targets have - * been found and their operator parsed. If the previous and new - * operators are incompatible, a major error is taken. - * - * Results: - * Always 0 - * - * Side Effects: - * The type field of the node is altered to reflect any new bits in - * the op. - *--------------------------------------------------------------------- - */ -static int -ParseDoOp (gnp, opp) - ClientData gnp; /* The node to which the operator is to be - * applied */ - ClientData opp; /* The operator to apply */ -{ - 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. - */ - register GNode *cohort; - LstNode ln; - - cohort = Targ_NewGN(gn->name); - /* - * Duplicate links to parents so graph traversal is simple. Perhaps - * some type bits should be duplicated? - * - * 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. - */ - Lst_ForEach(gn->parents, ParseLinkSrc, (ClientData)cohort); - cohort->type = OP_DOUBLEDEP|OP_INVISIBLE; - (void)Lst_AtEnd(gn->cohorts, (ClientData)cohort); - - /* - * Replace the node in the targets list with the new copy - */ - ln = Lst_Member(targets, (ClientData)gn); - Lst_Replace(ln, (ClientData)cohort); - gn = cohort; - } - /* - * 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' - * - * 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 (tOp, src) - int tOp; /* operator (if any) from special targets */ - char *src; /* name of the source to handle */ -{ - int op; /* operator (if any) from special source */ - GNode *gn; - - op = 0; - if (*src == '.' && isupper (src[1])) { - int keywd = ParseFindKeyword(src); - if (keywd != -1) { - op = parseKeywords[keywd].op; - } - } - if (op != 0) { - Lst_ForEach (targets, ParseDoOp, (ClientData)&op); - } else if (specType == Main) { - /* - * If we have noted the existence of a .MAIN, it means we need - * to add the sources of said target to the list of things - * to create. The string 'src' is likely to be free, so we - * must make a new copy of it. Note that this will only be - * invoked if the user didn't specify a target on the command - * line. This is to allow #ifmake's to succeed, or something... - */ - (void) Lst_AtEnd (create, (ClientData)strdup(src)); - /* - * Add the name to the .TARGETS variable as well, so the user cna - * employ that, if desired. - */ - Var_Append(".TARGETS", src, VAR_GLOBAL); - } else if (specType == Order) { - /* - * Create proper predecessor/successor links between the previous - * source and the current one. - */ - gn = Targ_FindNode(src, TARG_CREATE); - if (predecessor != NILGNODE) { - (void)Lst_AtEnd(predecessor->successors, (ClientData)gn); - (void)Lst_AtEnd(gn->preds, (ClientData)predecessor); - } - /* - * The current source now becomes the predecessor for the next one. - */ - predecessor = gn; - } else { - /* - * 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. - */ - gn = Targ_FindNode (src, TARG_CREATE); - if (tOp) { - gn->type |= tOp; - } else { - Lst_ForEach (targets, ParseLinkSrc, (ClientData)gn); - } - if ((gn->type & OP_OPMASK) == OP_DOUBLEDEP) { - register GNode *cohort; - register LstNode ln; - - for (ln=Lst_First(gn->cohorts); ln != NILLNODE; ln = Lst_Succ(ln)){ - cohort = (GNode *)Lst_Datum(ln); - if (tOp) { - cohort->type |= tOp; - } else { - Lst_ForEach(targets, ParseLinkSrc, (ClientData)cohort); - } - } - } - } -} - -/*- - *----------------------------------------------------------------------- - * 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. - * - * Results: - * 0 if main not found yet, 1 if it is. - * - * Side Effects: - * mainNode is changed and Targ_SetMain is called. - * - *----------------------------------------------------------------------- - */ -static int -ParseFindMain(gnp, dummy) - ClientData gnp; /* Node to examine */ - ClientData dummy; -{ - GNode *gn = (GNode *) gnp; - if ((gn->type & (OP_NOTMAIN|OP_USE|OP_EXEC|OP_TRANSFORM)) == 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(path, name) - ClientData path; - ClientData name; -{ - 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(path, dummy) - ClientData path; - ClientData dummy; -{ - Dir_ClearPath((Lst) path); - return(dummy ? 0 : 0); -} - -/*- - *--------------------------------------------------------------------- - * ParseDoDependency -- - * Parse the dependency line in line. - * - * 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 (line) - char *line; /* the line to parse */ -{ - char *cp; /* our current position */ - GNode *gn; /* 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 source names after expansion */ - Lst curTargs; /* list of target names to be found and added - * to the targets list */ - - tOp = 0; - - specType = Not; - paths = (Lst)NULL; - - curTargs = Lst_Init(FALSE); - - do { - for (cp = line; - *cp && !isspace (*cp) && - (*cp != '!') && (*cp != ':') && (*cp != '('); - cp++) - { - if (*cp == '$') { - /* - * Must be a dynamic source (would have been expanded - * otherwise), so call the Var module to parse the puppy - * so we can safely advance beyond it...There should be - * no errors in this, as they would have been discovered - * in the initial Var_Subst and we wouldn't be here. - */ - int length; - Boolean freeIt; - char *result; - - result=Var_Parse(cp, VAR_CMD, TRUE, &length, &freeIt); - - if (freeIt) { - free(result); - } - cp += length-1; - } - continue; - } - if (*cp == '(') { - /* - * Archives must be handled specially to make sure the OP_ARCHV - * flag is set in their 'type' field, for one thing, and because - * things like "archive(file1.o file2.o file3.o)" are permissible. - * Arch_ParseArchive will set 'line' to be the first non-blank - * after the archive-spec. It creates/finds nodes for the members - * and places them on the given list, returning SUCCESS if all - * went well and FAILURE if there was an error in the - * specification. On error, line should remain untouched. - */ - if (Arch_ParseArchive (&line, targets, VAR_CMD) != SUCCESS) { - Parse_Error (PARSE_FATAL, - "Error in archive specification: \"%s\"", line); - return; - } else { - continue; - } - } - savec = *cp; - - if (!*cp) { - /* - * Ending a dependency line without an operator is a Bozo - * no-no - */ - Parse_Error (PARSE_FATAL, "Need an operator"); - return; - } - *cp = '\0'; - /* - * Have a word in line. See if it's a special target and set - * specType to match it. - */ - if (*line == '.' && isupper (line[1])) { - /* - * See if the target is a special target that must have it - * or its sources handled specially. - */ - int keywd = ParseFindKeyword(line); - if (keywd != -1) { - if (specType == ExPath && parseKeywords[keywd].spec != ExPath) { - Parse_Error(PARSE_FATAL, "Mismatched special targets"); - return; - } - - specType = parseKeywords[keywd].spec; - tOp = parseKeywords[keywd].op; - - /* - * Certain special targets have special semantics: - * .PATH Have to set the dirSearchPath - * variable too - * .MAIN Its sources are only used if - * nothing has been specified to - * create. - * .DEFAULT Need to create a node to hang - * commands on, but we don't want - * it in the graph, nor do we want - * it to be the Main Target, so we - * create it, set OP_NOTMAIN and - * add it to the list, setting - * DEFAULT to the new node for - * later use. We claim the node is - * A transformation rule to make - * life easier later, when we'll - * use Make_HandleUse to actually - * apply the .DEFAULT commands. - * .BEGIN - * .END - * .INTERRUPT Are not to be considered the - * main target. - * .NOTPARALLEL Make only one target at a time. - * .SINGLESHELL Create a shell for each command. - * .ORDER Must set initial predecessor to NIL - */ - switch (specType) { - case ExPath: - if (paths == NULL) { - paths = Lst_Init(FALSE); - } - (void)Lst_AtEnd(paths, (ClientData)dirSearchPath); - break; - case Main: - if (!Lst_IsEmpty(create)) { - specType = Not; - } - break; - case Begin: - case End: - case Interrupt: - gn = Targ_FindNode(line, TARG_CREATE); - gn->type |= OP_NOTMAIN; - (void)Lst_AtEnd(targets, (ClientData)gn); - break; - case Default: - gn = Targ_NewGN(".DEFAULT"); - gn->type |= (OP_NOTMAIN|OP_TRANSFORM); - (void)Lst_AtEnd(targets, (ClientData)gn); - DEFAULT = gn; - break; - case NotParallel: - { - extern int maxJobs; - - maxJobs = 1; - break; - } - case SingleShell: - compatMake = 1; - break; - case Order: - predecessor = NILGNODE; - break; - case Reserved: - /* - * A posix reserved target that we don't know - * how to deal with. - */ - return; - default: - break; - } - } else if (strncmp (line, ".PATH", 5) == 0) { - /* - * .PATH has to be handled specially. - * Call on the suffix module to give us a path to - * modify. - */ - Lst path; - - specType = ExPath; - path = Suff_GetPath (&line[5]); - if (path == NILLST) { - Parse_Error (PARSE_FATAL, - "Suffix '%s' not defined (yet)", - &line[5]); - return; - } else { - if (paths == (Lst)NULL) { - paths = Lst_Init(FALSE); - } - (void)Lst_AtEnd(paths, (ClientData)path); - } - } - } - - /* - * Have word in line. Get or create its node and stick it at - * the end of the targets list - */ - if ((specType == Not) && (*line != '\0')) { - if (Dir_HasWildcards(line)) { - /* - * Targets are to be sought only in the current directory, - * so create an empty path for the thing. Note we need to - * use Dir_Destroy in the destruction of the path as the - * Dir module could have added a directory to the path... - */ - Lst emptyPath = Lst_Init(FALSE); - - Dir_Expand(line, emptyPath, curTargs); - - Lst_Destroy(emptyPath, Dir_Destroy); - } else { - /* - * No wildcards, but we want to avoid code duplication, - * so create a list with the word on it. - */ - (void)Lst_AtEnd(curTargs, (ClientData)line); - } - - while(!Lst_IsEmpty(curTargs)) { - char *targName = (char *)Lst_DeQueue(curTargs); - - if (!Suff_IsTransform (targName)) { - gn = Targ_FindNode (targName, TARG_CREATE); - } else { - gn = Suff_AddTransform (targName); - } - - (void)Lst_AtEnd (targets, (ClientData)gn); - } - } else if (specType == ExPath && *line != '.' && *line != '\0') { - Parse_Error(PARSE_WARNING, "Extra target (%s) ignored", line); - } - - *cp = savec; - /* - * If it is a special type and not .PATH, it's the only target we - * allow on this line... - */ - if (specType != Not && specType != ExPath) { - Boolean warn = FALSE; - - while ((*cp != '!') && (*cp != ':') && *cp) { - if (*cp != ' ' && *cp != '\t') { - warn = TRUE; - } - cp++; - } - if (warn) { - Parse_Error(PARSE_WARNING, "Extra target ignored"); - } - } else { - while (*cp && isspace (*cp)) { - cp++; - } - } - line = cp; - } while ((*line != '!') && (*line != ':') && *line); - - /* - * Don't need the list of target names anymore... - */ - Lst_Destroy(curTargs, NOFREE); - - if (!Lst_IsEmpty(targets)) { - switch(specType) { - default: - Parse_Error(PARSE_WARNING, "Special and mundane targets don't mix. Mundane ones ignored"); - break; - case Default: - case Begin: - case End: - case Interrupt: - /* - * These four create nodes on which to hang commands, so - * targets shouldn't be empty... - */ - case Not: - /* - * Nothing special here -- targets can be empty if it wants. - */ - break; - } - } - - /* - * Have now parsed all the target names. Must parse the operator next. The - * result is left in op . - */ - if (*cp == '!') { - op = OP_FORCE; - } else if (*cp == ':') { - if (cp[1] == ':') { - op = OP_DOUBLEDEP; - cp++; - } else { - op = OP_DEPENDS; - } - } else { - Parse_Error (PARSE_FATAL, "Missing dependency operator"); - return; - } - - cp++; /* Advance beyond operator */ - - Lst_ForEach (targets, ParseDoOp, (ClientData)&op); - - /* - * Get to the first source - */ - while (*cp && isspace (*cp)) { - cp++; - } - line = cp; - - /* - * Several special targets take different actions if present with no - * sources: - * a .SUFFIXES line with no sources clears out all old suffixes - * a .PRECIOUS line makes all targets precious - * a .IGNORE line ignores errors for all targets - * a .SILENT line creates silence when making all targets - * a .PATH removes all directories from the search path(s). - */ - if (!*line) { - switch (specType) { - case Suffixes: - Suff_ClearSuffixes (); - break; - case Precious: - allPrecious = TRUE; - break; - case Ignore: - ignoreErrors = TRUE; - break; - case Silent: - beSilent = TRUE; - break; - case ExPath: - Lst_ForEach(paths, ParseClearPath, (ClientData)NULL); - break; - default: - break; - } - } else if (specType == MFlags) { - /* - * Call on functions in main.c to deal with these arguments and - * set the initial character to a null-character so the loop to - * get sources won't get anything - */ - Main_ParseArgLine (line); - *line = '\0'; - } else if (specType == ExShell) { - if (Job_ParseShell (line) != SUCCESS) { - Parse_Error (PARSE_FATAL, "improper shell specification"); - return; - } - *line = '\0'; - } else if ((specType == NotParallel) || (specType == SingleShell)) { - *line = '\0'; - } - - /* - * NOW GO FOR THE SOURCES - */ - if ((specType == Suffixes) || (specType == ExPath) || - (specType == Includes) || (specType == Libs) || - (specType == Null)) - { - 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. - */ - char savec; - while (*cp && !isspace (*cp)) { - cp++; - } - savec = *cp; - *cp = '\0'; - switch (specType) { - case Suffixes: - Suff_AddSuffix (line); - break; - case ExPath: - Lst_ForEach(paths, ParseAddDir, (ClientData)line); - break; - case Includes: - Suff_AddInclude (line); - break; - case Libs: - Suff_AddLib (line); - break; - case Null: - Suff_SetNull (line); - break; - default: - break; - } - *cp = savec; - if (savec != '\0') { - cp++; - } - while (*cp && isspace (*cp)) { - cp++; - } - line = cp; - } - if (paths) { - Lst_Destroy(paths, NOFREE); - } - } 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. - */ - while (*cp && !isspace (*cp)) { - if ((*cp == '(') && (cp > line) && (cp[-1] != '$')) { - /* - * Only stop for a left parenthesis if it isn't at the - * start of a word (that'll be for variable changes - * later) and isn't preceded by a dollar sign (a dynamic - * source). - */ - break; - } else { - cp++; - } - } - - if (*cp == '(') { - GNode *gn; - - sources = Lst_Init (FALSE); - if (Arch_ParseArchive (&line, sources, VAR_CMD) != SUCCESS) { - Parse_Error (PARSE_FATAL, - "Error in source archive spec \"%s\"", line); - return; - } - - while (!Lst_IsEmpty (sources)) { - gn = (GNode *) Lst_DeQueue (sources); - ParseDoSrc (tOp, gn->name); - } - Lst_Destroy (sources, NOFREE); - cp = line; - } else { - if (*cp) { - *cp = '\0'; - cp += 1; - } - - ParseDoSrc (tOp, line); - } - while (*cp && isspace (*cp)) { - cp++; - } - line = cp; - } - } - - if (mainNode == NILGNODE) { - /* - * If we have yet to decide on a main target to make, in the - * absence of any user input, we want the first target on - * the first dependency line that is actually a real target - * (i.e. isn't a .USE or .EXEC rule) to be made. - */ - Lst_ForEach (targets, ParseFindMain, (ClientData)0); - } - -} - -/*- - *--------------------------------------------------------------------- - * 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. - * - * Results: - * TRUE if it is. FALSE if it ain't - * - * Side Effects: - * none - *--------------------------------------------------------------------- - */ -Boolean -Parse_IsVar (line) - register char *line; /* the line to check */ -{ - register Boolean wasSpace = FALSE; /* set TRUE if found a space */ - register Boolean haveName = FALSE; /* Set TRUE if have a variable name */ - - /* - * Skip to variable name - */ - while ((*line == ' ') || (*line == '\t')) { - line++; - } - - while (*line != '=') { - if (*line == '\0') { - /* - * end-of-line -- can't be a variable assignment. - */ - return (FALSE); - } else if ((*line == ' ') || (*line == '\t')) { - /* - * there can be as much white space as desired so long as there is - * only one word before the operator - */ - wasSpace = TRUE; - } else if (wasSpace && haveName) { - /* - * Stop when an = operator is found. - */ - if ((*line == '+') || (*line == ':') || (*line == '?') || - (*line == '!')) { - break; - } - - /* - * This is the start of another word, so not assignment. - */ - return (FALSE); - } else { - haveName = TRUE; - wasSpace = FALSE; - } - line++; - } - - /* - * A final check: if we stopped on a +, ?, ! or :, the next character must - * be an = or it ain't a valid assignment - */ - if (((*line == '+') || - (*line == '?') || - (*line == ':') || - (*line == '!')) && - (line[1] != '=')) - { - return (FALSE); - } else { - return (haveName); - } -} - -/*- - *--------------------------------------------------------------------- - * Parse_DoVar -- - * Take the variable assignment in the passed line and do it in the - * global context. - * - * Note: There is a lexical ambiguity with assignment modifier characters - * in variable names. This routine interprets the character before the = - * as a modifier. Therefore, an assignment like - * C++=/usr/bin/CC - * is interpreted as "C+ +=" instead of "C++ =". - * - * Results: - * none - * - * Side Effects: - * the variable structure of the given variable name is altered in the - * global context. - *--------------------------------------------------------------------- - */ -void -Parse_DoVar (line, ctxt) - char *line; /* a line guaranteed to be a variable - * assignment. This reduces error checks */ - GNode *ctxt; /* Context in which to do the assignment */ -{ - 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 */ - /* - * Avoid clobbered variable warnings by forcing the compiler - * to ``unregister'' variables - */ -#if __GNUC__ - (void) &cp; - (void) &line; -#endif - - /* - * Skip to variable name - */ - while ((*line == ' ') || (*line == '\t')) { - line++; - } - - /* - * Skip to operator character, nulling out whitespace as we go - */ - for (cp = line + 1; *cp != '='; cp++) { - if (isspace (*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: - type = VAR_NORMAL; - break; - } - - while (isspace (*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; - cp = Var_Subst(NULL, cp, ctxt, FALSE); - oldVars = oldOldVars; - - Var_Set(line, cp, ctxt); - free(cp); - } else if (type == VAR_SHELL) { - char *args[4]; /* Args for invoking the shell */ - int fds[2]; /* Pipe streams */ - int cpid; /* Child PID */ - int pid; /* PID from wait() */ - Boolean freeCmd; /* TRUE if the command needs to be freed, i.e. - * if any variable expansion was performed */ - - /* - * Avoid clobbered variable warnings by forcing the compiler - * to ``unregister'' variables - */ -#if __GNUC__ - (void) &freeCmd; -#endif - - /* - * Set up arguments for shell - */ - args[0] = "sh"; - args[1] = "-c"; - if (strchr(cp, '$') != (char *)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. - */ - args[2] = Var_Subst(NULL, cp, VAR_CMD, TRUE); - freeCmd = TRUE; - } else { - args[2] = cp; - freeCmd = FALSE; - } - args[3] = (char *)NULL; - - /* - * Open a pipe for fetching its output - */ - pipe(fds); - - /* - * Fork - */ - cpid = vfork(); - if (cpid == 0) { - /* - * Close input side of pipe - */ - 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? - */ - dup2(fds[1], 1); - close(fds[1]); - - execv("/bin/sh", args); - _exit(1); - } else if (cpid < 0) { - /* - * Couldn't fork -- tell the user and make the variable null - */ - Parse_Error(PARSE_WARNING, "Couldn't exec \"%s\"", cp); - Var_Set(line, "", ctxt); - } else { - int status; - int cc; - Buffer buf; - char *res; - - /* - * No need for the writing half - */ - close(fds[1]); - - buf = Buf_Init (MAKE_BSIZE); - - do { - char result[BUFSIZ]; - cc = read(fds[0], result, sizeof(result)); - if (cc > 0) - Buf_AddBytes(buf, cc, (Byte *) result); - } - while (cc > 0 || (cc == -1 && errno == EINTR)); - - /* - * Close the input side of the pipe. - */ - close(fds[0]); - - /* - * Wait for the process to exit. - */ - while(((pid = wait(&status)) != cpid) && (pid >= 0)) - continue; - - res = (char *)Buf_GetAll (buf, &cc); - Buf_Destroy (buf, FALSE); - - if (cc == 0) { - /* - * Couldn't read the child's output -- tell the user and - * set the variable to null - */ - Parse_Error(PARSE_WARNING, "Couldn't read shell's output"); - } - - if (status) { - /* - * Child returned an error -- tell the user but still use - * the result. - */ - Parse_Error(PARSE_WARNING, "\"%s\" returned non-zero", cp); - } - - /* - * Null-terminate the result, convert newlines to spaces and - * install it in the variable. - */ - res[cc] = '\0'; - cp = &res[cc] - 1; - - if (*cp == '\n') { - /* - * A final newline is just stripped - */ - *cp-- = '\0'; - } - while (cp >= res) { - if (*cp == '\n') { - *cp = ' '; - } - cp--; - } - Var_Set(line, res, ctxt); - free(res); - - } - if (freeCmd) { - free(args[2]); - } - } else { - /* - * Normal assignment -- just do it. - */ - Var_Set (line, cp, ctxt); - } -} - -/*- - * ParseAddCmd -- - * Lst_ForEach function to add a command line to all targets - * - * Results: - * Always 0 - * - * Side Effects: - * A new element is added to the commands list of the node. - */ -static int -ParseAddCmd(gnp, cmd) - ClientData gnp; /* the node to which the command is to be added */ - ClientData cmd; /* the command to add */ -{ - GNode *gn = (GNode *) gnp; - /* if target already supplied, ignore commands */ - if (!(gn->type & OP_HAS_COMMANDS)) - (void)Lst_AtEnd(gn->commands, cmd); - 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. - * - * Results: - * None - * - * Side Effects: - * OP_HAS_COMMANDS may be set for the target. - * - *----------------------------------------------------------------------- - */ -static void -ParseHasCommands(gnp) - ClientData gnp; /* Node to examine */ -{ - 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 - * - * Results: - * None. - * - * Side Effects: - * The directory is appended to the list. - * - *----------------------------------------------------------------------- - */ -void -Parse_AddIncludeDir (dir) - char *dir; /* The name of the directory to add */ -{ - Dir_AddDir (parseIncPath, dir); -} - -/*- - *--------------------------------------------------------------------- - * ParseDoInclude -- - * Push to another file. - * - * The input is the line minus the #include. A file spec is a string - * enclosed in <> or "". The former is looked for only in sysIncPath. - * The latter in . and the directories specified by -I command line - * options - * - * Results: - * None - * - * Side Effects: - * A structure is added to the includes Lst and readProc, lineno, - * fname and curFILE are altered for the new file - *--------------------------------------------------------------------- - */ -static void -ParseDoInclude (file) - char *file; /* file specification */ -{ - char *fullname; /* full pathname of file */ - IFile *oldFile; /* state associated with current file */ - char endc; /* the character which ends the file spec */ - char *cp; /* current position in file spec */ - Boolean isSystem; /* TRUE if makefile is a system makefile */ - - /* - * Skip to delimiter character so we know where to look - */ - while ((*file == ' ') || (*file == '\t')) { - file++; - } - - if ((*file != '"') && (*file != '<')) { - Parse_Error (PARSE_FATAL, - ".include filename must be delimited by '\"' or '<'"); - return; - } - - /* - * Set the search path on which to find the include file based on the - * characters which bracket its name. Angle-brackets imply it's - * a system Makefile while double-quotes imply it's a user makefile - */ - if (*file == '<') { - isSystem = TRUE; - endc = '>'; - } else { - isSystem = FALSE; - endc = '"'; - } - - /* - * Skip to matching delimiter - */ - for (cp = ++file; *cp && *cp != endc; cp++) { - continue; - } - - if (*cp != endc) { - Parse_Error (PARSE_FATAL, - "Unclosed %cinclude filename. '%c' expected", - '.', endc); - return; - } - *cp = '\0'; - - /* - * Substitute for any variables in the file name before trying to - * find the thing. - */ - file = Var_Subst (NULL, file, VAR_CMD, FALSE); - - /* - * Now we know the file's name and its search path, we attempt to - * find the durn thing. A return of NULL indicates the file don't - * exist. - */ - if (!isSystem) { - /* - * Include files contained in double-quotes are first searched for - * relative to the including file's location. We don't want to - * cd there, of course, so we just tack on the old file's - * leading path components and call Dir_FindFile to see if - * we can locate the beast. - */ - char *prefEnd; - - prefEnd = strrchr (fname, '/'); - if (prefEnd != (char *)NULL) { - char *newName; - - *prefEnd = '\0'; - if (file[0] == '/') - newName = strdup(file); - else - newName = str_concat (fname, file, STR_ADDSLASH); - fullname = Dir_FindFile (newName, parseIncPath); - if (fullname == (char *)NULL) { - fullname = Dir_FindFile(newName, dirSearchPath); - } - free (newName); - *prefEnd = '/'; - } else { - fullname = (char *)NULL; - } - } else { - fullname = (char *)NULL; - } - - if (fullname == (char *)NULL) { - /* - * System makefile or makefile wasn't found in same directory as - * included makefile. Search for it first on the -I search path, - * then on the .PATH search path, if not found in a -I directory. - * XXX: Suffix specific? - */ - fullname = Dir_FindFile (file, parseIncPath); - if (fullname == (char *)NULL) { - fullname = Dir_FindFile(file, dirSearchPath); - } - } - - if (fullname == (char *)NULL) { - /* - * Still haven't found the makefile. Look for it on the system - * path as a last resort. - */ - fullname = Dir_FindFile(file, sysIncPath); - } - - if (fullname == (char *) NULL) { - *cp = endc; - Parse_Error (PARSE_FATAL, "Could not find %s", file); - return; - } - - free(file); - - /* - * Once we find the absolute path to the file, we get to save all the - * state from the current file before we can start reading this - * include file. The state is stored in an IFile structure which - * is placed on a list with other IFile structures. The list makes - * a very nice stack to track how we got here... - */ - oldFile = (IFile *) emalloc (sizeof (IFile)); - oldFile->fname = fname; - - oldFile->F = curFILE; - oldFile->p = curPTR; - oldFile->lineno = lineno; - - (void) Lst_AtFront (includes, (ClientData)oldFile); - - /* - * Once the previous state has been saved, we can get down to reading - * the new file. We set up the name of the file to be the absolute - * name of the include file so error messages refer to the right - * place. Naturally enough, we start reading at line number 0. - */ - fname = fullname; - lineno = 0; - - curFILE = fopen (fullname, "r"); - curPTR = NULL; - if (curFILE == (FILE * ) NULL) { - Parse_Error (PARSE_FATAL, "Cannot open %s", fullname); - /* - * Pop to previous file - */ - (void) ParseEOF(0); - } -} - - -/*- - *--------------------------------------------------------------------- - * Parse_FromString -- - * Start Parsing from the given string - * - * Results: - * None - * - * Side Effects: - * A structure is added to the includes Lst and readProc, lineno, - * fname and curFILE are altered for the new file - *--------------------------------------------------------------------- - */ -void -Parse_FromString(str) - char *str; -{ - IFile *oldFile; /* state associated with this file */ - - if (DEBUG(FOR)) - (void) fprintf(stderr, "%s\n----\n", str); - - oldFile = (IFile *) emalloc (sizeof (IFile)); - oldFile->lineno = lineno; - oldFile->fname = fname; - oldFile->F = curFILE; - oldFile->p = curPTR; - - (void) Lst_AtFront (includes, (ClientData)oldFile); - - curFILE = NULL; - curPTR = (PTR *) emalloc (sizeof (PTR)); - curPTR->str = curPTR->ptr = str; - lineno = 0; - fname = strdup(fname); -} - - -#ifdef SYSVINCLUDE -/*- - *--------------------------------------------------------------------- - * ParseTraditionalInclude -- - * Push to another file. - * - * The input is the line minus the "include". The file name is - * the string 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 (file) - char *file; /* file specification */ -{ - char *fullname; /* full pathname of file */ - IFile *oldFile; /* state associated with current file */ - char *cp; /* current position in file spec */ - char *prefEnd; - - /* - * Skip over whitespace - */ - while ((*file == ' ') || (*file == '\t')) { - file++; - } - - if (*file == '\0') { - Parse_Error (PARSE_FATAL, - "Filename missing from \"include\""); - return; - } - - /* - * Skip to end of line or next whitespace - */ - for (cp = file; *cp && *cp != '\n' && *cp != '\t' && *cp != ' '; cp++) { - continue; - } - - *cp = '\0'; - - /* - * Substitute for any variables in the file name before trying to - * find the thing. - */ - file = Var_Subst (NULL, file, VAR_CMD, FALSE); - - /* - * Now we know the file's name, we attempt to find the durn thing. - * A return of NULL indicates the file don't exist. - * - * Include files are first searched for relative to the including - * file's location. We don't want to cd there, of course, so we - * just tack on the old file's leading path components and call - * Dir_FindFile to see if we can locate the beast. - * XXX - this *does* search in the current directory, right? - */ - - prefEnd = strrchr (fname, '/'); - if (prefEnd != (char *)NULL) { - char *newName; - - *prefEnd = '\0'; - newName = str_concat (fname, file, STR_ADDSLASH); - fullname = Dir_FindFile (newName, parseIncPath); - if (fullname == (char *)NULL) { - fullname = Dir_FindFile(newName, dirSearchPath); - } - free (newName); - *prefEnd = '/'; - } else { - fullname = (char *)NULL; - } - - if (fullname == (char *)NULL) { - /* - * System makefile or makefile wasn't found in same directory as - * included makefile. Search for it first on the -I search path, - * then on the .PATH search path, if not found in a -I directory. - * XXX: Suffix specific? - */ - fullname = Dir_FindFile (file, parseIncPath); - if (fullname == (char *)NULL) { - fullname = Dir_FindFile(file, dirSearchPath); - } - } - - if (fullname == (char *)NULL) { - /* - * Still haven't found the makefile. Look for it on the system - * path as a last resort. - */ - fullname = Dir_FindFile(file, sysIncPath); - } - - if (fullname == (char *) NULL) { - Parse_Error (PARSE_FATAL, "Could not find %s", file); - return; - } - - /* - * Once we find the absolute path to the file, we get to save all the - * state from the current file before we can start reading this - * include file. The state is stored in an IFile structure which - * is placed on a list with other IFile structures. The list makes - * a very nice stack to track how we got here... - */ - oldFile = (IFile *) emalloc (sizeof (IFile)); - oldFile->fname = fname; - - oldFile->F = curFILE; - oldFile->p = curPTR; - oldFile->lineno = lineno; - - (void) Lst_AtFront (includes, (ClientData)oldFile); - - /* - * Once the previous state has been saved, we can get down to reading - * the new file. We set up the name of the file to be the absolute - * name of the include file so error messages refer to the right - * place. Naturally enough, we start reading at line number 0. - */ - fname = fullname; - lineno = 0; - - curFILE = fopen (fullname, "r"); - curPTR = NULL; - if (curFILE == (FILE * ) NULL) { - Parse_Error (PARSE_FATAL, "Cannot open %s", fullname); - /* - * Pop to previous file - */ - (void) ParseEOF(1); - } -} -#endif - -/*- - *--------------------------------------------------------------------- - * ParseEOF -- - * Called when EOF is reached in the current file. If we were reading - * an include file, the includes stack is popped and things set up - * to go back to reading the previous file at the previous location. - * - * Results: - * CONTINUE if there's more to do. DONE if not. - * - * Side Effects: - * The old curFILE, is closed. The includes list is shortened. - * lineno, curFILE, and fname are changed if CONTINUE is returned. - *--------------------------------------------------------------------- - */ -static int -ParseEOF (opened) - int opened; -{ - IFile *ifile; /* the state on the top of the includes stack */ - - if (Lst_IsEmpty (includes)) { - return (DONE); - } - - ifile = (IFile *) Lst_DeQueue (includes); - free ((Address) fname); - fname = ifile->fname; - lineno = ifile->lineno; - if (opened && curFILE) - (void) fclose (curFILE); - if (curPTR) { - free((Address) curPTR->str); - free((Address) curPTR); - } - curFILE = ifile->F; - curPTR = ifile->p; - free ((Address)ifile); - return (CONTINUE); -} - -/*- - *--------------------------------------------------------------------- - * ParseReadc -- - * Read a character from the current file - * - * Results: - * The character that was read - * - * Side Effects: - *--------------------------------------------------------------------- - */ -static int -ParseReadc() -{ - if (curFILE) - return fgetc(curFILE); - - if (curPTR && *curPTR->ptr) - return *curPTR->ptr++; - return EOF; -} - - -/*- - *--------------------------------------------------------------------- - * ParseUnreadc -- - * Put back a character to the current file - * - * Results: - * None. - * - * Side Effects: - *--------------------------------------------------------------------- - */ -static void -ParseUnreadc(c) - int c; -{ - if (curFILE) { - ungetc(c, curFILE); - return; - } - if (curPTR) { - *--(curPTR->ptr) = c; - return; - } -} - - -/* ParseSkipLine(): - * Grab the next line - */ -static char * -ParseSkipLine(skip) - int skip; /* Skip lines that don't start with . */ -{ - char *line; - int c, lastc = '\0', lineLength; - Buffer buf; - - c = ParseReadc(); - - if (skip) { - /* - * Skip lines until get to one that begins with a - * special char. - */ - while ((c != '.') && (c != EOF)) { - while (((c != '\n') || (lastc == '\\')) && (c != EOF)) - { - /* - * Advance to next unescaped newline - */ - if ((lastc = c) == '\n') { - lineno++; - } - c = ParseReadc(); - } - lineno++; - - lastc = c; - c = ParseReadc (); - } - } - - if (c == EOF) { - Parse_Error (PARSE_FATAL, "Unclosed conditional/for loop"); - return ((char *)NULL); - } - - /* - * Read the entire line into buf - */ - buf = Buf_Init (MAKE_BSIZE); - if (c != '\n') { - do { - Buf_AddByte (buf, (Byte)c); - c = ParseReadc(); - } while ((c != '\n') && (c != EOF)); - } - lineno++; - - Buf_AddByte (buf, (Byte)'\0'); - line = (char *)Buf_GetAll (buf, &lineLength); - Buf_Destroy (buf, FALSE); - return line; -} - - -/*- - *--------------------------------------------------------------------- - * ParseReadLine -- - * Read an entire line from the input file. Called only by Parse_File. - * To facilitate escaped newlines and what have you, a character is - * buffered in 'lastc', which is '\0' when no characters have been - * read. When we break out of the loop, c holds the terminating - * character and lastc holds a character that should be added to - * the line (unless we don't read anything but a terminator). - * - * Results: - * A line w/o its newline - * - * Side Effects: - * Only those associated with reading a character - *--------------------------------------------------------------------- - */ -static char * -ParseReadLine () -{ - Buffer buf; /* Buffer for current line */ - register int c; /* the current character */ - register int lastc; /* The most-recent character */ - Boolean semiNL; /* treat semi-colons as newlines */ - Boolean ignDepOp; /* TRUE if should ignore dependency operators - * for the purposes of setting semiNL */ - Boolean ignComment; /* TRUE if should ignore comments (in a - * shell command */ - char *line; /* Result */ - char *ep; /* to strip trailing blanks */ - int lineLength; /* Length of result */ - - semiNL = FALSE; - ignDepOp = FALSE; - ignComment = FALSE; - - /* - * Handle special-characters at the beginning of the line. Either a - * leading tab (shell command) or pound-sign (possible conditional) - * forces us to ignore comments and dependency operators and treat - * semi-colons as semi-colons (by leaving semiNL FALSE). This also - * discards completely blank lines. - */ - for (;;) { - c = ParseReadc(); - - if (c == '\t') { - ignComment = ignDepOp = TRUE; - break; - } else if (c == '\n') { - lineno++; - } else if (c == '#') { - ParseUnreadc(c); - break; - } else { - /* - * Anything else breaks out without doing anything - */ - break; - } - } - - if (c != EOF) { - lastc = c; - buf = Buf_Init(MAKE_BSIZE); - - while (((c = ParseReadc ()) != '\n' || (lastc == '\\')) && - (c != EOF)) - { -test_char: - switch(c) { - case '\n': - /* - * Escaped newline: read characters until a non-space or an - * unescaped newline and replace them all by a single space. - * This is done by storing the space over the backslash and - * dropping through with the next nonspace. If it is a - * semi-colon and semiNL is TRUE, it will be recognized as a - * newline in the code below this... - */ - lineno++; - lastc = ' '; - while ((c = ParseReadc ()) == ' ' || c == '\t') { - continue; - } - if (c == EOF || c == '\n') { - goto line_read; - } else { - /* - * Check for comments, semiNL's, etc. -- easier than - * ParseUnreadc(c); continue; - */ - goto test_char; - } - /*NOTREACHED*/ - break; - - case ';': - /* - * Semi-colon: Need to see if it should be interpreted as a - * newline - */ - if (semiNL) { - /* - * To make sure the command that may be following this - * semi-colon begins with a tab, we push one back into the - * input stream. This will overwrite the semi-colon in the - * buffer. If there is no command following, this does no - * harm, since the newline remains in the buffer and the - * whole line is ignored. - */ - ParseUnreadc('\t'); - goto line_read; - } - break; - case '=': - if (!semiNL) { - /* - * Haven't seen a dependency operator before this, so this - * must be a variable assignment -- don't pay attention to - * dependency operators after this. - */ - ignDepOp = TRUE; - } else if (lastc == ':' || lastc == '!') { - /* - * Well, we've seen a dependency operator already, but it - * was the previous character, so this is really just an - * expanded variable assignment. Revert semi-colons to - * being just semi-colons again and ignore any more - * dependency operators. - * - * XXX: Note that a line like "foo : a:=b" will blow up, - * but who'd write a line like that anyway? - */ - ignDepOp = TRUE; semiNL = FALSE; - } - break; - case '#': - if (!ignComment) { - if (compatMake && (lastc != '\\')) { - /* - * If the character is a hash mark and it isn't escaped - * (or we're being compatible), the thing is a comment. - * Skip to the end of the line. - */ - do { - c = ParseReadc(); - } while ((c != '\n') && (c != EOF)); - goto line_read; - } else { - /* - * Don't add the backslash. Just let the # get copied - * over. - */ - lastc = c; - continue; - } - } - break; - case ':': - case '!': - if (!ignDepOp && (c == ':' || c == '!')) { - /* - * A semi-colon is recognized as a newline only on - * dependency lines. Dependency lines are lines with a - * colon or an exclamation point. Ergo... - */ - semiNL = TRUE; - } - break; - } - /* - * Copy in the previous character and save this one in lastc. - */ - Buf_AddByte (buf, (Byte)lastc); - lastc = c; - - } - line_read: - lineno++; - - if (lastc != '\0') { - Buf_AddByte (buf, (Byte)lastc); - } - Buf_AddByte (buf, (Byte)'\0'); - line = (char *)Buf_GetAll (buf, &lineLength); - Buf_Destroy (buf, FALSE); - - /* - * Strip trailing blanks and tabs from the line. - * Do not strip a blank or tab that is preceeded by - * a '\' - */ - ep = line; - while (*ep) - ++ep; - while (ep > line && (ep[-1] == ' ' || ep[-1] == '\t')) { - if (ep > line + 1 && ep[-2] == '\\') - break; - --ep; - } - *ep = 0; - - if (line[0] == '.') { - /* - * The line might be a conditional. Ask the conditional module - * about it and act accordingly - */ - switch (Cond_Eval (line)) { - case COND_SKIP: - /* - * Skip to next conditional that evaluates to COND_PARSE. - */ - do { - free (line); - line = ParseSkipLine(1); - } while (line && Cond_Eval(line) != COND_PARSE); - if (line == NULL) - break; - /*FALLTHRU*/ - case COND_PARSE: - free ((Address) line); - line = ParseReadLine(); - break; - case COND_INVALID: - if (For_Eval(line)) { - int ok; - free(line); - do { - /* - * Skip after the matching end - */ - line = ParseSkipLine(0); - if (line == NULL) { - Parse_Error (PARSE_FATAL, - "Unexpected end of file in for loop.\n"); - break; - } - ok = For_Eval(line); - free(line); - } - while (ok); - if (line != NULL) - For_Run(); - line = ParseReadLine(); - } - break; - } - } - return (line); - - } else { - /* - * Hit end-of-file, so return a NULL line to indicate this. - */ - return((char *)NULL); - } -} - -/*- - *----------------------------------------------------------------------- - * ParseFinishLine -- - * Handle the end of a dependency group. - * - * Results: - * Nothing. - * - * Side Effects: - * inLine set FALSE. 'targets' list destroyed. - * - *----------------------------------------------------------------------- - */ -static void -ParseFinishLine() -{ - if (inLine) { - Lst_ForEach(targets, Suff_EndTransform, (ClientData)NULL); - Lst_Destroy (targets, ParseHasCommands); - targets = NULL; - inLine = FALSE; - } -} - - -/*- - *--------------------------------------------------------------------- - * Parse_File -- - * Parse a file into its component parts, incorporating it into the - * current dependency graph. This is the main function and controls - * almost every other function in this module - * - * Results: - * None - * - * Side Effects: - * Loads. Nodes are added to the list of all targets, nodes and links - * are added to the dependency graph. etc. etc. etc. - *--------------------------------------------------------------------- - */ -void -Parse_File(name, stream) - char *name; /* the name of the file being read */ - FILE * stream; /* Stream open to makefile to parse */ -{ - register char *cp, /* pointer into the line */ - *line; /* the line we're working on */ - - inLine = FALSE; - fname = name; - curFILE = stream; - lineno = 0; - fatals = 0; - - do { - while ((line = ParseReadLine ()) != NULL) { - if (*line == '.') { - /* - * Lines that begin with the special character are either - * include or undef directives. - */ - for (cp = line + 1; isspace (*cp); cp++) { - continue; - } - if (strncmp (cp, "include", 7) == 0) { - ParseDoInclude (cp + 7); - goto nextLine; - } else if (strncmp(cp, "undef", 5) == 0) { - char *cp2; - for (cp += 5; isspace((unsigned char) *cp); cp++) { - continue; - } - - for (cp2 = cp; !isspace((unsigned char) *cp2) && - (*cp2 != '\0'); cp2++) { - continue; - } - - *cp2 = '\0'; - - Var_Delete(cp, VAR_GLOBAL); - goto nextLine; - } - } - if (*line == '#' || *line == '\0') { - /* If we're this far, the line must be a comment. - (Empty lines are ignored as well) */ - goto nextLine; - } - - if (*line == '\t') { - /* - * If a line starts with a tab, it can only hope to be - * a creation command. - */ -#ifndef POSIX - shellCommand: -#endif - for (cp = line + 1; isspace (*cp); cp++) { - continue; - } - if (*cp) { - if (inLine) { - /* - * So long as it's not a blank line and we're actually - * in a dependency spec, add the command to the list of - * commands of all targets in the dependency spec - */ - Lst_ForEach (targets, ParseAddCmd, cp); - Lst_AtEnd(targCmds, (ClientData) line); - continue; - } else { - Parse_Error (PARSE_FATAL, - "Unassociated shell command \"%.20s\"", - cp); - } - } -#ifdef SYSVINCLUDE - } else if (strncmp (line, "include", 7) == 0 && - strchr(line, ':') == NULL) { - /* - * It's an S3/S5-style "include". - */ - ParseTraditionalInclude (line + 7); - goto nextLine; -#endif - } else if (Parse_IsVar (line)) { - ParseFinishLine(); - Parse_DoVar (line, VAR_GLOBAL); - } else { - /* - * We now know it's a dependency line so it needs to have all - * variables expanded before being parsed. Tell the variable - * module to complain if some variable is undefined... - * To make life easier on novices, if the line is indented we - * first make sure the line has a dependency operator in it. - * If it doesn't have an operator and we're in a dependency - * line's script, we assume it's actually a shell command - * and add it to the current list of targets. - */ -#ifndef POSIX - Boolean nonSpace = FALSE; -#endif - - cp = line; - if (isspace((unsigned char) line[0])) { - while ((*cp != '\0') && isspace((unsigned char) *cp)) { - cp++; - } - if (*cp == '\0') { - goto nextLine; - } -#ifndef POSIX - while ((*cp != ':') && (*cp != '!') && (*cp != '\0')) { - nonSpace = TRUE; - cp++; - } -#endif - } - -#ifndef POSIX - if (*cp == '\0') { - if (inLine) { - Parse_Error (PARSE_WARNING, - "Shell command needs a leading tab"); - goto shellCommand; - } else if (nonSpace) { - Parse_Error (PARSE_FATAL, "Missing operator"); - } - } else { -#endif - ParseFinishLine(); - - cp = Var_Subst (NULL, line, VAR_CMD, TRUE); - free (line); - line = cp; - - /* - * Need a non-circular list for the target nodes - */ - if (targets) - Lst_Destroy(targets, NOFREE); - - targets = Lst_Init (FALSE); - inLine = TRUE; - - ParseDoDependency (line); -#ifndef POSIX - } -#endif - } - - nextLine: - - free (line); - } - /* - * Reached EOF, but it may be just EOF of an include file... - */ - } while (ParseEOF(1) == CONTINUE); - - /* - * Make sure conditionals are clean - */ - Cond_End(); - - if (fatals) { - fprintf (stderr, "Fatal errors encountered -- cannot continue\n"); - exit (1); - } -} - -/*- - *--------------------------------------------------------------------- - * Parse_Init -- - * initialize the parsing module - * - * Results: - * none - * - * Side Effects: - * the parseIncPath list is initialized... - *--------------------------------------------------------------------- - */ -void -Parse_Init () -{ - char *cp = NULL, *start; - /* avoid faults on read-only strings */ - static char syspath[] = _PATH_DEFSYSPATH; - - mainNode = NILGNODE; - parseIncPath = Lst_Init (FALSE); - sysIncPath = Lst_Init (FALSE); - includes = Lst_Init (FALSE); - targCmds = Lst_Init (FALSE); - - /* - * Add the directories from the DEFSYSPATH (more than one may be given - * as dir1:...:dirn) to the system include path. - */ - for (start = syspath; *start != '\0'; start = cp) { - for (cp = start; *cp != '\0' && *cp != ':'; cp++) - continue; - if (*cp == '\0') { - Dir_AddDir(sysIncPath, start); - } else { - *cp++ = '\0'; - Dir_AddDir(sysIncPath, start); - } - } -} - -void -Parse_End() -{ - Lst_Destroy(targCmds, (void (*) __P((ClientData))) free); - if (targets) - Lst_Destroy(targets, NOFREE); - Lst_Destroy(sysIncPath, Dir_Destroy); - Lst_Destroy(parseIncPath, Dir_Destroy); - Lst_Destroy(includes, NOFREE); /* Should be empty now */ -} - - -/*- - *----------------------------------------------------------------------- - * 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() -{ - Lst main; /* result list */ - - main = Lst_Init (FALSE); - - if (mainNode == NILGNODE) { - Punt ("make: no target to make.\n"); - /*NOTREACHED*/ - } else if (mainNode->type & OP_DOUBLEDEP) { - (void) Lst_AtEnd (main, (ClientData)mainNode); - Lst_Concat(main, mainNode->cohorts, LST_CONCNEW); - } - else - (void) Lst_AtEnd (main, (ClientData)mainNode); - return (main); -} diff --git a/usr.bin/make/targ.c b/usr.bin/make/targ.c deleted file mode 100644 index 49aedbd5d218..000000000000 --- a/usr.bin/make/targ.c +++ /dev/null @@ -1,657 +0,0 @@ -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char sccsid[] = "@(#)targ.c 8.3 (Berkeley) 4/28/95"; -#endif /* not lint */ - -/*- - * 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_NewGN Create a new GNode for the passed target - * (string). The node is *not* placed in the - * hash table, though all its fields are - * initialized. - * - * Targ_FindNode Find the node for a given target, creating - * and storing it if it doesn't exist and the - * flags are right (TARG_CREATE) - * - * Targ_FindList Given a list of names, find nodes for all - * of them. If a name doesn't exist and the - * TARG_NOCREATE flag was given, an error message - * is printed. Else, if a name doesn't exist, - * its node is created. - * - * Targ_Ignore Return TRUE if errors should be ignored when - * creating the given target. - * - * Targ_Silent Return TRUE if we should be silent when - * creating the given target. - * - * Targ_Precious Return TRUE if the target is precious and - * should not be removed if we are interrupted. - * - * Debugging: - * Targ_PrintGraph Print out the entire graphm all variables - * and statistics for the directory cache. Should - * print something for suffixes, too, but... - */ - -#include -#include -#include "make.h" -#include "hash.h" -#include "dir.h" - -static Lst allTargets; /* the list of all targets found so far */ -static Lst allGNs; /* List of all the GNodes */ -static Hash_Table targets; /* a hash table of same */ - -#define HTSIZE 191 /* initial size of hash table */ - -static int TargPrintOnlySrc __P((ClientData, ClientData)); -static int TargPrintName __P((ClientData, ClientData)); -static int TargPrintNode __P((ClientData, ClientData)); -static void TargFreeGN __P((ClientData)); - -/*- - *----------------------------------------------------------------------- - * Targ_Init -- - * Initialize this module - * - * Results: - * None - * - * Side Effects: - * The allTargets list and the targets hash table are initialized - *----------------------------------------------------------------------- - */ -void -Targ_Init () -{ - 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 () -{ - Lst_Destroy(allTargets, NOFREE); - if (allGNs) - Lst_Destroy(allGNs, TargFreeGN); - Hash_DeleteTable(&targets); -} - -/*- - *----------------------------------------------------------------------- - * Targ_NewGN -- - * Create and initialize a new graph 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 (name) - char *name; /* the name to stick in the new node */ -{ - register GNode *gn; - - gn = (GNode *) emalloc (sizeof (GNode)); - gn->name = strdup (name); - gn->path = (char *) 0; - if (name[0] == '-' && name[1] == 'l') { - gn->type = OP_LIB; - } else { - gn->type = 0; - } - gn->unmade = 0; - gn->make = FALSE; - gn->made = UNMADE; - gn->childMade = FALSE; - gn->mtime = gn->cmtime = 0; - gn->iParents = Lst_Init (FALSE); - gn->cohorts = Lst_Init (FALSE); - gn->parents = Lst_Init (FALSE); - gn->children = Lst_Init (FALSE); - gn->successors = Lst_Init (FALSE); - gn->preds = Lst_Init (FALSE); - gn->context = Lst_Init (FALSE); - gn->commands = Lst_Init (FALSE); - gn->suffix = NULL; - - if (allGNs == NULL) - allGNs = Lst_Init(FALSE); - Lst_AtEnd(allGNs, (ClientData) gn); - - return (gn); -} - -/*- - *----------------------------------------------------------------------- - * TargFreeGN -- - * Destroy a GNode - * - * Results: - * None. - * - * Side Effects: - * None. - *----------------------------------------------------------------------- - */ -static void -TargFreeGN (gnp) - ClientData gnp; -{ - GNode *gn = (GNode *) gnp; - - - free(gn->name); - if (gn->path) - free(gn->path); - - Lst_Destroy(gn->iParents, NOFREE); - Lst_Destroy(gn->cohorts, NOFREE); - Lst_Destroy(gn->parents, NOFREE); - Lst_Destroy(gn->children, NOFREE); - Lst_Destroy(gn->successors, NOFREE); - Lst_Destroy(gn->preds, NOFREE); - Lst_Destroy(gn->context, NOFREE); - Lst_Destroy(gn->commands, NOFREE); - free((Address)gn); -} - - -/*- - *----------------------------------------------------------------------- - * Targ_FindNode -- - * Find a node in the list using the given name for matching - * - * Results: - * The node in the list if it was. If it wasn't, return NILGNODE of - * flags was TARG_NOCREATE or the newly created and initialized node - * if it was TARG_CREATE - * - * Side Effects: - * Sometimes a node is created and added to the list - *----------------------------------------------------------------------- - */ -GNode * -Targ_FindNode (name, flags) - char *name; /* the name to find */ - int flags; /* flags governing events when target not - * found */ -{ - GNode *gn; /* node in that element */ - Hash_Entry *he; /* New or used hash entry for node */ - Boolean isNew; /* Set TRUE if Hash_CreateEntry had to create */ - /* an entry for the node */ - - - if (flags & TARG_CREATE) { - he = Hash_CreateEntry (&targets, name, &isNew); - if (isNew) { - gn = Targ_NewGN (name); - Hash_SetValue (he, gn); - (void) Lst_AtEnd (allTargets, (ClientData)gn); - } - } else { - he = Hash_FindEntry (&targets, name); - } - - if (he == (Hash_Entry *) NULL) { - return (NILGNODE); - } else { - return ((GNode *) Hash_GetValue (he)); - } -} - -/*- - *----------------------------------------------------------------------- - * Targ_FindList -- - * Make a complete list of GNodes from the given list of names - * - * 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 (names, flags) - Lst names; /* list of names to find */ - int flags; /* flags used if no node is found for a given - * name */ -{ - Lst nodes; /* result list */ - register LstNode ln; /* name list element */ - register GNode *gn; /* node in tLn */ - char *name; - - nodes = Lst_Init (FALSE); - - if (Lst_Open (names) == FAILURE) { - return (nodes); - } - while ((ln = Lst_Next (names)) != NILLNODE) { - name = (char *)Lst_Datum(ln); - gn = Targ_FindNode (name, flags); - if (gn != NILGNODE) { - /* - * Note: Lst_AtEnd must come before the Lst_Concat so the nodes - * are added to the list in the order in which they were - * encountered in the makefile. - */ - (void) Lst_AtEnd (nodes, (ClientData)gn); - if (gn->type & OP_DOUBLEDEP) { - (void)Lst_Concat (nodes, gn->cohorts, LST_CONCNEW); - } - } 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 - * - * Results: - * TRUE if should ignore errors - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ -Boolean -Targ_Ignore (gn) - GNode *gn; /* node to check for */ -{ - if (ignoreErrors || gn->type & OP_IGNORE) { - return (TRUE); - } else { - return (FALSE); - } -} - -/*- - *----------------------------------------------------------------------- - * Targ_Silent -- - * Return true if be silent when creating gn - * - * Results: - * TRUE if should be silent - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ -Boolean -Targ_Silent (gn) - GNode *gn; /* node to check for */ -{ - if (beSilent || gn->type & OP_SILENT) { - return (TRUE); - } else { - return (FALSE); - } -} - -/*- - *----------------------------------------------------------------------- - * Targ_Precious -- - * See if the given target is precious - * - * Results: - * TRUE if it is precious. FALSE otherwise - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ -Boolean -Targ_Precious (gn) - GNode *gn; /* the node to check */ -{ - 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. - * - * Results: - * None. - * - * Side Effects: - * "mainTarg" is set to the main target's node. - *----------------------------------------------------------------------- - */ -void -Targ_SetMain (gn) - GNode *gn; /* The main target we'll create */ -{ - mainTarg = gn; -} - -static int -TargPrintName (gnp, ppath) - ClientData gnp; - ClientData ppath; -{ - GNode *gn = (GNode *) gnp; - printf ("%s ", gn->name); -#ifdef notdef - if (ppath) { - if (gn->path) { - printf ("[%s] ", gn->path); - } - if (gn == mainTarg) { - printf ("(MAIN NAME) "); - } - } -#endif /* notdef */ - return (ppath ? 0 : 0); -} - - -int -Targ_PrintCmd (cmd, dummy) - ClientData cmd; - ClientData dummy; -{ - printf ("\t%s\n", (char *) cmd); - return (dummy ? 0 : 0); -} - -/*- - *----------------------------------------------------------------------- - * Targ_FmtTime -- - * Format a modification time in some reasonable way and return it. - * - * Results: - * The time reformatted. - * - * Side Effects: - * The time is placed in a static area, so it is overwritten - * with each call. - * - *----------------------------------------------------------------------- - */ -char * -Targ_FmtTime (time) - time_t time; -{ - struct tm *parts; - static char buf[40]; - static char *months[] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" - }; - - parts = localtime(&time); - - sprintf (buf, "%d:%02d:%02d %s %d, 19%d", - parts->tm_hour, parts->tm_min, parts->tm_sec, - months[parts->tm_mon], parts->tm_mday, parts->tm_year); - return(buf); -} - -/*- - *----------------------------------------------------------------------- - * Targ_PrintType -- - * Print out a type field giving only those attributes the user can - * set. - * - * Results: - * - * Side Effects: - * - *----------------------------------------------------------------------- - */ -void -Targ_PrintType (type) - register int type; -{ - register int tbit; - -#ifdef __STDC__ -#define PRINTBIT(attr) case CONCAT(OP_,attr): printf("." #attr " "); break -#define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG)) printf("." #attr " "); break -#else -#define PRINTBIT(attr) case CONCAT(OP_,attr): printf(".attr "); break -#define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG)) printf(".attr "); break -#endif /* __STDC__ */ - - type &= ~OP_OPMASK; - - while (type) { - tbit = 1 << (ffs(type) - 1); - type &= ~tbit; - - switch(tbit) { - PRINTBIT(OPTIONAL); - PRINTBIT(USE); - PRINTBIT(EXEC); - PRINTBIT(IGNORE); - PRINTBIT(PRECIOUS); - PRINTBIT(SILENT); - PRINTBIT(MAKE); - PRINTBIT(JOIN); - PRINTBIT(INVISIBLE); - PRINTBIT(NOTMAIN); - PRINTDBIT(LIB); - /*XXX: MEMBER is defined, so CONCAT(OP_,MEMBER) gives OP_"%" */ - case OP_MEMBER: if (DEBUG(TARG)) printf(".MEMBER "); break; - PRINTDBIT(ARCHV); - } - } -} - -/*- - *----------------------------------------------------------------------- - * TargPrintNode -- - * print the contents of a node - *----------------------------------------------------------------------- - */ -static int -TargPrintNode (gnp, passp) - ClientData gnp; - ClientData passp; -{ - GNode *gn = (GNode *) gnp; - int pass = *(int *) passp; - if (!OP_NOP(gn->type)) { - printf("#\n"); - if (gn == mainTarg) { - printf("# *** MAIN TARGET ***\n"); - } - if (pass == 2) { - if (gn->unmade) { - printf("# %d unmade children\n", gn->unmade); - } else { - printf("# No unmade children\n"); - } - if (! (gn->type & (OP_JOIN|OP_USE|OP_EXEC))) { - if (gn->mtime != 0) { - printf("# last modified %s: %s\n", - Targ_FmtTime(gn->mtime), - (gn->made == UNMADE ? "unmade" : - (gn->made == MADE ? "made" : - (gn->made == UPTODATE ? "up-to-date" : - "error when made")))); - } else if (gn->made != UNMADE) { - printf("# non-existent (maybe): %s\n", - (gn->made == MADE ? "made" : - (gn->made == UPTODATE ? "up-to-date" : - (gn->made == ERROR ? "error when made" : - "aborted")))); - } else { - printf("# unmade\n"); - } - } - if (!Lst_IsEmpty (gn->iParents)) { - printf("# implicit parents: "); - Lst_ForEach (gn->iParents, TargPrintName, (ClientData)0); - fputc ('\n', stdout); - } - } - if (!Lst_IsEmpty (gn->parents)) { - printf("# parents: "); - Lst_ForEach (gn->parents, TargPrintName, (ClientData)0); - fputc ('\n', stdout); - } - - printf("%-16s", gn->name); - switch (gn->type & OP_OPMASK) { - case OP_DEPENDS: - printf(": "); break; - case OP_FORCE: - printf("! "); break; - case OP_DOUBLEDEP: - printf(":: "); break; - } - Targ_PrintType (gn->type); - Lst_ForEach (gn->children, TargPrintName, (ClientData)0); - fputc ('\n', stdout); - Lst_ForEach (gn->commands, Targ_PrintCmd, (ClientData)0); - printf("\n\n"); - if (gn->type & OP_DOUBLEDEP) { - Lst_ForEach (gn->cohorts, TargPrintNode, (ClientData)&pass); - } - } - return (0); -} - -/*- - *----------------------------------------------------------------------- - * TargPrintOnlySrc -- - * Print only those targets that are just a source. - * - * Results: - * 0. - * - * Side Effects: - * The name of each file is printed preceeded by #\t - * - *----------------------------------------------------------------------- - */ -static int -TargPrintOnlySrc(gnp, dummy) - ClientData gnp; - ClientData dummy; -{ - GNode *gn = (GNode *) gnp; - if (OP_NOP(gn->type)) - printf("#\t%s [%s]\n", gn->name, gn->path ? gn->path : gn->name); - - return (dummy ? 0 : 0); -} - -/*- - *----------------------------------------------------------------------- - * Targ_PrintGraph -- - * print the entire graph. heh heh - * - * Results: - * none - * - * Side Effects: - * lots o' output - *----------------------------------------------------------------------- - */ -void -Targ_PrintGraph (pass) - int pass; /* Which pass this is. 1 => no processing - * 2 => processing done */ -{ - printf("#*** Input graph:\n"); - Lst_ForEach (allTargets, TargPrintNode, (ClientData)&pass); - printf("\n\n"); - printf("#\n# Files that are only sources:\n"); - Lst_ForEach (allTargets, TargPrintOnlySrc, (ClientData) 0); - printf("#*** Global Variables:\n"); - Var_Dump (VAR_GLOBAL); - printf("#*** Command-line Variables:\n"); - Var_Dump (VAR_CMD); - printf("\n"); - Dir_PrintDirectories(); - printf("\n"); - Suff_PrintAll(); -} diff --git a/usr.bin/make/util.c b/usr.bin/make/util.c deleted file mode 100644 index 07eb6a47e55d..000000000000 --- a/usr.bin/make/util.c +++ /dev/null @@ -1,351 +0,0 @@ -/* $NetBSD: util.c,v 1.7 1996/08/30 17:59:44 thorpej Exp $ */ - -/* - * Missing stuff from OS's - */ - -#ifndef lint -static char rcsid[] = "$NetBSD: util.c,v 1.7 1996/08/30 17:59:44 thorpej Exp $"; -#endif - -#include -#include "make.h" - -#if !__STDC__ -# ifndef const -# define const -# endif -#endif - -#ifdef sun - - - -extern int errno, sys_nerr; -extern char *sys_errlist[]; - -char * -strerror(e) - int e; -{ - static char buf[100]; - if (e < 0 || e >= sys_nerr) { - sprintf(buf, "Unknown error %d", e); - return buf; - } - else - return sys_errlist[e]; -} -#endif - -#ifdef ultrix -#include - -/* strdup - * - * Make a duplicate of a string. - * For systems which lack this function. - */ -char * -strdup(str) - const char *str; -{ - size_t len; - - if (str == NULL) - return NULL; - len = strlen(str) + 1; - if ((p = malloc(len)) == NULL) - return NULL; - - return memcpy(p, str, len); -} - -#endif - -#if defined(sun) || defined(__hpux) || defined(__sgi) - -int -setenv(name, value, dum) - const char *name; - const char *value; - int dum; -{ - register char *p; - int len = strlen(name) + strlen(value) + 2; /* = \0 */ - char *ptr = (char*) malloc(len); - - (void) dum; - - if (ptr == NULL) - return -1; - - p = ptr; - - while (*name) - *p++ = *name++; - - *p++ = '='; - - while (*value) - *p++ = *value++; - - *p = '\0'; - - len = putenv(ptr); -/* free(ptr); */ - return len; -} -#endif - -#ifdef __hpux -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -int -killpg(pid, sig) - int pid, sig; -{ - return kill(-pid, sig); -} - -void -srandom(seed) - long seed; -{ - srand48(seed); -} - -long -random() -{ - return lrand48(); -} - -/* turn into bsd signals */ -void (* -signal(s, a)) () - int s; - void (*a)(); -{ - struct sigvec osv, sv; - - (void) sigvector(s, (struct sigvec *) 0, &osv); - sv = osv; - sv.sv_handler = a; -#ifdef SV_BSDSIG - sv.sv_flags = SV_BSDSIG; -#endif - - if (sigvector(s, &sv, (struct sigvec *) 0) == -1) - return (BADSIG); - return (osv.sv_handler); -} - -#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]))) - - -/* strrcpy(): - * Like strcpy, going backwards and returning the new pointer - */ -static char * -strrcpy(ptr, str) - register char *ptr, *str; -{ - register int len = strlen(str); - - while (len) - *--ptr = str[--len]; - - return (ptr); -} /* end strrcpy */ - - -char * -getwd(pathname) - 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 */ - - -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 */ -}; - -int -utimes(file, tvp) - 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 /* __hpux */ - -#if defined(sun) && defined(__svr4__) -#include - -/* turn into bsd signals */ -void (* -signal(s, a)) () - int s; - void (*a)(); -{ - struct sigaction sa, osa; - - sa.sa_handler = a; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_RESTART; - - if (sigaction(s, &sa, &osa) == -1) - return SIG_ERR; - else - return osa.sa_handler; -} - -#endif diff --git a/usr.bin/make/var.c b/usr.bin/make/var.c deleted file mode 100644 index 4d7b2e956878..000000000000 --- a/usr.bin/make/var.c +++ /dev/null @@ -1,2033 +0,0 @@ -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char sccsid[] = "@(#)var.c 8.4 (Berkeley) 4/28/95"; -#endif /* not lint */ - -/*- - * 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 -#include "make.h" -#include "buf.h" - -/* - * This is a harmless return value for Var_Parse that can be used by Var_Subst - * to determine if there was an error in parsing -- easier than returning - * a flag, as things outside this module don't give a hoot. - */ -char var_Error[] = ""; - -/* - * Similar to var_Error, but returned when the 'err' flag for Var_Parse is - * set false. Why not just use a constant? Well, gcc likes to condense - * identical string instances... - */ -static char varNoError[] = ""; - -/* - * Internally, variables are contained in four different contexts. - * 1) the environment. They may not be changed. If an environment - * variable is appended-to, the result is placed in the global - * context. - * 2) the global context. Variables set in the Makefile are located in - * the global context. It is the penultimate context searched when - * substituting. - * 3) the command-line context. All variables set on the command line - * are placed in this context. They are UNALTERABLE once placed here. - * 4) the local context. Each target has associated with it a context - * list. On this list are located the structures describing such - * local variables as $(@) and $(*) - * The four contexts are searched in the reverse order from which they are - * listed. - */ -GNode *VAR_GLOBAL; /* variables from the makefile */ -GNode *VAR_CMD; /* variables defined on the command-line */ - -static Lst allVars; /* List of all variables */ - -#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 */ -} Var; - -typedef struct { - char *lhs; /* String to match */ - int leftLen; /* Length of string */ - char *rhs; /* Replacement string (w/ &'s removed) */ - int rightLen; /* Length of replacement */ - int flags; -#define VAR_SUB_GLOBAL 1 /* Apply substitution globally */ -#define VAR_MATCH_START 2 /* Match at start of word */ -#define VAR_MATCH_END 4 /* Match at end of word */ -#define VAR_NO_SUB 8 /* Substitution is non-global and already done */ -} VarPattern; - -static int VarCmp __P((ClientData, ClientData)); -static Var *VarFind __P((char *, GNode *, int)); -static void VarAdd __P((char *, char *, GNode *)); -static void VarDelete __P((ClientData)); -static Boolean VarHead __P((char *, Boolean, Buffer, ClientData)); -static Boolean VarTail __P((char *, Boolean, Buffer, ClientData)); -static Boolean VarSuffix __P((char *, Boolean, Buffer, ClientData)); -static Boolean VarRoot __P((char *, Boolean, Buffer, ClientData)); -static Boolean VarMatch __P((char *, Boolean, Buffer, ClientData)); -static Boolean VarSYSVMatch __P((char *, Boolean, Buffer, ClientData)); -static Boolean VarNoMatch __P((char *, Boolean, Buffer, ClientData)); -static Boolean VarSubstitute __P((char *, Boolean, Buffer, ClientData)); -static char *VarModify __P((char *, Boolean (*)(char *, Boolean, Buffer, - ClientData), - ClientData)); -static int VarPrintVar __P((ClientData, ClientData)); - -/*- - *----------------------------------------------------------------------- - * VarCmp -- - * See if the given variable matches the named one. Called from - * Lst_Find when searching for a variable of a given name. - * - * Results: - * 0 if they match. non-zero otherwise. - * - * Side Effects: - * none - *----------------------------------------------------------------------- - */ -static int -VarCmp (v, name) - ClientData v; /* VAR structure to compare */ - ClientData name; /* name to look for */ -{ - return (strcmp ((char *) name, ((Var *) v)->name)); -} - -/*- - *----------------------------------------------------------------------- - * VarFind -- - * Find the given variable in the given context and any other contexts - * indicated. - * - * Results: - * A pointer to the structure describing the desired variable or - * NIL if the variable does not exist. - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ -static Var * -VarFind (name, ctxt, flags) - char *name; /* name to find */ - GNode *ctxt; /* context in which to find it */ - int 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 */ -{ - LstNode var; - Var *v; - - /* - * If the variable name begins with a '.', it could very well be one of - * the local ones. We check the name against all the local variables - * and substitute the short version in for 'name' if it matches one of - * them. - */ - if (*name == '.' && isupper((unsigned char) name[1])) - switch (name[1]) { - case 'A': - if (!strcmp(name, ".ALLSRC")) - name = ALLSRC; - if (!strcmp(name, ".ARCHIVE")) - name = ARCHIVE; - break; - case 'I': - if (!strcmp(name, ".IMPSRC")) - name = IMPSRC; - break; - case 'M': - if (!strcmp(name, ".MEMBER")) - name = MEMBER; - break; - case 'O': - if (!strcmp(name, ".OODATE")) - name = OODATE; - break; - case 'P': - if (!strcmp(name, ".PREFIX")) - name = PREFIX; - break; - case 'T': - if (!strcmp(name, ".TARGET")) - name = TARGET; - break; - } - /* - * First look for the variable in the given context. If it's not there, - * look for it in VAR_CMD, VAR_GLOBAL and the environment, in that order, - * depending on the FIND_* flags in 'flags' - */ - var = Lst_Find (ctxt->context, (ClientData)name, VarCmp); - - if ((var == NILLNODE) && (flags & FIND_CMD) && (ctxt != VAR_CMD)) { - var = Lst_Find (VAR_CMD->context, (ClientData)name, VarCmp); - } - if (!checkEnvFirst && (var == NILLNODE) && (flags & FIND_GLOBAL) && - (ctxt != VAR_GLOBAL)) - { - var = Lst_Find (VAR_GLOBAL->context, (ClientData)name, VarCmp); - } - if ((var == NILLNODE) && (flags & FIND_ENV)) { - char *env; - - if ((env = getenv (name)) != NULL) { - int len; - - v = (Var *) emalloc(sizeof(Var)); - v->name = strdup(name); - - len = strlen(env); - - v->val = Buf_Init(len); - Buf_AddBytes(v->val, len, (Byte *)env); - - v->flags = VAR_FROM_ENV; - return (v); - } else if (checkEnvFirst && (flags & FIND_GLOBAL) && - (ctxt != VAR_GLOBAL)) - { - var = Lst_Find (VAR_GLOBAL->context, (ClientData)name, VarCmp); - if (var == NILLNODE) { - return ((Var *) NIL); - } else { - return ((Var *)Lst_Datum(var)); - } - } else { - return((Var *)NIL); - } - } else if (var == NILLNODE) { - return ((Var *) NIL); - } else { - return ((Var *) Lst_Datum (var)); - } -} - -/*- - *----------------------------------------------------------------------- - * VarAdd -- - * Add a new variable of name name and value val to the given context - * - * 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 (name, val, ctxt) - char *name; /* name of variable to add */ - char *val; /* value to set it to */ - GNode *ctxt; /* context in which to set it */ -{ - register Var *v; - int len; - - v = (Var *) emalloc (sizeof (Var)); - - v->name = strdup (name); - - len = val ? strlen(val) : 0; - v->val = Buf_Init(len+1); - Buf_AddBytes(v->val, len, (Byte *)val); - - v->flags = 0; - - (void) Lst_AtFront (ctxt->context, (ClientData)v); - (void) Lst_AtEnd (allVars, (ClientData) v); - if (DEBUG(VAR)) { - printf("%s:%s = %s\n", ctxt->name, name, val); - } -} - - -/*- - *----------------------------------------------------------------------- - * VarDelete -- - * Delete a variable and all the space associated with it. - * - * Results: - * None - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ -static void -VarDelete(vp) - ClientData vp; -{ - Var *v = (Var *) vp; - free(v->name); - Buf_Destroy(v->val, TRUE); - free((Address) v); -} - - - -/*- - *----------------------------------------------------------------------- - * Var_Delete -- - * Remove a variable from a context. - * - * Results: - * None. - * - * Side Effects: - * The Var structure is removed and freed. - * - *----------------------------------------------------------------------- - */ -void -Var_Delete(name, ctxt) - char *name; - GNode *ctxt; -{ - LstNode ln; - - if (DEBUG(VAR)) { - printf("%s:delete %s\n", ctxt->name, name); - } - ln = Lst_Find(ctxt->context, (ClientData)name, VarCmp); - if (ln != NILLNODE) { - register Var *v; - - v = (Var *)Lst_Datum(ln); - Lst_Remove(ctxt->context, ln); - ln = Lst_Member(allVars, v); - Lst_Remove(allVars, ln); - VarDelete((ClientData) v); - } -} - -/*- - *----------------------------------------------------------------------- - * Var_Set -- - * Set the variable name to the value val in the given context. - * - * Results: - * None. - * - * Side Effects: - * If the variable doesn't yet exist, a new record is created for it. - * Else the old value is freed and the new one stuck in its place - * - * Notes: - * The variable is searched for only in its context before being - * created in that context. I.e. if the context is VAR_GLOBAL, - * only VAR_GLOBAL->context is searched. Likewise if it is VAR_CMD, only - * VAR_CMD->context is searched. This is done to avoid the literally - * thousands of unnecessary strcmp's that used to be done to - * set, say, $(@) or $(<). - *----------------------------------------------------------------------- - */ -void -Var_Set (name, val, ctxt) - char *name; /* name of variable to set */ - char *val; /* value to give to the variable */ - GNode *ctxt; /* context in which to set it */ -{ - register Var *v; - - /* - * 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... - */ - v = VarFind (name, ctxt, 0); - if (v == (Var *) NIL) { - VarAdd (name, val, ctxt); - } else { - Buf_Discard(v->val, Buf_Size(v->val)); - Buf_AddBytes(v->val, strlen(val), (Byte *)val); - - if (DEBUG(VAR)) { - printf("%s:%s = %s\n", ctxt->name, name, val); - } - } - /* - * Any variables given on the command line are automatically exported - * to the environment (as per POSIX standard) - */ - if (ctxt == VAR_CMD) { - setenv(name, val, 1); - } -} - -/*- - *----------------------------------------------------------------------- - * Var_Append -- - * The variable of the given name has the given value appended to it in - * the given context. - * - * 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 (name, val, ctxt) - char *name; /* Name of variable to modify */ - char *val; /* String to append to it */ - GNode *ctxt; /* Context in which this should occur */ -{ - register Var *v; - - v = VarFind (name, ctxt, (ctxt == VAR_GLOBAL) ? FIND_ENV : 0); - - if (v == (Var *) NIL) { - VarAdd (name, val, ctxt); - } else { - Buf_AddByte(v->val, (Byte)' '); - Buf_AddBytes(v->val, strlen(val), (Byte *)val); - - if (DEBUG(VAR)) { - printf("%s:%s = %s\n", ctxt->name, name, - (char *) Buf_GetAll(v->val, (int *)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; - Lst_AtFront(ctxt->context, (ClientData)v); - } - } -} - -/*- - *----------------------------------------------------------------------- - * Var_Exists -- - * See if the given variable exists. - * - * Results: - * TRUE if it does, FALSE if it doesn't - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -Boolean -Var_Exists(name, ctxt) - char *name; /* Variable to find */ - GNode *ctxt; /* Context in which to start search */ -{ - Var *v; - - v = VarFind(name, ctxt, FIND_CMD|FIND_GLOBAL|FIND_ENV); - - if (v == (Var *)NIL) { - return(FALSE); - } else if (v->flags & VAR_FROM_ENV) { - free(v->name); - Buf_Destroy(v->val, TRUE); - free((char *)v); - } - return(TRUE); -} - -/*- - *----------------------------------------------------------------------- - * Var_Value -- - * Return the value of the named variable in the given context - * - * Results: - * The value if the variable exists, NULL if it doesn't - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ -char * -Var_Value (name, ctxt, frp) - char *name; /* name to find */ - GNode *ctxt; /* context in which to search for it */ - char **frp; -{ - Var *v; - - v = VarFind (name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); - *frp = NULL; - if (v != (Var *) NIL) { - char *p = ((char *)Buf_GetAll(v->val, (int *)NULL)); - if (v->flags & VAR_FROM_ENV) { - Buf_Destroy(v->val, FALSE); - free((Address) v); - *frp = p; - } - return p; - } else { - return ((char *) NULL); - } -} - -/*- - *----------------------------------------------------------------------- - * VarHead -- - * Remove the tail of the given word and place the result in the given - * buffer. - * - * 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 (word, addSpace, buf, dummy) - char *word; /* Word to trim */ - Boolean addSpace; /* True if need to add a space to the buffer - * before sticking in the head */ - Buffer buf; /* Buffer in which to store it */ - ClientData dummy; -{ - register char *slash; - - slash = strrchr (word, '/'); - if (slash != (char *)NULL) { - if (addSpace) { - Buf_AddByte (buf, (Byte)' '); - } - *slash = '\0'; - Buf_AddBytes (buf, strlen (word), (Byte *)word); - *slash = '/'; - return (TRUE); - } else { - /* - * If no directory part, give . (q.v. the POSIX standard) - */ - if (addSpace) { - Buf_AddBytes(buf, 2, (Byte *)" ."); - } else { - Buf_AddByte(buf, (Byte)'.'); - } - } - return(dummy ? TRUE : TRUE); -} - -/*- - *----------------------------------------------------------------------- - * VarTail -- - * Remove the head of the given word and place the result in the given - * buffer. - * - * 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 (word, addSpace, buf, dummy) - char *word; /* Word to trim */ - Boolean addSpace; /* TRUE if need to stick a space in the - * buffer before adding the tail */ - Buffer buf; /* Buffer in which to store it */ - ClientData dummy; -{ - register char *slash; - - if (addSpace) { - Buf_AddByte (buf, (Byte)' '); - } - - slash = strrchr (word, '/'); - if (slash != (char *)NULL) { - *slash++ = '\0'; - Buf_AddBytes (buf, strlen(slash), (Byte *)slash); - slash[-1] = '/'; - } else { - Buf_AddBytes (buf, strlen(word), (Byte *)word); - } - return (dummy ? TRUE : TRUE); -} - -/*- - *----------------------------------------------------------------------- - * VarSuffix -- - * Place the suffix of the given word in the given buffer. - * - * 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 (word, addSpace, buf, dummy) - char *word; /* Word to trim */ - Boolean addSpace; /* TRUE if need to add a space before placing - * the suffix in the buffer */ - Buffer buf; /* Buffer in which to store it */ - ClientData dummy; -{ - register char *dot; - - dot = strrchr (word, '.'); - if (dot != (char *)NULL) { - if (addSpace) { - Buf_AddByte (buf, (Byte)' '); - } - *dot++ = '\0'; - Buf_AddBytes (buf, strlen (dot), (Byte *)dot); - dot[-1] = '.'; - addSpace = TRUE; - } - return (dummy ? addSpace : addSpace); -} - -/*- - *----------------------------------------------------------------------- - * VarRoot -- - * Remove the suffix of the given word and place the result in the - * buffer. - * - * 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 (word, addSpace, buf, dummy) - char *word; /* Word to trim */ - Boolean addSpace; /* TRUE if need to add a space to the buffer - * before placing the root in it */ - Buffer buf; /* Buffer in which to store it */ - ClientData dummy; -{ - register char *dot; - - if (addSpace) { - Buf_AddByte (buf, (Byte)' '); - } - - dot = strrchr (word, '.'); - if (dot != (char *)NULL) { - *dot = '\0'; - Buf_AddBytes (buf, strlen (word), (Byte *)word); - *dot = '.'; - } else { - Buf_AddBytes (buf, strlen(word), (Byte *)word); - } - return (dummy ? TRUE : TRUE); -} - -/*- - *----------------------------------------------------------------------- - * VarMatch -- - * Place the word in the buffer if it matches the given pattern. - * Callback function for VarModify to implement the :M modifier. - * - * 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 (word, addSpace, buf, pattern) - char *word; /* Word to examine */ - Boolean addSpace; /* TRUE if need to add a space to the - * buffer before adding the word, if it - * matches */ - Buffer buf; /* Buffer in which to store it */ - ClientData pattern; /* Pattern the word must match */ -{ - if (Str_Match(word, (char *) pattern)) { - if (addSpace) { - Buf_AddByte(buf, (Byte)' '); - } - addSpace = TRUE; - Buf_AddBytes(buf, strlen(word), (Byte *)word); - } - return(addSpace); -} - - - -/*- - *----------------------------------------------------------------------- - * VarSYSVMatch -- - * Place the word in the buffer if it matches the given pattern. - * Callback function for VarModify to implement the System V % - * modifiers. - * - * 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 (word, addSpace, buf, patp) - char *word; /* Word to examine */ - Boolean addSpace; /* TRUE if need to add a space to the - * buffer before adding the word, if it - * matches */ - Buffer buf; /* Buffer in which to store it */ - ClientData patp; /* Pattern the word must match */ -{ - int len; - char *ptr; - VarPattern *pat = (VarPattern *) patp; - - if (addSpace) - Buf_AddByte(buf, (Byte)' '); - - addSpace = TRUE; - - if ((ptr = Str_SYSVMatch(word, pat->lhs, &len)) != NULL) - Str_SYSVSubst(buf, pat->rhs, ptr, len); - else - Buf_AddBytes(buf, strlen(word), (Byte *) word); - - return(addSpace); -} - - -/*- - *----------------------------------------------------------------------- - * VarNoMatch -- - * Place the word in the buffer if it doesn't match the given pattern. - * Callback function for VarModify to implement the :N modifier. - * - * 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 (word, addSpace, buf, pattern) - char *word; /* Word to examine */ - Boolean addSpace; /* TRUE if need to add a space to the - * buffer before adding the word, if it - * matches */ - Buffer buf; /* Buffer in which to store it */ - ClientData pattern; /* Pattern the word must match */ -{ - if (!Str_Match(word, (char *) pattern)) { - if (addSpace) { - Buf_AddByte(buf, (Byte)' '); - } - addSpace = TRUE; - Buf_AddBytes(buf, strlen(word), (Byte *)word); - } - return(addSpace); -} - - -/*- - *----------------------------------------------------------------------- - * VarSubstitute -- - * Perform a string-substitution on the given word, placing the - * result in the passed buffer. - * - * Results: - * TRUE if a space is needed before more characters are added. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -static Boolean -VarSubstitute (word, addSpace, buf, patternp) - char *word; /* Word to modify */ - Boolean addSpace; /* True if space should be added before - * other characters */ - Buffer buf; /* Buffer for result */ - ClientData patternp; /* Pattern for substitution */ -{ - register int wordLen; /* Length of word */ - register char *cp; /* General pointer */ - VarPattern *pattern = (VarPattern *) patternp; - - wordLen = strlen(word); - if ((pattern->flags & VAR_NO_SUB) == 0) { - /* - * 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) { - Buf_AddByte(buf, (Byte)' '); - } - addSpace = TRUE; - Buf_AddBytes(buf, pattern->rightLen, - (Byte *)pattern->rhs); - } - } 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) { - Buf_AddByte(buf, (Byte)' '); - } - addSpace = TRUE; - } - Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs); - Buf_AddBytes(buf, wordLen - pattern->leftLen, - (Byte *)(word + pattern->leftLen)); - } - } 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) { - Buf_AddByte(buf, (Byte)' '); - } - addSpace = TRUE; - } - Buf_AddBytes(buf, cp - word, (Byte *)word); - Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs); - } 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 - * subsititutions 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. - */ - register Boolean done; - int origSize; - - done = FALSE; - origSize = Buf_Size(buf); - while (!done) { - cp = Str_FindSubstring(word, pattern->lhs); - if (cp != (char *)NULL) { - if (addSpace && (((cp - word) + pattern->rightLen) != 0)){ - Buf_AddByte(buf, (Byte)' '); - addSpace = FALSE; - } - Buf_AddBytes(buf, cp-word, (Byte *)word); - Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs); - wordLen -= (cp - word) + pattern->leftLen; - word = cp + pattern->leftLen; - if (wordLen == 0) { - done = TRUE; - } - if ((pattern->flags & VAR_SUB_GLOBAL) == 0) { - done = TRUE; - pattern->flags |= VAR_NO_SUB; - } - } else { - done = TRUE; - } - } - if (wordLen != 0) { - if (addSpace) { - Buf_AddByte(buf, (Byte)' '); - } - Buf_AddBytes(buf, wordLen, (Byte *)word); - } - /* - * If added characters to the buffer, need to add a space - * before we add any more. If we didn't add any, just return - * the previous value of addSpace. - */ - return ((Buf_Size(buf) != origSize) || addSpace); - } - /* - * Common code for anchored substitutions: if performed a substitution - * and it's not supposed to be global, mark the pattern as requiring - * no more substitutions. addSpace was set TRUE if characters were - * added to the buffer. - */ - if ((pattern->flags & VAR_SUB_GLOBAL) == 0) { - pattern->flags |= VAR_NO_SUB; - } - return (addSpace); - } - nosub: - if (addSpace) { - Buf_AddByte(buf, (Byte)' '); - } - Buf_AddBytes(buf, wordLen, (Byte *)word); - return(TRUE); -} - -/*- - *----------------------------------------------------------------------- - * VarModify -- - * Modify each of the words of the passed string using the given - * function. Used to implement all modifiers. - * - * Results: - * A string of all the words modified appropriately. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -static char * -VarModify (str, modProc, datum) - char *str; /* String whose words should be trimmed */ - /* Function to use to modify them */ - Boolean (*modProc) __P((char *, Boolean, Buffer, ClientData)); - ClientData datum; /* Datum to pass it */ -{ - 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 [first word does not count] */ - int ac, i; - - buf = Buf_Init (0); - addSpace = FALSE; - - av = brk_string(str, &ac, FALSE); - - for (i = 1; i < ac; i++) - addSpace = (*modProc)(av[i], addSpace, buf, datum); - - Buf_AddByte (buf, '\0'); - str = (char *)Buf_GetAll (buf, (int *)NULL); - Buf_Destroy (buf, FALSE); - return (str); -} - -/*- - *----------------------------------------------------------------------- - * Var_Parse -- - * Given the start of a variable invocation, extract the variable - * name and find its value, then modify it according to the - * specification. - * - * Results: - * The (possibly-modified) value of the variable or var_Error if the - * specification is invalid. The length of the specification is - * placed in *lengthPtr (for invalid specifications, this is just - * 2...?). - * A Boolean in *freePtr telling whether the returned string should - * be freed by the caller. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -char * -Var_Parse (str, ctxt, err, lengthPtr, freePtr) - char *str; /* The string to parse */ - GNode *ctxt; /* The context for the variable */ - Boolean err; /* TRUE if undefined variables are an error */ - int *lengthPtr; /* OUT: The length of the specification */ - Boolean *freePtr; /* OUT: TRUE if caller should free result */ -{ - register char *tstr; /* Pointer into str */ - Var *v; /* Variable in invocation */ - register char *cp; /* Secondary pointer into str (place marker - * for tstr) */ - Boolean haveModifier;/* TRUE if have modifiers for the variable */ - register char endc; /* Ending character when variable in parens - * or braces */ - register char startc; /* Starting character when variable in parens - * or braces */ - int cnt; /* Used to count brace pairs when variable in - * in parens or braces */ - char *start; - 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 */ - - *freePtr = FALSE; - dynamic = FALSE; - start = str; - - if (str[1] != '(' && str[1] != '{') { - /* - * If it's not bounded by braces of some sort, life is much simpler. - * We just need to check for the first character and return the - * value if it exists. - */ - char name[2]; - - name[0] = str[1]; - name[1] = '\0'; - - v = VarFind (name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); - if (v == (Var *)NIL) { - *lengthPtr = 2; - - if ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)) { - /* - * If substituting a local variable in a non-local context, - * assume it's for dynamic source stuff. We have to handle - * this specially and return the longhand for the variable - * with the dollar sign escaped so it makes it back to the - * caller. Only four of the local variables are treated - * specially as they are the only four that will be set - * when dynamic sources are expanded. - */ - switch (str[1]) { - case '@': - return("$(.TARGET)"); - case '%': - return("$(.ARCHIVE)"); - case '*': - return("$(.PREFIX)"); - case '!': - return("$(.MEMBER)"); - } - } - /* - * Error - */ - return (err ? var_Error : varNoError); - } else { - haveModifier = FALSE; - tstr = &str[1]; - endc = str[1]; - } - } else { - startc = str[1]; - endc = startc == '(' ? ')' : '}'; - - /* - * Skip to the end character or a colon, whichever comes first. - */ - for (tstr = str + 2; - *tstr != '\0' && *tstr != endc && *tstr != ':'; - tstr++) - { - continue; - } - if (*tstr == ':') { - haveModifier = TRUE; - } else if (*tstr != '\0') { - haveModifier = FALSE; - } else { - /* - * If we never did find the end character, return NULL - * right now, setting the length to be the distance to - * the end of the string, since that's what make does. - */ - *lengthPtr = tstr - str; - return (var_Error); - } - *tstr = '\0'; - - v = VarFind (str + 2, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); - if ((v == (Var *)NIL) && (ctxt != VAR_CMD) && (ctxt != VAR_GLOBAL) && - ((tstr-str) == 4) && (str[3] == 'F' || str[3] == 'D')) - { - /* - * Check for bogus D and F forms of local variables since we're - * in a local context and the name is the right length. - */ - switch(str[2]) { - case '@': - case '%': - case '*': - case '!': - case '>': - case '<': - { - char vname[2]; - char *val; - - /* - * Well, it's local -- go look for it. - */ - vname[0] = str[2]; - vname[1] = '\0'; - v = VarFind(vname, ctxt, 0); - - if (v != (Var *)NIL) { - /* - * No need for nested expansion or anything, as we're - * the only one who sets these things and we sure don't - * but nested invocations in them... - */ - val = (char *)Buf_GetAll(v->val, (int *)NULL); - - if (str[3] == 'D') { - val = VarModify(val, VarHead, (ClientData)0); - } else { - val = VarModify(val, VarTail, (ClientData)0); - } - /* - * Resulting string is dynamically allocated, so - * tell caller to free it. - */ - *freePtr = TRUE; - *lengthPtr = tstr-start+1; - *tstr = endc; - return(val); - } - break; - } - } - } - - if (v == (Var *)NIL) { - if ((((tstr-str) == 3) || - ((((tstr-str) == 4) && (str[3] == 'F' || - str[3] == '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[2]) { - case '@': - case '%': - case '*': - case '!': - dynamic = TRUE; - break; - } - } else if (((tstr-str) > 4) && (str[2] == '.') && - isupper((unsigned char) str[3]) && - ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL))) - { - int len; - - len = (tstr-str) - 3; - if ((strncmp(str+2, ".TARGET", len) == 0) || - (strncmp(str+2, ".ARCHIVE", len) == 0) || - (strncmp(str+2, ".PREFIX", len) == 0) || - (strncmp(str+2, ".MEMBER", len) == 0)) - { - dynamic = TRUE; - } - } - - if (!haveModifier) { - /* - * No modifiers -- have specification length so we can return - * now. - */ - *lengthPtr = tstr - start + 1; - *tstr = endc; - if (dynamic) { - str = emalloc(*lengthPtr + 1); - strncpy(str, start, *lengthPtr); - str[*lengthPtr] = '\0'; - *freePtr = TRUE; - return(str); - } else { - return (err ? var_Error : varNoError); - } - } else { - /* - * Still need to get to the end of the variable specification, - * so kludge up a Var structure for the modifications - */ - v = (Var *) emalloc(sizeof(Var)); - v->name = &str[1]; - v->val = Buf_Init(1); - v->flags = VAR_JUNK; - } - } - } - - 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. - */ - str = (char *)Buf_GetAll(v->val, (int *)NULL); - if (strchr (str, '$') != (char *)NULL) { - str = Var_Subst(NULL, str, ctxt, err); - *freePtr = TRUE; - } - - v->flags &= ~VAR_IN_USE; - - /* - * Now we need to apply any modifiers the user wants applied. - * These are: - * :M words which match the given . - * is of the standard file - * wildcarding form. - * :S[g] - * Substitute for 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). - * :lhs=rhs Like :S, but the rhs goes to the end of - * the invocation. - */ - if ((str != (char *)NULL) && haveModifier) { - /* - * Skip initial colon while putting it back. - */ - *tstr++ = ':'; - while (*tstr != endc) { - char *newStr; /* New value to return */ - char termc; /* Character which terminated scan */ - - if (DEBUG(VAR)) { - printf("Applying :%c to \"%s\"\n", *tstr, str); - } - switch (*tstr) { - case 'N': - case 'M': - { - char *pattern; - char *cp2; - Boolean copy; - - copy = FALSE; - for (cp = tstr + 1; - *cp != '\0' && *cp != ':' && *cp != endc; - cp++) - { - if (*cp == '\\' && (cp[1] == ':' || cp[1] == endc)){ - copy = TRUE; - cp++; - } - } - termc = *cp; - *cp = '\0'; - if (copy) { - /* - * Need to compress the \:'s out of the pattern, so - * allocate enough room to hold the uncompressed - * pattern (note that cp started at tstr+1, so - * cp - tstr takes the null byte into account) and - * compress the pattern into the space. - */ - pattern = emalloc(cp - tstr); - for (cp2 = pattern, cp = tstr + 1; - *cp != '\0'; - cp++, cp2++) - { - if ((*cp == '\\') && - (cp[1] == ':' || cp[1] == endc)) { - cp++; - } - *cp2 = *cp; - } - *cp2 = '\0'; - } else { - pattern = &tstr[1]; - } - if (*tstr == 'M' || *tstr == 'm') { - newStr = VarModify(str, VarMatch, (ClientData)pattern); - } else { - newStr = VarModify(str, VarNoMatch, - (ClientData)pattern); - } - if (copy) { - free(pattern); - } - break; - } - case 'S': - { - VarPattern pattern; - register char delim; - Buffer buf; /* Buffer for patterns */ - - pattern.flags = 0; - 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; - } - - buf = Buf_Init(0); - - /* - * Pass through the lhs 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. - */ - for (cp = tstr; *cp != '\0' && *cp != delim; cp++) { - if ((*cp == '\\') && - ((cp[1] == delim) || - (cp[1] == '$') || - (cp[1] == '\\'))) - { - Buf_AddByte(buf, (Byte)cp[1]); - cp++; - } else if (*cp == '$') { - if (cp[1] != delim) { - /* - * If unescaped dollar sign not before the - * delimiter, assume it's a variable - * substitution and recurse. - */ - char *cp2; - int len; - Boolean freeIt; - - cp2 = Var_Parse(cp, ctxt, err, &len, &freeIt); - Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2); - if (freeIt) { - free(cp2); - } - cp += len - 1; - } else { - /* - * Unescaped $ at end of pattern => anchor - * pattern at end. - */ - pattern.flags |= VAR_MATCH_END; - } - } else { - Buf_AddByte(buf, (Byte)*cp); - } - } - - Buf_AddByte(buf, (Byte)'\0'); - - /* - * If lhs didn't end with the delimiter, complain and - * return NULL - */ - if (*cp != delim) { - *lengthPtr = cp - start + 1; - if (*freePtr) { - free(str); - } - Buf_Destroy(buf, TRUE); - Error("Unclosed substitution for %s (%c missing)", - v->name, delim); - return (var_Error); - } - - /* - * Fetch pattern and destroy buffer, but preserve the data - * in it, since that's our lhs. Note that Buf_GetAll - * will return the actual number of bytes, which includes - * the null byte, so we have to decrement the length by - * one. - */ - pattern.lhs = (char *)Buf_GetAll(buf, &pattern.leftLen); - pattern.leftLen--; - Buf_Destroy(buf, FALSE); - - /* - * Now comes the replacement string. Three things need to - * be done here: 1) need to compress escaped delimiters and - * ampersands and 2) need to replace unescaped ampersands - * with the l.h.s. (since this isn't regexp, we can do - * it right here) and 3) expand any variable substitutions. - */ - buf = Buf_Init(0); - - tstr = cp + 1; - for (cp = tstr; *cp != '\0' && *cp != delim; cp++) { - if ((*cp == '\\') && - ((cp[1] == delim) || - (cp[1] == '&') || - (cp[1] == '\\') || - (cp[1] == '$'))) - { - Buf_AddByte(buf, (Byte)cp[1]); - cp++; - } else if ((*cp == '$') && (cp[1] != delim)) { - char *cp2; - int len; - Boolean freeIt; - - cp2 = Var_Parse(cp, ctxt, err, &len, &freeIt); - Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2); - cp += len - 1; - if (freeIt) { - free(cp2); - } - } else if (*cp == '&') { - Buf_AddBytes(buf, pattern.leftLen, - (Byte *)pattern.lhs); - } else { - Buf_AddByte(buf, (Byte)*cp); - } - } - - Buf_AddByte(buf, (Byte)'\0'); - - /* - * If didn't end in delimiter character, complain - */ - if (*cp != delim) { - *lengthPtr = cp - start + 1; - if (*freePtr) { - free(str); - } - Buf_Destroy(buf, TRUE); - Error("Unclosed substitution for %s (%c missing)", - v->name, delim); - return (var_Error); - } - - pattern.rhs = (char *)Buf_GetAll(buf, &pattern.rightLen); - pattern.rightLen--; - Buf_Destroy(buf, FALSE); - - /* - * Check for global substitution. If 'g' after the final - * delimiter, substitution is global and is marked that - * way. - */ - cp++; - if (*cp == 'g') { - pattern.flags |= VAR_SUB_GLOBAL; - cp++; - } - - termc = *cp; - newStr = VarModify(str, VarSubstitute, - (ClientData)&pattern); - /* - * Free the two strings. - */ - free(pattern.lhs); - free(pattern.rhs); - break; - } - case 'T': - if (tstr[1] == endc || tstr[1] == ':') { - newStr = VarModify (str, VarTail, (ClientData)0); - cp = tstr + 1; - termc = *cp; - break; - } - /*FALLTHRU*/ - case 'H': - if (tstr[1] == endc || tstr[1] == ':') { - newStr = VarModify (str, VarHead, (ClientData)0); - cp = tstr + 1; - termc = *cp; - break; - } - /*FALLTHRU*/ - case 'E': - if (tstr[1] == endc || tstr[1] == ':') { - newStr = VarModify (str, VarSuffix, (ClientData)0); - cp = tstr + 1; - termc = *cp; - break; - } - /*FALLTHRU*/ - case 'R': - if (tstr[1] == endc || tstr[1] == ':') { - newStr = VarModify (str, VarRoot, (ClientData)0); - cp = tstr + 1; - termc = *cp; - break; - } - /*FALLTHRU*/ - default: { - /* - * This can either be a bogus modifier or a System-V - * substitution command. - */ - VarPattern pattern; - Boolean eqFound; - - pattern.flags = 0; - eqFound = FALSE; - /* - * First we make a pass through the string trying - * to verify it is a SYSV-make-style translation: - * it must be: =) - */ - cp = tstr; - cnt = 1; - while (*cp != '\0' && cnt) { - if (*cp == '=') { - eqFound = TRUE; - /* continue looking for endc */ - } - else if (*cp == endc) - cnt--; - else if (*cp == startc) - cnt++; - if (cnt) - cp++; - } - if (*cp == endc && eqFound) { - - /* - * Now we break this sucker into the lhs and - * rhs. We must null terminate them of course. - */ - for (cp = tstr; *cp != '='; cp++) - continue; - pattern.lhs = tstr; - pattern.leftLen = cp - tstr; - *cp++ = '\0'; - - pattern.rhs = cp; - cnt = 1; - while (cnt) { - if (*cp == endc) - cnt--; - else if (*cp == startc) - cnt++; - if (cnt) - cp++; - } - pattern.rightLen = cp - pattern.rhs; - *cp = '\0'; - - /* - * SYSV modifications happen through the whole - * string. Note the pattern is anchored at the end. - */ - newStr = VarModify(str, VarSYSVMatch, - (ClientData)&pattern); - - /* - * Restore the nulled characters - */ - pattern.lhs[pattern.leftLen] = '='; - pattern.rhs[pattern.rightLen] = endc; - termc = endc; - } else { - Error ("Unknown modifier '%c'\n", *tstr); - for (cp = tstr+1; - *cp != ':' && *cp != endc && *cp != '\0'; - cp++) - continue; - termc = *cp; - newStr = var_Error; - } - } - } - if (DEBUG(VAR)) { - printf("Result is \"%s\"\n", newStr); - } - - if (*freePtr) { - free (str); - } - str = newStr; - if (str != var_Error) { - *freePtr = TRUE; - } else { - *freePtr = FALSE; - } - if (termc == '\0') { - Error("Unclosed variable specification for %s", v->name); - } else if (termc == ':') { - *cp++ = termc; - } else { - *cp = termc; - } - tstr = cp; - } - *lengthPtr = tstr - start + 1; - } else { - *lengthPtr = tstr - start + 1; - *tstr = endc; - } - - if (v->flags & VAR_FROM_ENV) { - Boolean destroy = FALSE; - - if (str != (char *)Buf_GetAll(v->val, (int *)NULL)) { - destroy = TRUE; - } else { - /* - * Returning the value unmodified, so tell the caller to free - * the thing. - */ - *freePtr = TRUE; - } - Buf_Destroy(v->val, destroy); - free((Address)v); - } else if (v->flags & VAR_JUNK) { - /* - * Perform any free'ing needed and set *freePtr to FALSE so the caller - * doesn't try to free a static pointer. - */ - if (*freePtr) { - free(str); - } - *freePtr = FALSE; - Buf_Destroy(v->val, TRUE); - free((Address)v); - if (dynamic) { - str = emalloc(*lengthPtr + 1); - strncpy(str, start, *lengthPtr); - str[*lengthPtr] = '\0'; - *freePtr = TRUE; - } else { - str = var_Error; - } - } - return (str); -} - -/*- - *----------------------------------------------------------------------- - * 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. - * - * Results: - * The resulting string. - * - * Side Effects: - * None. The old string must be freed by the caller - *----------------------------------------------------------------------- - */ -char * -Var_Subst (var, str, ctxt, undefErr) - char *var; /* Named variable || NULL for all */ - char *str; /* the string in which to substitute */ - GNode *ctxt; /* the context wherein to find variables */ - Boolean undefErr; /* TRUE if undefineds are an error */ -{ - Buffer buf; /* Buffer for forming things */ - char *val; /* Value to substitute for a variable */ - int length; /* Length of the variable invocation */ - Boolean doFree; /* Set true if val should be freed */ - static Boolean errorReported; /* Set true if an error has already - * been reported to prevent a plethora - * of messages when recursing */ - - buf = Buf_Init (MAKE_BSIZE); - errorReported = FALSE; - - while (*str) { - if (var == NULL && (*str == '$') && (str[1] == '$')) { - /* - * A dollar sign may be escaped either with another dollar sign. - * In such a case, we skip over the escape character and store the - * dollar sign into the buffer directly. - */ - str++; - Buf_AddByte(buf, (Byte)*str); - str++; - } else if (*str != '$') { - /* - * Skip as many characters as possible -- either to the end of - * the string or to the next dollar sign (variable invocation). - */ - char *cp; - - for (cp = str++; *str != '$' && *str != '\0'; str++) - continue; - Buf_AddBytes(buf, str - cp, (Byte *)cp); - } else { - if (var != NULL) { - int expand; - for (;;) { - if (str[1] != '(' && str[1] != '{') { - if (str[1] != *var) { - Buf_AddBytes(buf, 2, (Byte *) str); - str += 2; - expand = FALSE; - } - else - expand = TRUE; - break; - } - else { - char *p; - - /* - * Scan up to the end of the variable name. - */ - for (p = &str[2]; *p && - *p != ':' && *p != ')' && *p != '}'; 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, (Byte *) str); - str = p; - continue; - } - - if (strncmp(var, str + 2, p - str - 2) != 0 || - var[p - str - 2] != '\0') { - /* - * Not the variable we want to expand, scan - * until the next variable - */ - for (;*p != '$' && *p != '\0'; p++) - continue; - Buf_AddBytes(buf, p - str, (Byte *) str); - str = p; - expand = FALSE; - } - else - expand = TRUE; - break; - } - } - if (!expand) - continue; - } - - val = Var_Parse (str, ctxt, undefErr, &length, &doFree); - - /* - * When we come down here, val should either point to the - * value of this variable, suitably modified, or be NULL. - * Length should be the total length of the potential - * variable invocation (from $ to end character...) - */ - if (val == var_Error || val == varNoError) { - /* - * If performing old-time variable substitution, skip over - * the variable and continue with the substitution. Otherwise, - * store the dollar sign and advance str so we continue with - * the string... - */ - if (oldVars) { - str += length; - } else if (undefErr) { - /* - * If variable is undefined, complain and skip the - * variable. The complaint will stop us from doing anything - * when the file is parsed. - */ - if (!errorReported) { - Parse_Error (PARSE_FATAL, - "Undefined variable \"%.*s\"",length,str); - } - str += length; - errorReported = TRUE; - } else { - Buf_AddByte (buf, (Byte)*str); - str += 1; - } - } else { - /* - * We've now got a variable structure to store in. But first, - * advance the string pointer. - */ - str += length; - - /* - * Copy all the characters from the variable value straight - * into the new string. - */ - Buf_AddBytes (buf, strlen (val), (Byte *)val); - if (doFree) { - free ((Address)val); - } - } - } - } - - Buf_AddByte (buf, '\0'); - str = (char *)Buf_GetAll (buf, (int *)NULL); - Buf_Destroy (buf, FALSE); - return (str); -} - -/*- - *----------------------------------------------------------------------- - * Var_GetTail -- - * Return the tail from each of a list of words. Used to set the - * System V local variables. - * - * Results: - * The resulting string. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -char * -Var_GetTail(file) - char *file; /* Filename to modify */ -{ - return(VarModify(file, VarTail, (ClientData)0)); -} - -/*- - *----------------------------------------------------------------------- - * Var_GetHead -- - * Find the leading components of a (list of) filename(s). - * XXX: VarHead does not replace foo by ., as (sun) System V make - * does. - * - * Results: - * The leading components. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -char * -Var_GetHead(file) - char *file; /* Filename to manipulate */ -{ - return(VarModify(file, VarHead, (ClientData)0)); -} - -/*- - *----------------------------------------------------------------------- - * Var_Init -- - * Initialize the module - * - * Results: - * None - * - * Side Effects: - * The VAR_CMD and VAR_GLOBAL contexts are created - *----------------------------------------------------------------------- - */ -void -Var_Init () -{ - VAR_GLOBAL = Targ_NewGN ("Global"); - VAR_CMD = Targ_NewGN ("Command"); - allVars = Lst_Init(FALSE); - -} - - -void -Var_End () -{ - Lst_Destroy(allVars, VarDelete); -} - - -/****************** PRINT DEBUGGING INFO *****************/ -static int -VarPrintVar (vp, dummy) - ClientData vp; - ClientData dummy; -{ - Var *v = (Var *) vp; - printf ("%-16s = %s\n", v->name, (char *) Buf_GetAll(v->val, (int *)NULL)); - return (dummy ? 0 : 0); -} - -/*- - *----------------------------------------------------------------------- - * Var_Dump -- - * print all variables in a context - *----------------------------------------------------------------------- - */ -void -Var_Dump (ctxt) - GNode *ctxt; -{ - Lst_ForEach (ctxt->context, VarPrintVar, (ClientData) 0); -} diff --git a/util.c b/util.c new file mode 100644 index 000000000000..a63fd33f8f51 --- /dev/null +++ b/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 +#include +#include + +#if !defined(HAVE_STRERROR) +extern int errno, sys_nerr; +extern char *sys_errlist[]; + +char * +strerror(int e) +{ + static char buf[100]; + if (e < 0 || e >= sys_nerr) { + snprintf(buf, sizeof(buf), "Unknown error %d", e); + return buf; + } + else + return sys_errlist[e]; +} +#endif + +#if !defined(HAVE_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 +#include +#include +#include +#include +#include +#include + +int +killpg(int pid, int sig) +{ + return kill(-pid, sig); +} + +#if !defined(__hpux__) && !defined(__hpux) +void +srandom(long seed) +{ + srand48(seed); +} + +long +random(void) +{ + return lrand48(); +} +#endif + +#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 +#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/var.c b/var.c new file mode 100644 index 000000000000..3bf112c895cc --- /dev/null +++ b/var.c @@ -0,0 +1,4194 @@ +/* $NetBSD: var.c,v 1.170 2012/06/04 20:34:20 sjg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: var.c,v 1.170 2012/06/04 20:34:20 sjg Exp $"; +#else +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 3/19/94"; +#else +__RCSID("$NetBSD: var.c,v 1.170 2012/06/04 20:34:20 sjg Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * var.c -- + * Variable-handling functions + * + * Interface: + * Var_Set Set the value of a variable in the given + * context. The variable is created if it doesn't + * yet exist. The value and variable name need not + * be preserved. + * + * Var_Append Append more characters to an existing variable + * in the given context. The variable needn't + * exist already -- it will be created if it doesn't. + * A space is placed between the old value and the + * new one. + * + * Var_Exists See if a variable exists. + * + * Var_Value Return the value of a variable in a context or + * NULL if the variable is undefined. + * + * Var_Subst Substitute named variable, or all variables if + * NULL in a string using + * the given context as the top-most one. If the + * third argument is non-zero, Parse_Error is + * called if any variables are undefined. + * + * Var_Parse Parse a variable expansion from a string and + * return the result and the number of characters + * consumed. + * + * Var_Delete Delete a variable in a context. + * + * Var_Init Initialize this module. + * + * Debugging: + * Var_Dump Print out all variables defined in the given + * context. + * + * XXX: There's a lot of duplication in these functions. + */ + +#include +#ifndef NO_REGEX +#include +#include +#endif +#include +#include +#include +#include +#include + +#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 __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 __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 __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 __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 __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 __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 __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 __unused, Var_Parse_State *vpstate __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 :@@@ modifier of ODE make. + * We set the temp variable named in pattern.lhs to word and expand + * pattern.rhs storing the result in the passed buffer. + * + * Input: + * word Word to modify + * addSpace True if space should be added before + * other characters + * buf Buffer for result + * pattern Datafor substitution + * + * Results: + * TRUE if a space is needed before more characters are added. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static Boolean +VarLoopExpand(GNode *ctx __unused, Var_Parse_State *vpstate __unused, + char *word, Boolean addSpace, Buffer *buf, + 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 __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 __unused, Var_Parse_State *vpstate, + char *word, Boolean addSpace, Buffer *buf, + void *patternp __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 __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 words which match the given . + * is of the standard file + * wildcarding form. + * :N words which do not match the given . + * :S[1gW] + * Substitute for in the value + * :C[1gW] + * Substitute for regex in the value + * :H Substitute the head of each word + * :T Substitute the tail of each word + * :E Substitute the extension (minus '.') of + * each word + * :R Substitute the root of each word + * (pathname minus the suffix). + * :O ("Order") Alphabeticaly sort words in variable. + * :Ox ("intermiX") Randomize words in variable. + * :u ("uniq") Remove adjacent duplicate words. + * :tu Converts the variable contents to uppercase. + * :tl Converts the variable contents to lowercase. + * :ts[c] Sets varSpace - the char used to + * separate words to 'c'. If 'c' is + * omitted then no separation is used. + * :tW Treat the variable contents as a single + * word, even if it contains spaces. + * (Mnemonic: one big 'W'ord.) + * :tw Treat the variable contents as multiple + * space-separated words. + * (Mnemonic: many small 'w'ords.) + * :[index] Select a single word from the value. + * :[start..end] Select multiple words from the value. + * :[*] or :[0] Select the entire value, as a single + * word. Equivalent to :tW. + * :[@] Select the entire value, as multiple + * words. Undoes the effect of :[*]. + * Equivalent to :tw. + * :[#] Returns the number of words in the value. + * + * :?: + * If the variable evaluates to true, return + * true value, else return the second value. + * :lhs=rhs Like :S, but the rhs goes to the end of + * the invocation. + * :sh Treat the current value as a command + * to be run, new value is its output. + * The following added so we can handle ODE makefiles. + * :@@@ + * Assign a temporary local variable + * to the current value of each word in turn + * and replace each word with the result of + * evaluating + * :D Use as value if variable defined + * :U Use as value if variable undefined + * :L Use the name of the variable as the value. + * :P Use the path of the node that has the same + * name as the variable as the value. This + * basically includes an implied :L so that + * the common method of refering to the path + * of your dependent 'x' in a rule is to use + * the form '${x:P}'. + * :!! Run cmd much the same as :sh run's the + * current value of the variable. + * The ::= modifiers, actually assign a value to the variable. + * Their main purpose is in supporting modifiers of .for loop + * iterators and other obscure uses. They always expand to + * nothing. In a target rule that would otherwise expand to an + * empty line they can be preceded with @: to keep make happy. + * Eg. + * + * foo: .USE + * .for i in ${.TARGET} ${.TARGET:R}.gz + * @: ${t::=$i} + * @echo blah ${t:T} + * .endfor + * + * ::= Assigns as the new value of variable. + * ::?= Assigns as value of variable if + * it was not already set. + * ::+= Appends to variable. + * ::!= Assigns output of as the new value of + * variable. + */ + +/* 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; /* "::" */ + } + 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" or + * ":ts:" */ + parsestate.varSpace = tstr[2]; + cp = tstr + 3; + } else if (tstr[2] == endc || tstr[2] == ':') { + /* ":ts" or ":ts:" */ + parsestate.varSpace = 0; /* no separator */ + cp = tstr + 2; + } else if (tstr[2] == '\\') { + switch (tstr[3]) { + case 'n': + parsestate.varSpace = '\n'; + cp = tstr + 4; + break; + case 't': + parsestate.varSpace = '\t'; + cp = tstr + 4; + break; + default: + if (isdigit((unsigned char)tstr[3])) { + char *ep; + + parsestate.varSpace = + strtoul(&tstr[3], &ep, 0); + if (*ep != ':' && *ep != endc) + goto bad_modifier; + cp = ep; + } else { + /* + * ":ts". + */ + goto bad_modifier; + } + break; + } + } else { + /* + * Found ":ts". + */ + goto bad_modifier; + } + + termc = *cp; + + /* + * We cannot be certain that VarModify + * will be used - even if there is a + * subsequent modifier, so do a no-op + * VarSubstitute now to for str to be + * re-expanded without the spaces. + */ + pattern.flags = VAR_SUB_ONE; + pattern.lhs = pattern.rhs = "\032"; + pattern.leftLen = pattern.rightLen = 1; + + newStr = VarModify(ctxt, &parsestate, nstr, + VarSubstitute, + &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:" or + * ":t". */ + goto bad_modifier; + } + } else { + /* + * Found ":t". + */ + goto bad_modifier; + } + } else { + /* + * Found ":t" or ":t:". + */ + goto bad_modifier; + } + break; + } + case 'N': + case 'M': + { + char *pattern; + 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: =) + */ + 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/wait.h b/wait.h new file mode 100644 index 000000000000..7408d15fb070 --- /dev/null +++ b/wait.h @@ -0,0 +1,81 @@ +/* NAME: + * wait.h - compensate for what vendors leave out + * + * AUTHOR: + * Simon J. Gerraty + */ +/* + * RCSid: + * $Id: wait.h,v 1.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 + +#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