aboutsummaryrefslogtreecommitdiff
path: root/contrib
diff options
context:
space:
mode:
authorSimon J. Gerraty <sjg@FreeBSD.org>2020-09-05 19:29:42 +0000
committerSimon J. Gerraty <sjg@FreeBSD.org>2020-09-05 19:29:42 +0000
commit2c3632d14fe37fa35c262ee9fb66835be0a52621 (patch)
tree57d0bb7a1e21c84dc625ee2f42c7677174a79c81 /contrib
parent08d0b468f14fec9d8d11eea0c7886f53df4eda2e (diff)
parent6bbc783f48498b808e19db4441299dc7d85a278b (diff)
downloadsrc-2c3632d14fe37fa35c262ee9fb66835be0a52621.tar.gz
src-2c3632d14fe37fa35c262ee9fb66835be0a52621.zip
Update to bmake-20200902
Lots of code refactoring, simplification and cleanup. Lots of new unit-tests providing much higher code coverage. All courtesy of rillig at netbsd. Other significant changes: o new read-only variable .SHELL which provides the path of the shell used to run scripts (as defined by the .SHELL target). o variable parsing detects more errors. o new debug option -dl: LINT mode, does the equivalent of := for all variable assignments so that file and line number are reported for variable parse errors.
Notes
Notes: svn path=/head/; revision=365366
Diffstat (limited to 'contrib')
-rw-r--r--contrib/bmake/ChangeLog240
-rw-r--r--contrib/bmake/FILES527
-rw-r--r--contrib/bmake/LICENSE60
-rw-r--r--contrib/bmake/Makefile42
-rw-r--r--contrib/bmake/VERSION2
-rw-r--r--contrib/bmake/arch.c505
-rw-r--r--contrib/bmake/bmake.1134
-rw-r--r--contrib/bmake/bmake.cat1136
-rw-r--r--contrib/bmake/bsd.after-import.mk21
-rw-r--r--contrib/bmake/buf.c216
-rw-r--r--contrib/bmake/buf.h62
-rw-r--r--contrib/bmake/compat.c136
-rw-r--r--contrib/bmake/cond.c621
-rw-r--r--contrib/bmake/dir.c1160
-rw-r--r--contrib/bmake/dir.h28
-rwxr-xr-xcontrib/bmake/enum.c100
-rwxr-xr-xcontrib/bmake/enum.h193
-rw-r--r--contrib/bmake/for.c157
-rw-r--r--contrib/bmake/hash.c277
-rw-r--r--contrib/bmake/hash.h81
-rw-r--r--contrib/bmake/job.c134
-rw-r--r--contrib/bmake/job.h17
-rw-r--r--contrib/bmake/lst.c641
-rw-r--r--contrib/bmake/lst.h194
-rw-r--r--contrib/bmake/lst.lib/Makefile0
-rw-r--r--contrib/bmake/lst.lib/lstAppend.c121
-rw-r--r--contrib/bmake/lst.lib/lstAtEnd.c79
-rw-r--r--contrib/bmake/lst.lib/lstAtFront.c76
-rw-r--r--contrib/bmake/lst.lib/lstClose.c85
-rw-r--r--contrib/bmake/lst.lib/lstConcat.c184
-rw-r--r--contrib/bmake/lst.lib/lstDatum.c76
-rw-r--r--contrib/bmake/lst.lib/lstDeQueue.c86
-rw-r--r--contrib/bmake/lst.lib/lstDestroy.c101
-rw-r--r--contrib/bmake/lst.lib/lstDupl.c107
-rw-r--r--contrib/bmake/lst.lib/lstEnQueue.c77
-rw-r--r--contrib/bmake/lst.lib/lstFind.c73
-rw-r--r--contrib/bmake/lst.lib/lstFindFrom.c89
-rw-r--r--contrib/bmake/lst.lib/lstFirst.c76
-rw-r--r--contrib/bmake/lst.lib/lstForEach.c75
-rw-r--r--contrib/bmake/lst.lib/lstForEachFrom.c124
-rw-r--r--contrib/bmake/lst.lib/lstInit.c85
-rw-r--r--contrib/bmake/lst.lib/lstInsert.c121
-rw-r--r--contrib/bmake/lst.lib/lstInt.h105
-rw-r--r--contrib/bmake/lst.lib/lstIsAtEnd.c86
-rw-r--r--contrib/bmake/lst.lib/lstIsEmpty.c74
-rw-r--r--contrib/bmake/lst.lib/lstLast.c76
-rw-r--r--contrib/bmake/lst.lib/lstMember.c77
-rw-r--r--contrib/bmake/lst.lib/lstNext.c119
-rw-r--r--contrib/bmake/lst.lib/lstOpen.c86
-rw-r--r--contrib/bmake/lst.lib/lstPrev.c78
-rw-r--r--contrib/bmake/lst.lib/lstRemove.c134
-rw-r--r--contrib/bmake/lst.lib/lstReplace.c77
-rw-r--r--contrib/bmake/lst.lib/lstSucc.c78
-rw-r--r--contrib/bmake/main.c376
-rwxr-xr-xcontrib/bmake/make-bootstrap.sh.in11
-rw-r--r--contrib/bmake/make-conf.h10
-rw-r--r--contrib/bmake/make.1134
-rw-r--r--contrib/bmake/make.c452
-rw-r--r--contrib/bmake/make.h413
-rw-r--r--contrib/bmake/make_malloc.c50
-rw-r--r--contrib/bmake/make_malloc.h19
-rw-r--r--contrib/bmake/meta.c151
-rw-r--r--contrib/bmake/metachar.c36
-rw-r--r--contrib/bmake/metachar.h21
-rw-r--r--contrib/bmake/mk/ChangeLog301
-rw-r--r--contrib/bmake/mk/FILES1
-rw-r--r--contrib/bmake/mk/README8
-rw-r--r--contrib/bmake/mk/auto.dep.mk14
-rw-r--r--contrib/bmake/mk/auto.obj.mk12
-rw-r--r--contrib/bmake/mk/autoconf.mk10
-rw-r--r--contrib/bmake/mk/autodep.mk16
-rw-r--r--contrib/bmake/mk/compiler.mk8
-rw-r--r--contrib/bmake/mk/cython.mk8
-rw-r--r--contrib/bmake/mk/dirdeps-cache-update.mk179
-rw-r--r--contrib/bmake/mk/dirdeps-options.mk39
-rw-r--r--contrib/bmake/mk/dirdeps-targets.mk47
-rw-r--r--contrib/bmake/mk/dirdeps.mk132
-rw-r--r--contrib/bmake/mk/dpadd.mk30
-rw-r--r--contrib/bmake/mk/files.mk8
-rw-r--r--contrib/bmake/mk/gendirdeps.mk42
-rw-r--r--contrib/bmake/mk/host-target.mk4
-rw-r--r--contrib/bmake/mk/host.libnames.mk8
-rw-r--r--contrib/bmake/mk/inc.mk8
-rw-r--r--contrib/bmake/mk/init.mk14
-rw-r--r--contrib/bmake/mk/install-mk14
-rw-r--r--contrib/bmake/mk/install-new.mk8
-rw-r--r--contrib/bmake/mk/java.mk10
-rw-r--r--contrib/bmake/mk/lib.mk14
-rw-r--r--contrib/bmake/mk/libnames.mk8
-rw-r--r--contrib/bmake/mk/libs.mk20
-rw-r--r--contrib/bmake/mk/links.mk8
-rw-r--r--contrib/bmake/mk/manifest.mk16
-rw-r--r--contrib/bmake/mk/meta.autodep.mk10
-rw-r--r--contrib/bmake/mk/meta.stage.mk8
-rw-r--r--contrib/bmake/mk/meta.subdir.mk10
-rw-r--r--contrib/bmake/mk/meta.sys.mk14
-rwxr-xr-xcontrib/bmake/mk/meta2deps.py48
-rwxr-xr-xcontrib/bmake/mk/meta2deps.sh30
-rw-r--r--contrib/bmake/mk/mk-files.txt51
-rwxr-xr-xcontrib/bmake/mk/mkopt.sh8
-rw-r--r--contrib/bmake/mk/obj.mk8
-rw-r--r--contrib/bmake/mk/options.mk12
-rw-r--r--contrib/bmake/mk/own.mk8
-rw-r--r--contrib/bmake/mk/prlist.mk8
-rw-r--r--contrib/bmake/mk/prog.mk4
-rw-r--r--contrib/bmake/mk/progs.mk17
-rw-r--r--contrib/bmake/mk/rst2htm.mk8
-rw-r--r--contrib/bmake/mk/scripts.mk8
-rw-r--r--contrib/bmake/mk/srctop.mk10
-rwxr-xr-xcontrib/bmake/mk/stage-install.sh61
-rw-r--r--contrib/bmake/mk/sys.clean-env.mk10
-rw-r--r--contrib/bmake/mk/sys.debug.mk8
-rw-r--r--contrib/bmake/mk/sys.dependfile.mk8
-rw-r--r--contrib/bmake/mk/sys.mk11
-rw-r--r--contrib/bmake/mk/sys.vars.mk10
-rw-r--r--contrib/bmake/mk/sys/AIX.mk8
-rw-r--r--contrib/bmake/mk/sys/Darwin.mk4
-rw-r--r--contrib/bmake/mk/sys/Generic.mk8
-rw-r--r--contrib/bmake/mk/sys/HP-UX.mk10
-rw-r--r--contrib/bmake/mk/sys/IRIX.mk4
-rw-r--r--contrib/bmake/mk/sys/Linux.mk6
-rw-r--r--contrib/bmake/mk/sys/NetBSD.mk4
-rw-r--r--contrib/bmake/mk/sys/OSF1.mk6
-rw-r--r--contrib/bmake/mk/sys/OpenBSD.mk6
-rw-r--r--contrib/bmake/mk/sys/SunOS.mk4
-rw-r--r--contrib/bmake/mk/sys/UnixWare.mk4
-rw-r--r--contrib/bmake/mk/target-flags.mk12
-rw-r--r--contrib/bmake/mk/warnings.mk20
-rw-r--r--contrib/bmake/mk/whats.mk10
-rw-r--r--contrib/bmake/mk/yacc.mk8
-rw-r--r--contrib/bmake/nonints.h69
-rwxr-xr-xcontrib/bmake/os.sh9
-rw-r--r--contrib/bmake/parse.c346
-rw-r--r--contrib/bmake/sprite.h116
-rw-r--r--contrib/bmake/str.c241
-rw-r--r--contrib/bmake/strlist.c20
-rw-r--r--contrib/bmake/strlist.h8
-rw-r--r--contrib/bmake/suff.c1235
-rw-r--r--contrib/bmake/targ.c444
-rw-r--r--contrib/bmake/trace.c12
-rw-r--r--contrib/bmake/unit-tests/Makefile358
-rwxr-xr-xcontrib/bmake/unit-tests/archive-suffix.exp2
-rwxr-xr-xcontrib/bmake/unit-tests/archive-suffix.mk23
-rw-r--r--contrib/bmake/unit-tests/archive.exp13
-rw-r--r--contrib/bmake/unit-tests/archive.mk45
-rwxr-xr-xcontrib/bmake/unit-tests/cmd-interrupt.exp9
-rwxr-xr-xcontrib/bmake/unit-tests/cmd-interrupt.mk50
-rw-r--r--contrib/bmake/unit-tests/cmdline.exp5
-rw-r--r--contrib/bmake/unit-tests/cmdline.mk37
-rw-r--r--contrib/bmake/unit-tests/cond-cmp-numeric-eq.exp1
-rwxr-xr-xcontrib/bmake/unit-tests/cond-cmp-numeric-eq.mk53
-rw-r--r--contrib/bmake/unit-tests/cond-cmp-numeric-ge.exp1
-rwxr-xr-xcontrib/bmake/unit-tests/cond-cmp-numeric-ge.mk75
-rw-r--r--contrib/bmake/unit-tests/cond-cmp-numeric-gt.exp1
-rwxr-xr-xcontrib/bmake/unit-tests/cond-cmp-numeric-gt.mk73
-rw-r--r--contrib/bmake/unit-tests/cond-cmp-numeric-le.exp1
-rwxr-xr-xcontrib/bmake/unit-tests/cond-cmp-numeric-le.mk75
-rw-r--r--contrib/bmake/unit-tests/cond-cmp-numeric-lt.exp1
-rwxr-xr-xcontrib/bmake/unit-tests/cond-cmp-numeric-lt.mk73
-rw-r--r--contrib/bmake/unit-tests/cond-cmp-numeric-ne.exp1
-rwxr-xr-xcontrib/bmake/unit-tests/cond-cmp-numeric-ne.mk49
-rw-r--r--contrib/bmake/unit-tests/cond-cmp-numeric.exp1
-rw-r--r--contrib/bmake/unit-tests/cond-cmp-numeric.mk8
-rw-r--r--contrib/bmake/unit-tests/cond-cmp-string.exp5
-rw-r--r--contrib/bmake/unit-tests/cond-cmp-string.mk39
-rw-r--r--contrib/bmake/unit-tests/cond-func-commands.exp1
-rw-r--r--contrib/bmake/unit-tests/cond-func-commands.mk36
-rw-r--r--contrib/bmake/unit-tests/cond-func-defined.exp5
-rw-r--r--contrib/bmake/unit-tests/cond-func-defined.mk33
-rw-r--r--contrib/bmake/unit-tests/cond-func-empty.exp1
-rw-r--r--contrib/bmake/unit-tests/cond-func-empty.mk8
-rw-r--r--contrib/bmake/unit-tests/cond-func-exists.exp1
-rw-r--r--contrib/bmake/unit-tests/cond-func-exists.mk42
-rw-r--r--contrib/bmake/unit-tests/cond-func-make.exp1
-rw-r--r--contrib/bmake/unit-tests/cond-func-make.mk8
-rw-r--r--contrib/bmake/unit-tests/cond-func-target.exp1
-rw-r--r--contrib/bmake/unit-tests/cond-func-target.mk38
-rw-r--r--contrib/bmake/unit-tests/cond-func.exp9
-rw-r--r--contrib/bmake/unit-tests/cond-func.mk63
-rw-r--r--contrib/bmake/unit-tests/cond-late.exp1
-rw-r--r--contrib/bmake/unit-tests/cond-late.mk10
-rw-r--r--contrib/bmake/unit-tests/cond-op-and.exp1
-rw-r--r--contrib/bmake/unit-tests/cond-op-and.mk27
-rw-r--r--contrib/bmake/unit-tests/cond-op-not.exp1
-rw-r--r--contrib/bmake/unit-tests/cond-op-not.mk22
-rw-r--r--contrib/bmake/unit-tests/cond-op-or.exp1
-rw-r--r--contrib/bmake/unit-tests/cond-op-or.mk27
-rw-r--r--contrib/bmake/unit-tests/cond-op-parentheses.exp1
-rw-r--r--contrib/bmake/unit-tests/cond-op-parentheses.mk8
-rw-r--r--contrib/bmake/unit-tests/cond-op.exp5
-rw-r--r--contrib/bmake/unit-tests/cond-op.mk60
-rw-r--r--contrib/bmake/unit-tests/cond-short.mk20
-rw-r--r--contrib/bmake/unit-tests/cond-token-number.exp1
-rw-r--r--contrib/bmake/unit-tests/cond-token-number.mk8
-rw-r--r--contrib/bmake/unit-tests/cond-token-plain.exp1
-rw-r--r--contrib/bmake/unit-tests/cond-token-plain.mk9
-rw-r--r--contrib/bmake/unit-tests/cond-token-string.exp1
-rw-r--r--contrib/bmake/unit-tests/cond-token-string.mk8
-rw-r--r--contrib/bmake/unit-tests/cond-token-var.exp7
-rw-r--r--contrib/bmake/unit-tests/cond-token-var.mk34
-rw-r--r--contrib/bmake/unit-tests/counter.exp88
-rw-r--r--contrib/bmake/unit-tests/counter.mk31
-rw-r--r--contrib/bmake/unit-tests/dep-colon.exp1
-rw-r--r--contrib/bmake/unit-tests/dep-colon.mk8
-rw-r--r--contrib/bmake/unit-tests/dep-double-colon.exp5
-rw-r--r--contrib/bmake/unit-tests/dep-double-colon.mk11
-rw-r--r--contrib/bmake/unit-tests/dep-exclam.exp1
-rw-r--r--contrib/bmake/unit-tests/dep-exclam.mk8
-rwxr-xr-xcontrib/bmake/unit-tests/dep-none.exp4
-rwxr-xr-xcontrib/bmake/unit-tests/dep-none.mk3
-rwxr-xr-xcontrib/bmake/unit-tests/dep-var.exp2
-rwxr-xr-xcontrib/bmake/unit-tests/dep-var.mk33
-rw-r--r--contrib/bmake/unit-tests/dep-wildcards.exp1
-rw-r--r--contrib/bmake/unit-tests/dep-wildcards.mk8
-rw-r--r--contrib/bmake/unit-tests/dep.exp1
-rw-r--r--contrib/bmake/unit-tests/dep.mk8
-rw-r--r--contrib/bmake/unit-tests/depsrc-exec.exp1
-rw-r--r--contrib/bmake/unit-tests/depsrc-exec.mk8
-rw-r--r--contrib/bmake/unit-tests/depsrc-ignore.exp11
-rw-r--r--contrib/bmake/unit-tests/depsrc-ignore.mk67
-rw-r--r--contrib/bmake/unit-tests/depsrc-made.exp1
-rw-r--r--contrib/bmake/unit-tests/depsrc-made.mk8
-rw-r--r--contrib/bmake/unit-tests/depsrc-make.exp1
-rw-r--r--contrib/bmake/unit-tests/depsrc-make.mk8
-rw-r--r--contrib/bmake/unit-tests/depsrc-meta.exp1
-rw-r--r--contrib/bmake/unit-tests/depsrc-meta.mk8
-rw-r--r--contrib/bmake/unit-tests/depsrc-nometa.exp1
-rw-r--r--contrib/bmake/unit-tests/depsrc-nometa.mk8
-rw-r--r--contrib/bmake/unit-tests/depsrc-nometa_cmp.exp1
-rw-r--r--contrib/bmake/unit-tests/depsrc-nometa_cmp.mk8
-rw-r--r--contrib/bmake/unit-tests/depsrc-nopath.exp1
-rw-r--r--contrib/bmake/unit-tests/depsrc-nopath.mk8
-rw-r--r--contrib/bmake/unit-tests/depsrc-notmain.exp1
-rw-r--r--contrib/bmake/unit-tests/depsrc-notmain.mk8
-rw-r--r--contrib/bmake/unit-tests/depsrc-optional.exp1
-rw-r--r--contrib/bmake/unit-tests/depsrc-optional.mk8
-rw-r--r--contrib/bmake/unit-tests/depsrc-phony.exp1
-rw-r--r--contrib/bmake/unit-tests/depsrc-phony.mk8
-rw-r--r--contrib/bmake/unit-tests/depsrc-precious.exp1
-rw-r--r--contrib/bmake/unit-tests/depsrc-precious.mk8
-rw-r--r--contrib/bmake/unit-tests/depsrc-recursive.exp1
-rw-r--r--contrib/bmake/unit-tests/depsrc-recursive.mk8
-rw-r--r--contrib/bmake/unit-tests/depsrc-silent.exp4
-rw-r--r--contrib/bmake/unit-tests/depsrc-silent.mk12
-rw-r--r--contrib/bmake/unit-tests/depsrc-use.exp6
-rw-r--r--contrib/bmake/unit-tests/depsrc-use.mk24
-rwxr-xr-xcontrib/bmake/unit-tests/depsrc-usebefore-double-colon.exp2
-rwxr-xr-xcontrib/bmake/unit-tests/depsrc-usebefore-double-colon.mk30
-rw-r--r--contrib/bmake/unit-tests/depsrc-usebefore.exp6
-rw-r--r--contrib/bmake/unit-tests/depsrc-usebefore.mk24
-rw-r--r--contrib/bmake/unit-tests/depsrc-wait.exp1
-rw-r--r--contrib/bmake/unit-tests/depsrc-wait.mk8
-rw-r--r--contrib/bmake/unit-tests/depsrc.exp1
-rw-r--r--contrib/bmake/unit-tests/depsrc.mk9
-rw-r--r--contrib/bmake/unit-tests/deptgt-begin.exp4
-rw-r--r--contrib/bmake/unit-tests/deptgt-begin.mk13
-rw-r--r--contrib/bmake/unit-tests/deptgt-default.exp1
-rw-r--r--contrib/bmake/unit-tests/deptgt-default.mk8
-rw-r--r--contrib/bmake/unit-tests/deptgt-delete_on_error.exp1
-rw-r--r--contrib/bmake/unit-tests/deptgt-delete_on_error.mk8
-rw-r--r--contrib/bmake/unit-tests/deptgt-end.exp4
-rw-r--r--contrib/bmake/unit-tests/deptgt-end.mk13
-rw-r--r--contrib/bmake/unit-tests/deptgt-error.exp1
-rw-r--r--contrib/bmake/unit-tests/deptgt-error.mk8
-rw-r--r--contrib/bmake/unit-tests/deptgt-ignore.exp1
-rw-r--r--contrib/bmake/unit-tests/deptgt-ignore.mk8
-rw-r--r--contrib/bmake/unit-tests/deptgt-interrupt.exp1
-rw-r--r--contrib/bmake/unit-tests/deptgt-interrupt.mk8
-rw-r--r--contrib/bmake/unit-tests/deptgt-main.exp1
-rw-r--r--contrib/bmake/unit-tests/deptgt-main.mk8
-rw-r--r--contrib/bmake/unit-tests/deptgt-makeflags.exp1
-rw-r--r--contrib/bmake/unit-tests/deptgt-makeflags.mk8
-rw-r--r--contrib/bmake/unit-tests/deptgt-no_parallel.exp1
-rw-r--r--contrib/bmake/unit-tests/deptgt-no_parallel.mk8
-rw-r--r--contrib/bmake/unit-tests/deptgt-nopath.exp1
-rw-r--r--contrib/bmake/unit-tests/deptgt-nopath.mk8
-rw-r--r--contrib/bmake/unit-tests/deptgt-notparallel.exp1
-rw-r--r--contrib/bmake/unit-tests/deptgt-notparallel.mk8
-rw-r--r--contrib/bmake/unit-tests/deptgt-objdir.exp1
-rw-r--r--contrib/bmake/unit-tests/deptgt-objdir.mk8
-rw-r--r--contrib/bmake/unit-tests/deptgt-order.exp1
-rw-r--r--contrib/bmake/unit-tests/deptgt-order.mk8
-rw-r--r--contrib/bmake/unit-tests/deptgt-path-suffix.exp1
-rw-r--r--contrib/bmake/unit-tests/deptgt-path-suffix.mk8
-rw-r--r--contrib/bmake/unit-tests/deptgt-path.exp1
-rw-r--r--contrib/bmake/unit-tests/deptgt-path.mk8
-rw-r--r--contrib/bmake/unit-tests/deptgt-phony.exp1
-rw-r--r--contrib/bmake/unit-tests/deptgt-phony.mk8
-rw-r--r--contrib/bmake/unit-tests/deptgt-precious.exp1
-rw-r--r--contrib/bmake/unit-tests/deptgt-precious.mk8
-rw-r--r--contrib/bmake/unit-tests/deptgt-shell.exp1
-rw-r--r--contrib/bmake/unit-tests/deptgt-shell.mk8
-rw-r--r--contrib/bmake/unit-tests/deptgt-silent.exp1
-rw-r--r--contrib/bmake/unit-tests/deptgt-silent.mk8
-rw-r--r--contrib/bmake/unit-tests/deptgt-stale.exp1
-rw-r--r--contrib/bmake/unit-tests/deptgt-stale.mk8
-rw-r--r--contrib/bmake/unit-tests/deptgt-suffixes.exp7
-rw-r--r--contrib/bmake/unit-tests/deptgt-suffixes.mk18
-rw-r--r--contrib/bmake/unit-tests/deptgt.exp1
-rw-r--r--contrib/bmake/unit-tests/deptgt.mk9
-rw-r--r--contrib/bmake/unit-tests/dir-expand-path.exp4
-rwxr-xr-xcontrib/bmake/unit-tests/dir-expand-path.mk19
-rw-r--r--contrib/bmake/unit-tests/dir.exp19
-rw-r--r--contrib/bmake/unit-tests/dir.mk58
-rw-r--r--contrib/bmake/unit-tests/directive-elif.exp1
-rw-r--r--contrib/bmake/unit-tests/directive-elif.mk8
-rw-r--r--contrib/bmake/unit-tests/directive-elifdef.exp1
-rw-r--r--contrib/bmake/unit-tests/directive-elifdef.mk8
-rw-r--r--contrib/bmake/unit-tests/directive-elifmake.exp1
-rw-r--r--contrib/bmake/unit-tests/directive-elifmake.mk8
-rw-r--r--contrib/bmake/unit-tests/directive-elifndef.exp1
-rw-r--r--contrib/bmake/unit-tests/directive-elifndef.mk8
-rw-r--r--contrib/bmake/unit-tests/directive-elifnmake.exp1
-rw-r--r--contrib/bmake/unit-tests/directive-elifnmake.mk8
-rw-r--r--contrib/bmake/unit-tests/directive-else.exp8
-rw-r--r--contrib/bmake/unit-tests/directive-else.mk32
-rw-r--r--contrib/bmake/unit-tests/directive-endif.exp1
-rw-r--r--contrib/bmake/unit-tests/directive-endif.mk8
-rw-r--r--contrib/bmake/unit-tests/directive-error.exp1
-rw-r--r--contrib/bmake/unit-tests/directive-error.mk8
-rw-r--r--contrib/bmake/unit-tests/directive-export-env.exp1
-rw-r--r--contrib/bmake/unit-tests/directive-export-env.mk8
-rw-r--r--contrib/bmake/unit-tests/directive-export-literal.exp1
-rw-r--r--contrib/bmake/unit-tests/directive-export-literal.mk8
-rw-r--r--contrib/bmake/unit-tests/directive-export.exp1
-rw-r--r--contrib/bmake/unit-tests/directive-export.mk8
-rwxr-xr-xcontrib/bmake/unit-tests/directive-for-generating-endif.exp7
-rwxr-xr-xcontrib/bmake/unit-tests/directive-for-generating-endif.mk25
-rwxr-xr-xcontrib/bmake/unit-tests/directive-for.exp1
-rwxr-xr-xcontrib/bmake/unit-tests/directive-for.mk97
-rw-r--r--contrib/bmake/unit-tests/directive-if.exp1
-rw-r--r--contrib/bmake/unit-tests/directive-if.mk8
-rw-r--r--contrib/bmake/unit-tests/directive-ifdef.exp1
-rw-r--r--contrib/bmake/unit-tests/directive-ifdef.mk8
-rw-r--r--contrib/bmake/unit-tests/directive-ifmake.exp10
-rw-r--r--contrib/bmake/unit-tests/directive-ifmake.mk55
-rw-r--r--contrib/bmake/unit-tests/directive-ifndef.exp1
-rw-r--r--contrib/bmake/unit-tests/directive-ifndef.mk8
-rw-r--r--contrib/bmake/unit-tests/directive-ifnmake.exp1
-rw-r--r--contrib/bmake/unit-tests/directive-ifnmake.mk8
-rw-r--r--contrib/bmake/unit-tests/directive-info.exp1
-rw-r--r--contrib/bmake/unit-tests/directive-info.mk8
-rw-r--r--contrib/bmake/unit-tests/directive-undef.exp1
-rw-r--r--contrib/bmake/unit-tests/directive-undef.mk17
-rw-r--r--contrib/bmake/unit-tests/directive-unexport-env.exp1
-rw-r--r--contrib/bmake/unit-tests/directive-unexport-env.mk8
-rw-r--r--contrib/bmake/unit-tests/directive-unexport.exp1
-rw-r--r--contrib/bmake/unit-tests/directive-unexport.mk8
-rw-r--r--contrib/bmake/unit-tests/directive-warning.exp1
-rw-r--r--contrib/bmake/unit-tests/directive-warning.mk8
-rw-r--r--contrib/bmake/unit-tests/directive.exp1
-rw-r--r--contrib/bmake/unit-tests/directive.mk8
-rw-r--r--contrib/bmake/unit-tests/directives.exp42
-rw-r--r--contrib/bmake/unit-tests/directives.mk163
-rw-r--r--contrib/bmake/unit-tests/envfirst.exp1
-rw-r--r--contrib/bmake/unit-tests/envfirst.mk42
-rw-r--r--contrib/bmake/unit-tests/export-all.mk3
-rwxr-xr-xcontrib/bmake/unit-tests/export-variants.exp1
-rwxr-xr-xcontrib/bmake/unit-tests/export-variants.mk40
-rw-r--r--contrib/bmake/unit-tests/export.exp2
-rw-r--r--contrib/bmake/unit-tests/export.mk31
-rw-r--r--contrib/bmake/unit-tests/forloop.mk4
-rw-r--r--contrib/bmake/unit-tests/impsrc.exp16
-rw-r--r--contrib/bmake/unit-tests/impsrc.mk29
-rw-r--r--contrib/bmake/unit-tests/include-main.mk4
-rwxr-xr-xcontrib/bmake/unit-tests/lint.exp4
-rwxr-xr-xcontrib/bmake/unit-tests/lint.mk17
-rwxr-xr-xcontrib/bmake/unit-tests/make-exported.exp3
-rwxr-xr-xcontrib/bmake/unit-tests/make-exported.mk16
-rw-r--r--contrib/bmake/unit-tests/moderrs.exp129
-rw-r--r--contrib/bmake/unit-tests/moderrs.mk151
-rw-r--r--contrib/bmake/unit-tests/modmatch.exp3
-rw-r--r--contrib/bmake/unit-tests/modmatch.mk17
-rw-r--r--contrib/bmake/unit-tests/modmisc.exp40
-rw-r--r--contrib/bmake/unit-tests/modmisc.mk113
-rw-r--r--contrib/bmake/unit-tests/modorder.exp12
-rw-r--r--contrib/bmake/unit-tests/modorder.mk24
-rw-r--r--contrib/bmake/unit-tests/modts.exp37
-rw-r--r--contrib/bmake/unit-tests/modts.mk37
-rw-r--r--contrib/bmake/unit-tests/opt-backwards.exp1
-rw-r--r--contrib/bmake/unit-tests/opt-backwards.mk8
-rw-r--r--contrib/bmake/unit-tests/opt-chdir.exp1
-rw-r--r--contrib/bmake/unit-tests/opt-chdir.mk8
-rwxr-xr-xcontrib/bmake/unit-tests/opt-debug-g1.exp15
-rwxr-xr-xcontrib/bmake/unit-tests/opt-debug-g1.mk19
-rw-r--r--contrib/bmake/unit-tests/opt-debug.exp1
-rw-r--r--contrib/bmake/unit-tests/opt-debug.mk8
-rw-r--r--contrib/bmake/unit-tests/opt-define.exp1
-rw-r--r--contrib/bmake/unit-tests/opt-define.mk8
-rw-r--r--contrib/bmake/unit-tests/opt-env.exp1
-rw-r--r--contrib/bmake/unit-tests/opt-env.mk8
-rw-r--r--contrib/bmake/unit-tests/opt-file.exp1
-rw-r--r--contrib/bmake/unit-tests/opt-file.mk8
-rw-r--r--contrib/bmake/unit-tests/opt-ignore.exp12
-rw-r--r--contrib/bmake/unit-tests/opt-ignore.mk30
-rw-r--r--contrib/bmake/unit-tests/opt-include-dir.exp1
-rw-r--r--contrib/bmake/unit-tests/opt-include-dir.mk8
-rw-r--r--contrib/bmake/unit-tests/opt-jobs-internal.exp1
-rw-r--r--contrib/bmake/unit-tests/opt-jobs-internal.mk8
-rw-r--r--contrib/bmake/unit-tests/opt-jobs.exp1
-rw-r--r--contrib/bmake/unit-tests/opt-jobs.mk8
-rw-r--r--contrib/bmake/unit-tests/opt-keep-going.exp6
-rw-r--r--contrib/bmake/unit-tests/opt-keep-going.mk24
-rw-r--r--contrib/bmake/unit-tests/opt-m-include-dir.exp2
-rw-r--r--contrib/bmake/unit-tests/opt-m-include-dir.mk61
-rw-r--r--contrib/bmake/unit-tests/opt-no-action-at-all.exp1
-rw-r--r--contrib/bmake/unit-tests/opt-no-action-at-all.mk8
-rw-r--r--contrib/bmake/unit-tests/opt-no-action.exp13
-rw-r--r--contrib/bmake/unit-tests/opt-no-action.mk33
-rw-r--r--contrib/bmake/unit-tests/opt-query.exp2
-rw-r--r--contrib/bmake/unit-tests/opt-query.mk24
-rw-r--r--contrib/bmake/unit-tests/opt-raw.exp1
-rw-r--r--contrib/bmake/unit-tests/opt-raw.mk8
-rw-r--r--contrib/bmake/unit-tests/opt-silent.exp1
-rw-r--r--contrib/bmake/unit-tests/opt-silent.mk8
-rw-r--r--contrib/bmake/unit-tests/opt-touch.exp1
-rw-r--r--contrib/bmake/unit-tests/opt-touch.mk8
-rw-r--r--contrib/bmake/unit-tests/opt-tracefile.exp1
-rw-r--r--contrib/bmake/unit-tests/opt-tracefile.mk8
-rw-r--r--contrib/bmake/unit-tests/opt-var-expanded.exp3
-rw-r--r--contrib/bmake/unit-tests/opt-var-expanded.mk6
-rw-r--r--contrib/bmake/unit-tests/opt-var-literal.exp3
-rw-r--r--contrib/bmake/unit-tests/opt-var-literal.mk6
-rw-r--r--contrib/bmake/unit-tests/opt-warnings-as-errors.exp7
-rw-r--r--contrib/bmake/unit-tests/opt-warnings-as-errors.mk11
-rw-r--r--contrib/bmake/unit-tests/opt-where-am-i.exp1
-rw-r--r--contrib/bmake/unit-tests/opt-where-am-i.mk8
-rw-r--r--contrib/bmake/unit-tests/opt-x-reduce-exported.exp1
-rw-r--r--contrib/bmake/unit-tests/opt-x-reduce-exported.mk8
-rw-r--r--contrib/bmake/unit-tests/opt.exp1
-rw-r--r--contrib/bmake/unit-tests/opt.mk8
-rw-r--r--contrib/bmake/unit-tests/phony-end.exp2
-rw-r--r--contrib/bmake/unit-tests/posix1.mk4
-rw-r--r--contrib/bmake/unit-tests/recursive.exp5
-rw-r--r--contrib/bmake/unit-tests/recursive.mk37
-rwxr-xr-xcontrib/bmake/unit-tests/sh-dots.exp15
-rwxr-xr-xcontrib/bmake/unit-tests/sh-dots.mk37
-rw-r--r--contrib/bmake/unit-tests/sh-jobs-error.exp1
-rw-r--r--contrib/bmake/unit-tests/sh-jobs-error.mk9
-rw-r--r--contrib/bmake/unit-tests/sh-jobs.exp1
-rw-r--r--contrib/bmake/unit-tests/sh-jobs.mk9
-rw-r--r--contrib/bmake/unit-tests/sh-leading-at.exp5
-rw-r--r--contrib/bmake/unit-tests/sh-leading-at.mk10
-rw-r--r--contrib/bmake/unit-tests/sh-leading-hyphen.exp1
-rw-r--r--contrib/bmake/unit-tests/sh-leading-hyphen.mk9
-rw-r--r--contrib/bmake/unit-tests/sh-leading-plus.exp4
-rw-r--r--contrib/bmake/unit-tests/sh-leading-plus.mk8
-rw-r--r--contrib/bmake/unit-tests/sh-meta-chars.exp1
-rw-r--r--contrib/bmake/unit-tests/sh-meta-chars.mk11
-rw-r--r--contrib/bmake/unit-tests/sh-multi-line.exp1
-rw-r--r--contrib/bmake/unit-tests/sh-multi-line.mk9
-rw-r--r--contrib/bmake/unit-tests/sh-single-line.exp1
-rw-r--r--contrib/bmake/unit-tests/sh-single-line.mk12
-rw-r--r--contrib/bmake/unit-tests/sh.exp1
-rw-r--r--contrib/bmake/unit-tests/sh.mk9
-rw-r--r--contrib/bmake/unit-tests/sysv.exp1
-rw-r--r--contrib/bmake/unit-tests/sysv.mk11
-rw-r--r--contrib/bmake/unit-tests/unexport-env.mk3
-rw-r--r--contrib/bmake/unit-tests/unexport.mk13
-rw-r--r--contrib/bmake/unit-tests/use-inference.exp4
-rw-r--r--contrib/bmake/unit-tests/use-inference.mk35
-rw-r--r--contrib/bmake/unit-tests/var-class-cmdline.exp1
-rw-r--r--contrib/bmake/unit-tests/var-class-cmdline.mk8
-rw-r--r--contrib/bmake/unit-tests/var-class-env.exp1
-rw-r--r--contrib/bmake/unit-tests/var-class-env.mk8
-rw-r--r--contrib/bmake/unit-tests/var-class-global.exp1
-rw-r--r--contrib/bmake/unit-tests/var-class-global.mk8
-rw-r--r--contrib/bmake/unit-tests/var-class-local-legacy.exp1
-rw-r--r--contrib/bmake/unit-tests/var-class-local-legacy.mk8
-rw-r--r--contrib/bmake/unit-tests/var-class-local.exp1
-rw-r--r--contrib/bmake/unit-tests/var-class-local.mk8
-rw-r--r--contrib/bmake/unit-tests/var-class.exp1
-rw-r--r--contrib/bmake/unit-tests/var-class.mk9
-rw-r--r--contrib/bmake/unit-tests/var-op-append.exp1
-rw-r--r--contrib/bmake/unit-tests/var-op-append.mk9
-rw-r--r--contrib/bmake/unit-tests/var-op-assign.exp6
-rw-r--r--contrib/bmake/unit-tests/var-op-assign.mk89
-rw-r--r--contrib/bmake/unit-tests/var-op-default.exp1
-rw-r--r--contrib/bmake/unit-tests/var-op-default.mk9
-rw-r--r--contrib/bmake/unit-tests/var-op-expand.exp1
-rw-r--r--contrib/bmake/unit-tests/var-op-expand.mk9
-rw-r--r--contrib/bmake/unit-tests/var-op-shell.exp1
-rw-r--r--contrib/bmake/unit-tests/var-op-shell.mk9
-rw-r--r--contrib/bmake/unit-tests/var-op.exp1
-rw-r--r--contrib/bmake/unit-tests/var-op.mk8
-rw-r--r--contrib/bmake/unit-tests/vardebug.exp80
-rw-r--r--contrib/bmake/unit-tests/vardebug.mk59
-rw-r--r--contrib/bmake/unit-tests/varfind.exp15
-rw-r--r--contrib/bmake/unit-tests/varfind.mk31
-rw-r--r--contrib/bmake/unit-tests/varmisc.exp48
-rw-r--r--contrib/bmake/unit-tests/varmisc.mk147
-rw-r--r--contrib/bmake/unit-tests/varmod-assign.exp26
-rw-r--r--contrib/bmake/unit-tests/varmod-assign.mk81
-rw-r--r--contrib/bmake/unit-tests/varmod-defined.exp1
-rw-r--r--contrib/bmake/unit-tests/varmod-defined.mk28
-rw-r--r--contrib/bmake/unit-tests/varmod-edge.exp35
-rw-r--r--contrib/bmake/unit-tests/varmod-edge.mk27
-rw-r--r--contrib/bmake/unit-tests/varmod-exclam-shell.exp1
-rw-r--r--contrib/bmake/unit-tests/varmod-exclam-shell.mk28
-rw-r--r--contrib/bmake/unit-tests/varmod-extension.exp10
-rw-r--r--contrib/bmake/unit-tests/varmod-extension.mk9
-rw-r--r--contrib/bmake/unit-tests/varmod-gmtime.exp9
-rw-r--r--contrib/bmake/unit-tests/varmod-gmtime.mk35
-rw-r--r--contrib/bmake/unit-tests/varmod-hash.exp9
-rw-r--r--contrib/bmake/unit-tests/varmod-hash.mk10
-rw-r--r--contrib/bmake/unit-tests/varmod-head.exp10
-rw-r--r--contrib/bmake/unit-tests/varmod-head.mk9
-rw-r--r--contrib/bmake/unit-tests/varmod-ifelse.exp1
-rw-r--r--contrib/bmake/unit-tests/varmod-ifelse.mk9
-rw-r--r--contrib/bmake/unit-tests/varmod-l-name-to-value.exp1
-rw-r--r--contrib/bmake/unit-tests/varmod-l-name-to-value.mk31
-rw-r--r--contrib/bmake/unit-tests/varmod-localtime.exp4
-rw-r--r--contrib/bmake/unit-tests/varmod-localtime.mk9
-rw-r--r--contrib/bmake/unit-tests/varmod-loop.exp16
-rw-r--r--contrib/bmake/unit-tests/varmod-loop.mk63
-rwxr-xr-xcontrib/bmake/unit-tests/varmod-match-escape.exp3
-rwxr-xr-xcontrib/bmake/unit-tests/varmod-match-escape.mk20
-rw-r--r--contrib/bmake/unit-tests/varmod-match.exp5
-rw-r--r--contrib/bmake/unit-tests/varmod-match.mk22
-rw-r--r--contrib/bmake/unit-tests/varmod-no-match.exp1
-rw-r--r--contrib/bmake/unit-tests/varmod-no-match.mk9
-rw-r--r--contrib/bmake/unit-tests/varmod-order-reverse.exp1
-rw-r--r--contrib/bmake/unit-tests/varmod-order-reverse.mk13
-rw-r--r--contrib/bmake/unit-tests/varmod-order-shuffle.exp1
-rw-r--r--contrib/bmake/unit-tests/varmod-order-shuffle.mk39
-rw-r--r--contrib/bmake/unit-tests/varmod-order.exp7
-rw-r--r--contrib/bmake/unit-tests/varmod-order.mk19
-rw-r--r--contrib/bmake/unit-tests/varmod-path.exp4
-rw-r--r--contrib/bmake/unit-tests/varmod-path.mk35
-rw-r--r--contrib/bmake/unit-tests/varmod-quote-dollar.exp1
-rw-r--r--contrib/bmake/unit-tests/varmod-quote-dollar.mk10
-rw-r--r--contrib/bmake/unit-tests/varmod-quote.exp1
-rw-r--r--contrib/bmake/unit-tests/varmod-quote.mk9
-rw-r--r--contrib/bmake/unit-tests/varmod-range.exp8
-rw-r--r--contrib/bmake/unit-tests/varmod-range.mk10
-rw-r--r--contrib/bmake/unit-tests/varmod-remember.exp3
-rw-r--r--contrib/bmake/unit-tests/varmod-remember.mk12
-rw-r--r--contrib/bmake/unit-tests/varmod-root.exp10
-rw-r--r--contrib/bmake/unit-tests/varmod-root.mk9
-rw-r--r--contrib/bmake/unit-tests/varmod-select-words.exp1
-rw-r--r--contrib/bmake/unit-tests/varmod-select-words.mk9
-rw-r--r--contrib/bmake/unit-tests/varmod-shell.exp1
-rw-r--r--contrib/bmake/unit-tests/varmod-shell.mk9
-rw-r--r--contrib/bmake/unit-tests/varmod-subst-regex.exp23
-rw-r--r--contrib/bmake/unit-tests/varmod-subst-regex.mk87
-rw-r--r--contrib/bmake/unit-tests/varmod-subst.exp51
-rw-r--r--contrib/bmake/unit-tests/varmod-subst.mk153
-rw-r--r--contrib/bmake/unit-tests/varmod-sysv.exp8
-rw-r--r--contrib/bmake/unit-tests/varmod-sysv.mk61
-rw-r--r--contrib/bmake/unit-tests/varmod-tail.exp10
-rw-r--r--contrib/bmake/unit-tests/varmod-tail.mk9
-rw-r--r--contrib/bmake/unit-tests/varmod-to-abs.exp1
-rw-r--r--contrib/bmake/unit-tests/varmod-to-abs.mk9
-rw-r--r--contrib/bmake/unit-tests/varmod-to-lower.exp1
-rw-r--r--contrib/bmake/unit-tests/varmod-to-lower.mk19
-rw-r--r--contrib/bmake/unit-tests/varmod-to-many-words.exp1
-rw-r--r--contrib/bmake/unit-tests/varmod-to-many-words.mk9
-rw-r--r--contrib/bmake/unit-tests/varmod-to-one-word.exp1
-rw-r--r--contrib/bmake/unit-tests/varmod-to-one-word.mk9
-rw-r--r--contrib/bmake/unit-tests/varmod-to-separator.exp9
-rw-r--r--contrib/bmake/unit-tests/varmod-to-separator.mk118
-rw-r--r--contrib/bmake/unit-tests/varmod-to-upper.exp2
-rw-r--r--contrib/bmake/unit-tests/varmod-to-upper.mk21
-rw-r--r--contrib/bmake/unit-tests/varmod-undefined.exp1
-rw-r--r--contrib/bmake/unit-tests/varmod-undefined.mk55
-rw-r--r--contrib/bmake/unit-tests/varmod-unique.exp1
-rw-r--r--contrib/bmake/unit-tests/varmod-unique.mk39
-rw-r--r--contrib/bmake/unit-tests/varmod.exp1
-rw-r--r--contrib/bmake/unit-tests/varmod.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-dollar.exp5
-rw-r--r--contrib/bmake/unit-tests/varname-dollar.mk29
-rw-r--r--contrib/bmake/unit-tests/varname-dot-alltargets.exp4
-rw-r--r--contrib/bmake/unit-tests/varname-dot-alltargets.mk25
-rw-r--r--contrib/bmake/unit-tests/varname-dot-curdir.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-dot-curdir.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-dot-includedfromdir.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-dot-includedfromdir.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-dot-includedfromfile.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-dot-includedfromfile.mk8
-rwxr-xr-xcontrib/bmake/unit-tests/varname-dot-includes.exp2
-rwxr-xr-xcontrib/bmake/unit-tests/varname-dot-includes.mk20
-rwxr-xr-xcontrib/bmake/unit-tests/varname-dot-libs.exp2
-rwxr-xr-xcontrib/bmake/unit-tests/varname-dot-libs.mk20
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-dependfile.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-dependfile.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-expand_variables.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-expand_variables.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-exported.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-exported.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-jobs-prefix.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-jobs-prefix.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-jobs.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-jobs.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-level.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-level.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-makefile_preference.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-makefile_preference.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-makefiles.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-makefiles.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-meta-bailiwick.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-meta-bailiwick.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-meta-created.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-meta-created.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-meta-files.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-meta-files.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-meta-prefix.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-meta-prefix.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-mode.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-mode.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-path_filemon.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-path_filemon.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-pid.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-pid.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-ppid.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-ppid.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-save_dollars.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-save_dollars.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-dot-makeoverrides.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-dot-makeoverrides.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-dot-newline.exp4
-rw-r--r--contrib/bmake/unit-tests/varname-dot-newline.mk23
-rw-r--r--contrib/bmake/unit-tests/varname-dot-objdir.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-dot-objdir.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-dot-parsedir.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-dot-parsedir.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-dot-parsefile.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-dot-parsefile.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-dot-path.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-dot-path.mk8
-rwxr-xr-xcontrib/bmake/unit-tests/varname-dot-shell.exp19
-rwxr-xr-xcontrib/bmake/unit-tests/varname-dot-shell.mk25
-rw-r--r--contrib/bmake/unit-tests/varname-dot-targets.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-dot-targets.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-empty.exp11
-rwxr-xr-xcontrib/bmake/unit-tests/varname-empty.mk26
-rw-r--r--contrib/bmake/unit-tests/varname-make.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-make.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-make_print_var_on_error.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-make_print_var_on_error.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-makeflags.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-makeflags.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-pwd.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-pwd.mk8
-rw-r--r--contrib/bmake/unit-tests/varname-vpath.exp1
-rw-r--r--contrib/bmake/unit-tests/varname-vpath.mk8
-rw-r--r--contrib/bmake/unit-tests/varname.exp1
-rw-r--r--contrib/bmake/unit-tests/varname.mk8
-rw-r--r--contrib/bmake/unit-tests/varparse-dynamic.exp5
-rw-r--r--contrib/bmake/unit-tests/varparse-dynamic.mk14
-rw-r--r--contrib/bmake/util.c74
-rw-r--r--contrib/bmake/var.c4749
657 files changed, 14930 insertions, 10884 deletions
diff --git a/contrib/bmake/ChangeLog b/contrib/bmake/ChangeLog
index 20e2628b3bed..847c2e4c0f90 100644
--- a/contrib/bmake/ChangeLog
+++ b/contrib/bmake/ChangeLog
@@ -1,3 +1,243 @@
+2020-09-02 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20200902
+ Merge with NetBSD make, pick up
+ o use make_stat to ensure no confusion over valid fields
+ returned by cached_stat
+ o var.c: make VarQuote const-correct
+ o add unit tests for .for
+
+2020-09-01 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20200901
+ Merge with NetBSD make, pick up
+ o rename Hash_Table fields
+ o make data types in Dir_HasWildcards more precise
+
+2020-08-31 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20200831
+ Merge with NetBSD make, pick up
+ o suff.c: fix unbalanced Lst_Open/Lst_Close in SuffFindCmds
+ o lst.c: Lst_Open renable assert that list isn't open
+ o unit test for .TARGET dependent flags
+ o var.c: fix aliasing bug in VarUniq
+ o more unit tests for :u
+
+2020-08-30 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20200830
+ Merge with NetBSD make, pick up
+ o allow for strict type checking for Boolean
+ o Var_Parse never returns NULL
+ o Var_Subst never returns NULL
+ o Lst_Find now takes boolean match function
+ o rename Lst_Memeber to Lst_FindDatum
+ o rename LstNode functions to match their type
+ o rename GNode.iParents to implicitParents
+ o fix assertion failure for .SUFFIXES in archives
+ o compat.c: clean up documentation for CompatInterrupt and Compat_Run
+ remove unreachable code from CompatRunCommand
+ o main.c: simplify getBoolean
+ o stc.c: replace brk_string with simpler Str_Words
+ o suff.c: add debug macros
+
+2020-08-28 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20200828
+ Merge with NetBSD make, pick up
+ o lst.c: inline LstIsValid and LstNodeIsValid
+ o remove trailing S from Lst function names after migration complete
+ o more comment cleanup/clarification
+ o suff.c: clean up suffix handling
+ o more unit tests
+
+2020-08-26 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20200826
+ Merge with NetBSD make, pick up
+ o enum.c: distinguish between bitsets containing flags and
+ ordinary enums
+ o var.c: fix error message for ::!= modifier with shell error
+ o fix bugs in -DCLEANUP mode
+
+2020-08-24 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20200824
+ Merge with NetBSD make, pick up
+ o in debug mode, print GNode details in symbols
+
+2020-08-23 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20200823
+ Merge with NetBSD make, pick up
+ o lst.c: more asserts,
+ make args to Lst_Find match others.
+ o var.c: pass flags to VarAdd
+ o arch.c: use Buffer
+ o str.c: brk_string return size_t for nwords
+ o more unit tests
+
+2020-08-22 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION):
+ Merge with NetBSD make, pick up
+ o var.c: support for read-only variables eg .SHELL
+ being the shell used to run scripts.
+ o lst.c: more simplification
+ o more documentation and style cleanup
+ o more unit tests
+ o ensure unit-test/Makefile is run by TEST_MAKE
+ o reduce duplication of header inclusion
+
+2020-08-21 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20200821
+ Merge with NetBSD make, pick up
+ o lst.c: revert invalid assertion - but document it
+ o dir.c: split Dir_Init into two functions
+
+2020-08-20 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * lst.c: needs inttypes.h on Linux
+
+ * VERSION (_MAKE_VERSION): 20200820
+ Merge with NetBSD make, pick up
+ o make.1: clarify some passages
+ o var.c: more cleanup, clarify comments
+ o make_malloc.c: remove unreachable code
+ o cond.c: make CondGetString easier to debug
+ o simplify list usage
+ o unit-tests: more
+
+2020-08-16 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20200816
+ Merge with NetBSD make, pick up
+ o refactor unit-tests to be more fine grained
+ not all tests moved yet
+
+2020-08-14 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20200814
+ Merge with NetBSD make, pick up
+ o more str_concat variants
+ o more enums for flags
+ o var.c: cleanup for higher warnings level
+
+2020-08-10 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20200810
+ Merge with NetBSD make, pick up
+ o more unit tests
+ o general comment and style cleanup
+
+2020-08-08 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20200808
+ Merge with NetBSD make, pick up
+ o enum.[ch]: streamline, enums for use in flags and debug output
+ o cond.c: cleanup
+ o var.c: reduce duplicate code for modifiers
+ debug logging for Var_Parse
+ more detailed debug output
+ o more unit tests
+
+2020-08-06 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * unit-tests/Makefile: -r for recursive and include Makefile.inc
+ so I can run tests in meta mode
+ supress extra noise if in meta mode
+
+ * VERSION (_MAKE_VERSION): 20200806
+ Merge with NetBSD make, pick up
+ o parse.c: remove VARE_WANTRES for LINT
+ we just want to check parsing (for now).
+
+2020-08-05 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20200805
+ Merge with NetBSD make, pick up
+ o make.1: Rework the description of dependence operators
+
+2020-08-03 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20200803
+ Merge with NetBSD make, pick up
+ o revert some C99 usage, for max portability
+ o unit-tests/lint
+
+2020-08-02 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20200802
+ Merge with NetBSD make, pick up
+ o more unit tests
+
+2020-08-01 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * Remove NetBSD specific plumbing from unit-tests/Makefile
+
+ * VERSION (_MAKE_VERSION): 20200801
+ Merge with NetBSD make, pick up
+ o make Var_Value return const
+ o size_t for buf sizes
+ o optimize some buffer operations - avoid strlen
+
+2020-07-31 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20200731
+ Merge with NetBSD make, pick up
+ o var.c: fix undefinded behavior for incomplete :t modifier
+ fixes unit-test/moderrs on Ubuntu
+ o parse.c: When parsing variable assignments other than :=
+ if DEBUG(LINT) test substition of value, so we get a file and
+ line number in the resulting error.
+ o dir.c: fix parsing of nested braces in dependency lines
+ add unit-tests
+
+2020-07-30 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20200730
+ Merge with NetBSD make, pick up
+ o var.c: minor cleanup
+ o unit-tests: more tests to improve code coverage
+
+2020-07-28 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20200728
+ Merge with NetBSD make, pick up
+ o var.c: more optimizations
+
+2020-07-26 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20200726
+ Merge with NetBSD make, pick up
+ o collapse lsd.lib into lst.c - reduce code size and allow inlining
+ o lots of function comment updates
+ o var.c: more optimizations
+ o make return of Var_Parse const
+
+2020-07-20 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20200720
+ Merge with NetBSD make, pick up
+ o DEBUG_HASH report stats at end and tone down the noise
+ o var.c: each flag type gets its own prefix.
+ move SysV string matching to var.c
+ make ampersand in ${VAR:from=to&} an ordinary character
+ cleanup and simplify implementation of modifiers
+ o make.1: move documentation for assignment modifiers
+
+2020-07-18 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20200718
+ Merge with NetBSD make, pick up
+ o DEBUG_HASH to see how well the hash tables are working
+
+2020-07-11 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * bsd.after-import.mk: make sure we update unit-tests/Makefile
+
2020-07-10 Simon J Gerraty <sjg@beast.crufty.net>
* configure.in: use AC_INCLUDES_DEFAULT rather than AC_HEADER_STDC
diff --git a/contrib/bmake/FILES b/contrib/bmake/FILES
index 0716bffa3c3b..33127290a76e 100644
--- a/contrib/bmake/FILES
+++ b/contrib/bmake/FILES
@@ -23,6 +23,8 @@ configure.in
dir.c
dir.h
dirname.c
+enum.c
+enum.h
filemon/filemon.h
filemon/filemon_dev.c
filemon/filemon_ktrace.c
@@ -34,36 +36,8 @@ hash.h
install-sh
job.c
job.h
+lst.c
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-bootstrap.sh.in
@@ -88,7 +62,6 @@ ranlib.h
realpath.c
setenv.c
sigcompat.c
-sprite.h
str.c
stresep.c
strlcpy.c
@@ -100,22 +73,234 @@ trace.c
trace.h
unit-tests/Makefile
unit-tests/Makefile.config.in
+unit-tests/archive.exp
+unit-tests/archive.mk
+unit-tests/archive-suffix.exp
+unit-tests/archive-suffix.mk
+unit-tests/cmd-interrupt.exp
+unit-tests/cmd-interrupt.mk
+unit-tests/cmdline.exp
+unit-tests/cmdline.mk
unit-tests/comment.exp
unit-tests/comment.mk
+unit-tests/cond-cmp-numeric-eq.exp
+unit-tests/cond-cmp-numeric-eq.mk
+unit-tests/cond-cmp-numeric-ge.exp
+unit-tests/cond-cmp-numeric-ge.mk
+unit-tests/cond-cmp-numeric-gt.exp
+unit-tests/cond-cmp-numeric-gt.mk
+unit-tests/cond-cmp-numeric-le.exp
+unit-tests/cond-cmp-numeric-le.mk
+unit-tests/cond-cmp-numeric-lt.exp
+unit-tests/cond-cmp-numeric-lt.mk
+unit-tests/cond-cmp-numeric-ne.exp
+unit-tests/cond-cmp-numeric-ne.mk
+unit-tests/cond-cmp-numeric.exp
+unit-tests/cond-cmp-numeric.mk
+unit-tests/cond-cmp-string.exp
+unit-tests/cond-cmp-string.mk
+unit-tests/cond-func.exp
+unit-tests/cond-func.mk
+unit-tests/cond-func-commands.exp
+unit-tests/cond-func-commands.mk
+unit-tests/cond-func-defined.exp
+unit-tests/cond-func-defined.mk
+unit-tests/cond-func-empty.exp
+unit-tests/cond-func-empty.mk
+unit-tests/cond-func-exists.exp
+unit-tests/cond-func-exists.mk
+unit-tests/cond-func-make.exp
+unit-tests/cond-func-make.mk
+unit-tests/cond-func-target.exp
+unit-tests/cond-func-target.mk
+unit-tests/cond-late.exp
+unit-tests/cond-late.mk
+unit-tests/cond-op-and.exp
+unit-tests/cond-op-and.mk
+unit-tests/cond-op-not.exp
+unit-tests/cond-op-not.mk
+unit-tests/cond-op-or.exp
+unit-tests/cond-op-or.mk
+unit-tests/cond-op-parentheses.exp
+unit-tests/cond-op-parentheses.mk
+unit-tests/cond-op.exp
+unit-tests/cond-op.mk
+unit-tests/cond-short.exp
+unit-tests/cond-short.mk
+unit-tests/cond-token-number.exp
+unit-tests/cond-token-number.mk
+unit-tests/cond-token-plain.exp
+unit-tests/cond-token-plain.mk
+unit-tests/cond-token-string.exp
+unit-tests/cond-token-string.mk
+unit-tests/cond-token-var.exp
+unit-tests/cond-token-var.mk
unit-tests/cond1.exp
unit-tests/cond1.mk
unit-tests/cond2.exp
unit-tests/cond2.mk
-unit-tests/cond-late.mk
-unit-tests/cond-late.exp
-unit-tests/cond-short.mk
-unit-tests/cond-short.exp
+unit-tests/counter.exp
+unit-tests/counter.mk
+unit-tests/dep-colon.exp
+unit-tests/dep-colon.mk
+unit-tests/dep-double-colon.exp
+unit-tests/dep-double-colon.mk
+unit-tests/dep-exclam.exp
+unit-tests/dep-exclam.mk
+unit-tests/dep-none.exp
+unit-tests/dep-none.mk
+unit-tests/dep-var.exp
+unit-tests/dep-var.mk
+unit-tests/dep-wildcards.exp
+unit-tests/dep-wildcards.mk
+unit-tests/dep.exp
+unit-tests/dep.mk
+unit-tests/depsrc-exec.exp
+unit-tests/depsrc-exec.mk
+unit-tests/depsrc-ignore.exp
+unit-tests/depsrc-ignore.mk
+unit-tests/depsrc-made.exp
+unit-tests/depsrc-made.mk
+unit-tests/depsrc-make.exp
+unit-tests/depsrc-make.mk
+unit-tests/depsrc-meta.exp
+unit-tests/depsrc-meta.mk
+unit-tests/depsrc-nometa.exp
+unit-tests/depsrc-nometa.mk
+unit-tests/depsrc-nometa_cmp.exp
+unit-tests/depsrc-nometa_cmp.mk
+unit-tests/depsrc-nopath.exp
+unit-tests/depsrc-nopath.mk
+unit-tests/depsrc-notmain.exp
+unit-tests/depsrc-notmain.mk
+unit-tests/depsrc-optional.exp
+unit-tests/depsrc-optional.mk
+unit-tests/depsrc-phony.exp
+unit-tests/depsrc-phony.mk
+unit-tests/depsrc-precious.exp
+unit-tests/depsrc-precious.mk
+unit-tests/depsrc-recursive.exp
+unit-tests/depsrc-recursive.mk
+unit-tests/depsrc-silent.exp
+unit-tests/depsrc-silent.mk
+unit-tests/depsrc-use.exp
+unit-tests/depsrc-use.mk
+unit-tests/depsrc-usebefore.exp
+unit-tests/depsrc-usebefore.mk
+unit-tests/depsrc-usebefore-double-colon.exp
+unit-tests/depsrc-usebefore-double-colon.mk
+unit-tests/depsrc-wait.exp
+unit-tests/depsrc-wait.mk
+unit-tests/depsrc.exp
+unit-tests/depsrc.mk
+unit-tests/deptgt-begin.exp
+unit-tests/deptgt-begin.mk
+unit-tests/deptgt-default.exp
+unit-tests/deptgt-default.mk
+unit-tests/deptgt-delete_on_error.exp
+unit-tests/deptgt-delete_on_error.mk
+unit-tests/deptgt-end.exp
+unit-tests/deptgt-end.mk
+unit-tests/deptgt-error.exp
+unit-tests/deptgt-error.mk
+unit-tests/deptgt-ignore.exp
+unit-tests/deptgt-ignore.mk
+unit-tests/deptgt-interrupt.exp
+unit-tests/deptgt-interrupt.mk
+unit-tests/deptgt-main.exp
+unit-tests/deptgt-main.mk
+unit-tests/deptgt-makeflags.exp
+unit-tests/deptgt-makeflags.mk
+unit-tests/deptgt-no_parallel.exp
+unit-tests/deptgt-no_parallel.mk
+unit-tests/deptgt-nopath.exp
+unit-tests/deptgt-nopath.mk
+unit-tests/deptgt-notparallel.exp
+unit-tests/deptgt-notparallel.mk
+unit-tests/deptgt-objdir.exp
+unit-tests/deptgt-objdir.mk
+unit-tests/deptgt-order.exp
+unit-tests/deptgt-order.mk
+unit-tests/deptgt-path-suffix.exp
+unit-tests/deptgt-path-suffix.mk
+unit-tests/deptgt-path.exp
+unit-tests/deptgt-path.mk
+unit-tests/deptgt-phony.exp
+unit-tests/deptgt-phony.mk
+unit-tests/deptgt-precious.exp
+unit-tests/deptgt-precious.mk
+unit-tests/deptgt-shell.exp
+unit-tests/deptgt-shell.mk
+unit-tests/deptgt-silent.exp
+unit-tests/deptgt-silent.mk
+unit-tests/deptgt-stale.exp
+unit-tests/deptgt-stale.mk
+unit-tests/deptgt-suffixes.exp
+unit-tests/deptgt-suffixes.mk
+unit-tests/deptgt.exp
+unit-tests/deptgt.mk
+unit-tests/dir.exp
+unit-tests/dir.mk
+unit-tests/dir-expand-path.exp
+unit-tests/dir-expand-path.mk
+unit-tests/directive-elif.exp
+unit-tests/directive-elif.mk
+unit-tests/directive-elifdef.exp
+unit-tests/directive-elifdef.mk
+unit-tests/directive-elifmake.exp
+unit-tests/directive-elifmake.mk
+unit-tests/directive-elifndef.exp
+unit-tests/directive-elifndef.mk
+unit-tests/directive-elifnmake.exp
+unit-tests/directive-elifnmake.mk
+unit-tests/directive-else.exp
+unit-tests/directive-else.mk
+unit-tests/directive-endif.exp
+unit-tests/directive-endif.mk
+unit-tests/directive-error.exp
+unit-tests/directive-error.mk
+unit-tests/directive-export-env.exp
+unit-tests/directive-export-env.mk
+unit-tests/directive-export-literal.exp
+unit-tests/directive-export-literal.mk
+unit-tests/directive-export.exp
+unit-tests/directive-export.mk
+unit-tests/directive-for.exp
+unit-tests/directive-for.mk
+unit-tests/directive-for-generating-endif.exp
+unit-tests/directive-for-generating-endif.mk
+unit-tests/directive-if.exp
+unit-tests/directive-if.mk
+unit-tests/directive-ifdef.exp
+unit-tests/directive-ifdef.mk
+unit-tests/directive-ifmake.exp
+unit-tests/directive-ifmake.mk
+unit-tests/directive-ifndef.exp
+unit-tests/directive-ifndef.mk
+unit-tests/directive-ifnmake.exp
+unit-tests/directive-ifnmake.mk
+unit-tests/directive-info.exp
+unit-tests/directive-info.mk
+unit-tests/directive-undef.exp
+unit-tests/directive-undef.mk
+unit-tests/directive-unexport-env.exp
+unit-tests/directive-unexport-env.mk
+unit-tests/directive-unexport.exp
+unit-tests/directive-unexport.mk
+unit-tests/directive-warning.exp
+unit-tests/directive-warning.mk
+unit-tests/directive.exp
+unit-tests/directive.mk
+unit-tests/directives.exp
+unit-tests/directives.mk
unit-tests/dollar.exp
unit-tests/dollar.mk
unit-tests/doterror.exp
unit-tests/doterror.mk
unit-tests/dotwait.exp
unit-tests/dotwait.mk
+unit-tests/envfirst.exp
+unit-tests/envfirst.mk
unit-tests/error.exp
unit-tests/error.mk
unit-tests/escape.exp
@@ -124,6 +309,8 @@ unit-tests/export-all.exp
unit-tests/export-all.mk
unit-tests/export-env.exp
unit-tests/export-env.mk
+unit-tests/export-variants.exp
+unit-tests/export-variants.mk
unit-tests/export.exp
unit-tests/export.mk
unit-tests/forloop.exp
@@ -138,6 +325,10 @@ unit-tests/include-main.exp
unit-tests/include-main.mk
unit-tests/include-sub.mk
unit-tests/include-subsub.mk
+unit-tests/lint.exp
+unit-tests/lint.mk
+unit-tests/make-exported.exp
+unit-tests/make-exported.mk
unit-tests/misc.exp
unit-tests/misc.mk
unit-tests/moderrs.exp
@@ -146,12 +337,62 @@ unit-tests/modmatch.exp
unit-tests/modmatch.mk
unit-tests/modmisc.exp
unit-tests/modmisc.mk
-unit-tests/modorder.exp
-unit-tests/modorder.mk
unit-tests/modts.exp
unit-tests/modts.mk
unit-tests/modword.exp
unit-tests/modword.mk
+unit-tests/opt-backwards.exp
+unit-tests/opt-backwards.mk
+unit-tests/opt-chdir.exp
+unit-tests/opt-chdir.mk
+unit-tests/opt-debug.exp
+unit-tests/opt-debug.mk
+unit-tests/opt-debug-g1.exp
+unit-tests/opt-debug-g1.mk
+unit-tests/opt-define.exp
+unit-tests/opt-define.mk
+unit-tests/opt-env.exp
+unit-tests/opt-env.mk
+unit-tests/opt-file.exp
+unit-tests/opt-file.mk
+unit-tests/opt-ignore.exp
+unit-tests/opt-ignore.mk
+unit-tests/opt-include-dir.exp
+unit-tests/opt-include-dir.mk
+unit-tests/opt-jobs-internal.exp
+unit-tests/opt-jobs-internal.mk
+unit-tests/opt-jobs.exp
+unit-tests/opt-jobs.mk
+unit-tests/opt-keep-going.exp
+unit-tests/opt-keep-going.mk
+unit-tests/opt-m-include-dir.exp
+unit-tests/opt-m-include-dir.mk
+unit-tests/opt-no-action-at-all.exp
+unit-tests/opt-no-action-at-all.mk
+unit-tests/opt-no-action.exp
+unit-tests/opt-no-action.mk
+unit-tests/opt-query.exp
+unit-tests/opt-query.mk
+unit-tests/opt-raw.exp
+unit-tests/opt-raw.mk
+unit-tests/opt-silent.exp
+unit-tests/opt-silent.mk
+unit-tests/opt-touch.exp
+unit-tests/opt-touch.mk
+unit-tests/opt-tracefile.exp
+unit-tests/opt-tracefile.mk
+unit-tests/opt-var-expanded.exp
+unit-tests/opt-var-expanded.mk
+unit-tests/opt-var-literal.exp
+unit-tests/opt-var-literal.mk
+unit-tests/opt-warnings-as-errors.exp
+unit-tests/opt-warnings-as-errors.mk
+unit-tests/opt-where-am-i.exp
+unit-tests/opt-where-am-i.mk
+unit-tests/opt-x-reduce-exported.exp
+unit-tests/opt-x-reduce-exported.mk
+unit-tests/opt.exp
+unit-tests/opt.mk
unit-tests/order.exp
unit-tests/order.mk
unit-tests/phony-end.exp
@@ -162,6 +403,28 @@ unit-tests/posix1.exp
unit-tests/posix1.mk
unit-tests/qequals.exp
unit-tests/qequals.mk
+unit-tests/recursive.exp
+unit-tests/recursive.mk
+unit-tests/sh-dots.exp
+unit-tests/sh-dots.mk
+unit-tests/sh-jobs-error.exp
+unit-tests/sh-jobs-error.mk
+unit-tests/sh-jobs.exp
+unit-tests/sh-jobs.mk
+unit-tests/sh-leading-at.exp
+unit-tests/sh-leading-at.mk
+unit-tests/sh-leading-hyphen.exp
+unit-tests/sh-leading-hyphen.mk
+unit-tests/sh-leading-plus.exp
+unit-tests/sh-leading-plus.mk
+unit-tests/sh-meta-chars.exp
+unit-tests/sh-meta-chars.mk
+unit-tests/sh-multi-line.exp
+unit-tests/sh-multi-line.mk
+unit-tests/sh-single-line.exp
+unit-tests/sh-single-line.mk
+unit-tests/sh.exp
+unit-tests/sh.mk
unit-tests/suffixes.exp
unit-tests/suffixes.mk
unit-tests/sunshcmd.exp
@@ -174,12 +437,204 @@ unit-tests/unexport-env.exp
unit-tests/unexport-env.mk
unit-tests/unexport.exp
unit-tests/unexport.mk
+unit-tests/use-inference.exp
+unit-tests/use-inference.mk
+unit-tests/var-class-cmdline.exp
+unit-tests/var-class-cmdline.mk
+unit-tests/var-class-env.exp
+unit-tests/var-class-env.mk
+unit-tests/var-class-global.exp
+unit-tests/var-class-global.mk
+unit-tests/var-class-local-legacy.exp
+unit-tests/var-class-local-legacy.mk
+unit-tests/var-class-local.exp
+unit-tests/var-class-local.mk
+unit-tests/var-class.exp
+unit-tests/var-class.mk
+unit-tests/var-op-append.exp
+unit-tests/var-op-append.mk
+unit-tests/var-op-assign.exp
+unit-tests/var-op-assign.mk
+unit-tests/var-op-default.exp
+unit-tests/var-op-default.mk
+unit-tests/var-op-expand.exp
+unit-tests/var-op-expand.mk
+unit-tests/var-op-shell.exp
+unit-tests/var-op-shell.mk
+unit-tests/var-op.exp
+unit-tests/var-op.mk
unit-tests/varcmd.exp
unit-tests/varcmd.mk
+unit-tests/vardebug.exp
+unit-tests/vardebug.mk
+unit-tests/varfind.exp
+unit-tests/varfind.mk
unit-tests/varmisc.exp
unit-tests/varmisc.mk
+unit-tests/varmod-assign.exp
+unit-tests/varmod-assign.mk
+unit-tests/varmod-defined.exp
+unit-tests/varmod-defined.mk
unit-tests/varmod-edge.exp
unit-tests/varmod-edge.mk
+unit-tests/varmod-exclam-shell.exp
+unit-tests/varmod-exclam-shell.mk
+unit-tests/varmod-extension.exp
+unit-tests/varmod-extension.mk
+unit-tests/varmod-gmtime.exp
+unit-tests/varmod-gmtime.mk
+unit-tests/varmod-hash.exp
+unit-tests/varmod-hash.mk
+unit-tests/varmod-head.exp
+unit-tests/varmod-head.mk
+unit-tests/varmod-ifelse.exp
+unit-tests/varmod-ifelse.mk
+unit-tests/varmod-l-name-to-value.exp
+unit-tests/varmod-l-name-to-value.mk
+unit-tests/varmod-localtime.exp
+unit-tests/varmod-localtime.mk
+unit-tests/varmod-loop.exp
+unit-tests/varmod-loop.mk
+unit-tests/varmod-match-escape.exp
+unit-tests/varmod-match-escape.mk
+unit-tests/varmod-match.exp
+unit-tests/varmod-match.mk
+unit-tests/varmod-no-match.exp
+unit-tests/varmod-no-match.mk
+unit-tests/varmod-order-reverse.exp
+unit-tests/varmod-order-reverse.mk
+unit-tests/varmod-order-shuffle.exp
+unit-tests/varmod-order-shuffle.mk
+unit-tests/varmod-order.exp
+unit-tests/varmod-order.mk
+unit-tests/varmod-path.exp
+unit-tests/varmod-path.mk
+unit-tests/varmod-quote-dollar.exp
+unit-tests/varmod-quote-dollar.mk
+unit-tests/varmod-quote.exp
+unit-tests/varmod-quote.mk
+unit-tests/varmod-range.exp
+unit-tests/varmod-range.mk
+unit-tests/varmod-remember.exp
+unit-tests/varmod-remember.mk
+unit-tests/varmod-root.exp
+unit-tests/varmod-root.mk
+unit-tests/varmod-select-words.exp
+unit-tests/varmod-select-words.mk
+unit-tests/varmod-shell.exp
+unit-tests/varmod-shell.mk
+unit-tests/varmod-subst-regex.exp
+unit-tests/varmod-subst-regex.mk
+unit-tests/varmod-subst.exp
+unit-tests/varmod-subst.mk
+unit-tests/varmod-sysv.exp
+unit-tests/varmod-sysv.mk
+unit-tests/varmod-tail.exp
+unit-tests/varmod-tail.mk
+unit-tests/varmod-to-abs.exp
+unit-tests/varmod-to-abs.mk
+unit-tests/varmod-to-lower.exp
+unit-tests/varmod-to-lower.mk
+unit-tests/varmod-to-many-words.exp
+unit-tests/varmod-to-many-words.mk
+unit-tests/varmod-to-one-word.exp
+unit-tests/varmod-to-one-word.mk
+unit-tests/varmod-to-separator.exp
+unit-tests/varmod-to-separator.mk
+unit-tests/varmod-to-upper.exp
+unit-tests/varmod-to-upper.mk
+unit-tests/varmod-undefined.exp
+unit-tests/varmod-undefined.mk
+unit-tests/varmod-unique.exp
+unit-tests/varmod-unique.mk
+unit-tests/varmod.exp
+unit-tests/varmod.mk
+unit-tests/varname-dollar.exp
+unit-tests/varname-dollar.mk
+unit-tests/varname-dot-alltargets.exp
+unit-tests/varname-dot-alltargets.mk
+unit-tests/varname-dot-curdir.exp
+unit-tests/varname-dot-curdir.mk
+unit-tests/varname-dot-includes.exp
+unit-tests/varname-dot-includes.mk
+unit-tests/varname-dot-includedfromdir.exp
+unit-tests/varname-dot-includedfromdir.mk
+unit-tests/varname-dot-includedfromfile.exp
+unit-tests/varname-dot-includedfromfile.mk
+unit-tests/varname-dot-libs.exp
+unit-tests/varname-dot-libs.mk
+unit-tests/varname-dot-make-dependfile.exp
+unit-tests/varname-dot-make-dependfile.mk
+unit-tests/varname-dot-make-expand_variables.exp
+unit-tests/varname-dot-make-expand_variables.mk
+unit-tests/varname-dot-make-exported.exp
+unit-tests/varname-dot-make-exported.mk
+unit-tests/varname-dot-make-jobs-prefix.exp
+unit-tests/varname-dot-make-jobs-prefix.mk
+unit-tests/varname-dot-make-jobs.exp
+unit-tests/varname-dot-make-jobs.mk
+unit-tests/varname-dot-make-level.exp
+unit-tests/varname-dot-make-level.mk
+unit-tests/varname-dot-make-makefile_preference.exp
+unit-tests/varname-dot-make-makefile_preference.mk
+unit-tests/varname-dot-make-makefiles.exp
+unit-tests/varname-dot-make-makefiles.mk
+unit-tests/varname-dot-make-meta-bailiwick.exp
+unit-tests/varname-dot-make-meta-bailiwick.mk
+unit-tests/varname-dot-make-meta-created.exp
+unit-tests/varname-dot-make-meta-created.mk
+unit-tests/varname-dot-make-meta-files.exp
+unit-tests/varname-dot-make-meta-files.mk
+unit-tests/varname-dot-make-meta-ignore_filter.exp
+unit-tests/varname-dot-make-meta-ignore_filter.mk
+unit-tests/varname-dot-make-meta-ignore_paths.exp
+unit-tests/varname-dot-make-meta-ignore_paths.mk
+unit-tests/varname-dot-make-meta-ignore_patterns.exp
+unit-tests/varname-dot-make-meta-ignore_patterns.mk
+unit-tests/varname-dot-make-meta-prefix.exp
+unit-tests/varname-dot-make-meta-prefix.mk
+unit-tests/varname-dot-make-mode.exp
+unit-tests/varname-dot-make-mode.mk
+unit-tests/varname-dot-make-path_filemon.exp
+unit-tests/varname-dot-make-path_filemon.mk
+unit-tests/varname-dot-make-pid.exp
+unit-tests/varname-dot-make-pid.mk
+unit-tests/varname-dot-make-ppid.exp
+unit-tests/varname-dot-make-ppid.mk
+unit-tests/varname-dot-make-save_dollars.exp
+unit-tests/varname-dot-make-save_dollars.mk
+unit-tests/varname-dot-makeoverrides.exp
+unit-tests/varname-dot-makeoverrides.mk
+unit-tests/varname-dot-newline.exp
+unit-tests/varname-dot-newline.mk
+unit-tests/varname-dot-objdir.exp
+unit-tests/varname-dot-objdir.mk
+unit-tests/varname-dot-parsedir.exp
+unit-tests/varname-dot-parsedir.mk
+unit-tests/varname-dot-parsefile.exp
+unit-tests/varname-dot-parsefile.mk
+unit-tests/varname-dot-path.exp
+unit-tests/varname-dot-path.mk
+unit-tests/varname-dot-shell.exp
+unit-tests/varname-dot-shell.mk
+unit-tests/varname-dot-targets.exp
+unit-tests/varname-dot-targets.mk
+unit-tests/varname-empty.exp
+unit-tests/varname-empty.mk
+unit-tests/varname-make.exp
+unit-tests/varname-make.mk
+unit-tests/varname-make_print_var_on_error.exp
+unit-tests/varname-make_print_var_on_error.mk
+unit-tests/varname-makeflags.exp
+unit-tests/varname-makeflags.mk
+unit-tests/varname-pwd.exp
+unit-tests/varname-pwd.mk
+unit-tests/varname-vpath.exp
+unit-tests/varname-vpath.mk
+unit-tests/varname.exp
+unit-tests/varname.mk
+unit-tests/varparse-dynamic.exp
+unit-tests/varparse-dynamic.mk
unit-tests/varquote.exp
unit-tests/varquote.mk
unit-tests/varshell.exp
diff --git a/contrib/bmake/LICENSE b/contrib/bmake/LICENSE
index e69de29bb2d1..283dd20cccd0 100644
--- a/contrib/bmake/LICENSE
+++ b/contrib/bmake/LICENSE
@@ -0,0 +1,60 @@
+The individual files in this distribution are copyright their
+original contributors or assignees.
+Including:
+
+ Copyright (c) 1993-2020, Simon J Gerraty
+ Copyright (c) 2020, Roland Illig <rillig@NetBSD.org>
+ Copyright (c) 2009-2016, Juniper Networks, Inc.
+ Copyright (c) 2009, John Birrell.
+ Copyright (c) 1997-2020 The NetBSD Foundation, Inc.
+ Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ Copyright (c) 1989 by Berkeley Softworks
+ Copyright (c) 1988, 1989, 1990, 1992, 1993
+ The Regents of the University of California.
+ Copyright (c) 1988, 1989 by Adam de Boor
+
+With the exception of the GNU configure script, which states:
+
+ # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
+ #
+ # This configure script is free software; the Free Software Foundation
+ # gives unlimited permission to copy, distribute and modify it.
+
+The license for this distribution is considered to be:
+
+ SPDX-License-Identifier: BSD-3-Clause
+
+example (from https://opensource.org/licenses/BSD-3-Clause):
+
+ Copyright <YEAR> <COPYRIGHT HOLDER>
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce 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 holder nor the names of its
+ 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 HOLDER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
diff --git a/contrib/bmake/Makefile b/contrib/bmake/Makefile
index c329017b13ef..7277d9554de4 100644
--- a/contrib/bmake/Makefile
+++ b/contrib/bmake/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.107 2020/06/07 21:18:46 sjg Exp $
+# $Id: Makefile,v 1.112 2020/08/28 16:26:17 sjg Exp $
PROG= bmake
@@ -8,9 +8,11 @@ SRCS= \
compat.c \
cond.c \
dir.c \
+ enum.c \
for.c \
hash.c \
job.c \
+ lst.c \
main.c \
make.c \
make_malloc.c \
@@ -25,36 +27,6 @@ SRCS= \
util.c \
var.c
-# from lst.lib/
-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 \
- lstPrev.c \
- lstRemove.c \
- lstReplace.c \
- lstSucc.c
-
.-include "VERSION"
.-include "Makefile.inc"
@@ -97,7 +69,6 @@ COPTS.filemon_dev.c += -DHAVE_FILEMON_H -I${FILEMON_H:H}
.endif # USE_FILEMON
.PATH: ${srcdir}
-.PATH: ${srcdir}/lst.lib
.if make(obj) || make(clean)
SUBDIR+= unit-tests
@@ -109,7 +80,7 @@ SUBDIR+= unit-tests
# list of OS's which are derrived from BSD4.4
BSD44_LIST= NetBSD FreeBSD OpenBSD DragonFly MirBSD Bitrig
# we are...
-OS!= uname -s
+OS := ${.MAKE.OS:U${uname -s:L:sh}}
# are we 4.4BSD ?
isBSD44:=${BSD44_LIST:M${OS}}
@@ -237,5 +208,8 @@ install-mk:
# end-delete2
# A simple unit-test driver to help catch regressions
+TEST_MAKE ?= ${.OBJDIR}/${PROG:T}
accept test:
- cd ${.CURDIR}/unit-tests && MAKEFLAGS= ${.MAKE} -r -m / TEST_MAKE=${TEST_MAKE:U${.OBJDIR}/${PROG:T}} ${.TARGET}
+ cd ${.CURDIR}/unit-tests && \
+ MAKEFLAGS= ${TEST_MAKE} -r -m / ${.TARGET} ${TESTS:DTESTS=${TESTS:Q}}
+
diff --git a/contrib/bmake/VERSION b/contrib/bmake/VERSION
index 3c8b783c2861..b13b442cd4a4 100644
--- a/contrib/bmake/VERSION
+++ b/contrib/bmake/VERSION
@@ -1,2 +1,2 @@
# keep this compatible with sh and make
-_MAKE_VERSION=20200710
+_MAKE_VERSION=20200902
diff --git a/contrib/bmake/arch.c b/contrib/bmake/arch.c
index 9db7ce2bd14b..fa54908d8817 100644
--- a/contrib/bmake/arch.c
+++ b/contrib/bmake/arch.c
@@ -1,4 +1,4 @@
-/* $NetBSD: arch.c,v 1.73 2020/07/03 08:02:55 rillig Exp $ */
+/* $NetBSD: arch.c,v 1.107 2020/08/30 11:15:05 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -69,14 +69,14 @@
*/
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: arch.c,v 1.73 2020/07/03 08:02:55 rillig Exp $";
+static char rcsid[] = "$NetBSD: arch.c,v 1.107 2020/08/30 11:15:05 rillig Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)arch.c 8.2 (Berkeley) 1/2/94";
#else
-__RCSID("$NetBSD: arch.c,v 1.73 2020/07/03 08:02:55 rillig Exp $");
+__RCSID("$NetBSD: arch.c,v 1.107 2020/08/30 11:15:05 rillig Exp $");
#endif
#endif /* not lint */
#endif
@@ -94,7 +94,7 @@ __RCSID("$NetBSD: arch.c,v 1.73 2020/07/03 08:02:55 rillig Exp $");
* The interface to this module is:
* Arch_ParseArchive Given an archive specification, return a list
* of GNode's, one for each member in the spec.
- * FAILURE is returned if the specification is
+ * FALSE is returned if the specification is
* invalid for some reason.
*
* Arch_Touch Alter the modification time of the archive
@@ -184,12 +184,9 @@ typedef struct Arch {
size_t fnamesize; /* Size of the string table */
} Arch;
-static int ArchFindArchive(const void *, const void *);
-#ifdef CLEANUP
-static void ArchFree(void *);
-#endif
-static struct ar_hdr *ArchStatMember(char *, char *, Boolean);
-static FILE *ArchFindMember(char *, char *, struct ar_hdr *, const char *);
+static struct ar_hdr *ArchStatMember(const char *, const char *, Boolean);
+static FILE *ArchFindMember(const char *, const char *,
+ struct ar_hdr *, const char *);
#if defined(__svr4__) || defined(__SVR4) || defined(__ELF__)
#define SVR4ARCHIVES
static int ArchSVR4Entry(Arch *, char *, size_t, FILE *);
@@ -225,19 +222,6 @@ static int ArchSVR4Entry(Arch *, char *, size_t, FILE *);
#define AR_MAX_NAME_LEN (sizeof(arh.AR_NAME)-1)
#ifdef CLEANUP
-/*-
- *-----------------------------------------------------------------------
- * ArchFree --
- * Free memory used by an archive
- *
- * Results:
- * None.
- *
- * Side Effects:
- * None.
- *
- *-----------------------------------------------------------------------
- */
static void
ArchFree(void *ap)
{
@@ -272,23 +256,18 @@ ArchFree(void *ap)
* ctxt Context in which to expand variables
*
* Results:
- * SUCCESS if it was a valid specification. The linePtr is updated
+ * TRUE if it was a valid specification. The linePtr is updated
* to point to the first non-space after the archive spec. The
* nodes for the members are placed on the given list.
- *
- * Side Effects:
- * Some nodes may be created. The given list is extended.
- *
*-----------------------------------------------------------------------
*/
-ReturnStatus
+Boolean
Arch_ParseArchive(char **linePtr, Lst nodeLst, GNode *ctxt)
{
char *cp; /* Pointer into line */
GNode *gn; /* New node */
char *libName; /* Library-part of specification */
char *memName; /* Member-part of specification */
- char *nameBuf; /* temporary place for node name */
char saveChar; /* Ending delimiter of member-name */
Boolean subLibName; /* TRUE if libName should have/had
* variable substitution performed on it */
@@ -304,26 +283,25 @@ Arch_ParseArchive(char **linePtr, Lst nodeLst, GNode *ctxt)
* so we can safely advance beyond it...
*/
int length;
- void *freeIt;
- char *result;
-
- result = Var_Parse(cp, ctxt, VARF_UNDEFERR|VARF_WANTRES,
- &length, &freeIt);
- free(freeIt);
-
- if (result == var_Error) {
- return FAILURE;
- } else {
- subLibName = TRUE;
- }
-
- cp += length-1;
+ void *result_freeIt;
+ const char *result;
+ Boolean isError;
+
+ result = Var_Parse(cp, ctxt, VARE_UNDEFERR|VARE_WANTRES,
+ &length, &result_freeIt);
+ isError = result == var_Error;
+ free(result_freeIt);
+ if (isError)
+ return FALSE;
+
+ subLibName = TRUE;
+ cp += length - 1;
}
}
*cp++ = '\0';
if (subLibName) {
- libName = Var_Subst(NULL, libName, ctxt, VARF_UNDEFERR|VARF_WANTRES);
+ libName = Var_Subst(libName, ctxt, VARE_UNDEFERR|VARE_WANTRES);
}
@@ -347,18 +325,18 @@ Arch_ParseArchive(char **linePtr, Lst nodeLst, GNode *ctxt)
*/
int length;
void *freeIt;
- char *result;
+ const char *result;
+ Boolean isError;
- result = Var_Parse(cp, ctxt, VARF_UNDEFERR|VARF_WANTRES,
+ result = Var_Parse(cp, ctxt, VARE_UNDEFERR|VARE_WANTRES,
&length, &freeIt);
+ isError = result == var_Error;
free(freeIt);
- if (result == var_Error) {
- return FAILURE;
- } else {
- doSubst = TRUE;
- }
+ if (isError)
+ return FALSE;
+ doSubst = TRUE;
cp += length;
} else {
cp++;
@@ -372,7 +350,7 @@ Arch_ParseArchive(char **linePtr, Lst nodeLst, GNode *ctxt)
*/
if (*cp == '\0') {
printf("No closing parenthesis in archive specification\n");
- return FAILURE;
+ return FALSE;
}
/*
@@ -401,20 +379,15 @@ Arch_ParseArchive(char **linePtr, Lst nodeLst, GNode *ctxt)
char *buf;
char *sacrifice;
char *oldMemName = memName;
- size_t sz;
- memName = Var_Subst(NULL, memName, ctxt,
- VARF_UNDEFERR|VARF_WANTRES);
+ memName = Var_Subst(memName, ctxt, VARE_UNDEFERR | VARE_WANTRES);
/*
* Now form an archive spec and recurse to deal with nested
* variables and multi-word variable values.... The results
* are just placed at the end of the nodeLst we're returning.
*/
- sz = strlen(memName)+strlen(libName)+3;
- buf = sacrifice = bmake_malloc(sz);
-
- snprintf(buf, sz, "%s(%s)", libName, memName);
+ buf = sacrifice = str_concat4(libName, "(", memName, ")");
if (strchr(memName, '$') && strcmp(memName, oldMemName) == 0) {
/*
@@ -426,42 +399,43 @@ Arch_ParseArchive(char **linePtr, Lst nodeLst, GNode *ctxt)
if (gn == NULL) {
free(buf);
- return FAILURE;
+ return FALSE;
} else {
gn->type |= OP_ARCHV;
- (void)Lst_AtEnd(nodeLst, gn);
+ Lst_Append(nodeLst, gn);
}
- } else if (Arch_ParseArchive(&sacrifice, nodeLst, ctxt)!=SUCCESS) {
+ } else if (!Arch_ParseArchive(&sacrifice, nodeLst, ctxt)) {
/*
- * Error in nested call -- free buffer and return FAILURE
+ * Error in nested call -- free buffer and return FALSE
* ourselves.
*/
free(buf);
- return FAILURE;
+ return FALSE;
}
/*
* Free buffer and continue with our work.
*/
free(buf);
} else if (Dir_HasWildcards(memName)) {
- Lst members = Lst_Init(FALSE);
- char *member;
- size_t sz = MAXPATHLEN, nsz;
- nameBuf = bmake_malloc(sz);
+ Lst members = Lst_Init();
+ Buffer nameBuf;
+ Buf_Init(&nameBuf, 0);
Dir_Expand(memName, dirSearchPath, members);
while (!Lst_IsEmpty(members)) {
- member = (char *)Lst_DeQueue(members);
- nsz = strlen(libName) + strlen(member) + 3;
- if (sz > nsz)
- nameBuf = bmake_realloc(nameBuf, sz = nsz * 2);
+ char *member = Lst_Dequeue(members);
- snprintf(nameBuf, sz, "%s(%s)", libName, member);
+ Buf_Empty(&nameBuf);
+ Buf_AddStr(&nameBuf, libName);
+ Buf_AddStr(&nameBuf, "(");
+ Buf_AddStr(&nameBuf, member);
+ Buf_AddStr(&nameBuf, ")");
free(member);
- gn = Targ_FindNode(nameBuf, TARG_CREATE);
+
+ gn = Targ_FindNode(Buf_GetAll(&nameBuf, NULL), TARG_CREATE);
if (gn == NULL) {
- free(nameBuf);
- return FAILURE;
+ Buf_Destroy(&nameBuf, TRUE);
+ return FALSE;
} else {
/*
* We've found the node, but have to make sure the rest of
@@ -471,19 +445,24 @@ Arch_ParseArchive(char **linePtr, Lst nodeLst, GNode *ctxt)
* end of the provided list.
*/
gn->type |= OP_ARCHV;
- (void)Lst_AtEnd(nodeLst, gn);
+ Lst_Append(nodeLst, gn);
}
}
- Lst_Destroy(members, NULL);
- free(nameBuf);
+ Lst_Free(members);
+ Buf_Destroy(&nameBuf, TRUE);
} else {
- size_t sz = strlen(libName) + strlen(memName) + 3;
- nameBuf = bmake_malloc(sz);
- snprintf(nameBuf, sz, "%s(%s)", libName, memName);
- gn = Targ_FindNode(nameBuf, TARG_CREATE);
- free(nameBuf);
+ Buffer nameBuf;
+
+ Buf_Init(&nameBuf, 0);
+ Buf_AddStr(&nameBuf, libName);
+ Buf_AddStr(&nameBuf, "(");
+ Buf_AddStr(&nameBuf, memName);
+ Buf_AddStr(&nameBuf, ")");
+
+ gn = Targ_FindNode(Buf_GetAll(&nameBuf, NULL), TARG_CREATE);
+ Buf_Destroy(&nameBuf, TRUE);
if (gn == NULL) {
- return FAILURE;
+ return FALSE;
} else {
/*
* We've found the node, but have to make sure the rest of the
@@ -493,7 +472,7 @@ Arch_ParseArchive(char **linePtr, Lst nodeLst, GNode *ctxt)
* provided list.
*/
gn->type |= OP_ARCHV;
- (void)Lst_AtEnd(nodeLst, gn);
+ Lst_Append(nodeLst, gn);
}
}
if (doSubst) {
@@ -520,31 +499,15 @@ Arch_ParseArchive(char **linePtr, Lst nodeLst, GNode *ctxt)
} while (*cp != '\0' && isspace ((unsigned char)*cp));
*linePtr = cp;
- return SUCCESS;
+ return TRUE;
}
-/*-
- *-----------------------------------------------------------------------
- * ArchFindArchive --
- * See if the given archive is the one we are looking for. Called
- * From ArchStatMember and ArchFindMember via Lst_Find.
- *
- * Input:
- * ar Current list element
- * archName Name we want
- *
- * Results:
- * 0 if it is, non-zero if it isn't.
- *
- * Side Effects:
- * None.
- *
- *-----------------------------------------------------------------------
- */
-static int
-ArchFindArchive(const void *ar, const void *archName)
+/* See if the given archive is the one we are looking for.
+ * Called via Lst_Find. */
+static Boolean
+ArchFindArchive(const void *ar, const void *desiredName)
{
- return strcmp(archName, ((const Arch *)ar)->name);
+ return strcmp(((const Arch *)ar)->name, desiredName) == 0;
}
/*-
@@ -565,24 +528,20 @@ ArchFindArchive(const void *ar, const void *archName)
* archive members. This is mostly because we have no assurances that
* The archive will remain constant after we read all the headers, so
* there's not much point in remembering the position...
- *
- * Side Effects:
- *
*-----------------------------------------------------------------------
*/
static struct ar_hdr *
-ArchStatMember(char *archive, char *member, Boolean hash)
+ArchStatMember(const char *archive, const char *member, Boolean hash)
{
FILE * arch; /* Stream to archive */
- int size; /* Size of archive member */
- char *cp; /* Useful character pointer */
+ size_t size; /* Size of archive member */
char magic[SARMAG];
LstNode ln; /* Lst member containing archive descriptor */
Arch *ar; /* Archive descriptor */
Hash_Entry *he; /* Entry containing member's description */
struct ar_hdr arh; /* archive-member header for reading archive */
char memName[MAXPATHLEN+1];
- /* Current member name while hashing. */
+ /* Current member name while hashing. */
/*
* Because of space constraints and similar things, files are archived
@@ -590,14 +549,14 @@ ArchStatMember(char *archive, char *member, Boolean hash)
* to point 'member' to the final component, if there is one, to make
* the comparisons easier...
*/
- cp = strrchr(member, '/');
- if (cp != NULL) {
- member = cp + 1;
+ const char *base = strrchr(member, '/');
+ if (base != NULL) {
+ member = base + 1;
}
- ln = Lst_Find(archives, archive, ArchFindArchive);
+ ln = Lst_Find(archives, ArchFindArchive, archive);
if (ln != NULL) {
- ar = (Arch *)Lst_Datum(ln);
+ ar = LstNode_Datum(ln);
he = Hash_FindEntry(&ar->members, member);
@@ -610,8 +569,7 @@ ArchStatMember(char *archive, char *member, Boolean hash)
if (len > AR_MAX_NAME_LEN) {
len = AR_MAX_NAME_LEN;
- strncpy(copy, member, AR_MAX_NAME_LEN);
- copy[AR_MAX_NAME_LEN] = '\0';
+ snprintf(copy, sizeof copy, "%s", member);
}
if ((he = Hash_FindEntry(&ar->members, copy)) != NULL)
return (struct ar_hdr *)Hash_GetValue(he);
@@ -653,7 +611,7 @@ ArchStatMember(char *archive, char *member, Boolean hash)
* can handle...
*/
if ((fread(magic, SARMAG, 1, arch) != 1) ||
- (strncmp(magic, ARMAG, SARMAG) != 0)) {
+ (strncmp(magic, ARMAG, SARMAG) != 0)) {
fclose(arch);
return NULL;
}
@@ -673,6 +631,8 @@ ArchStatMember(char *archive, char *member, Boolean hash)
*/
goto badarch;
} else {
+ char *nameend;
+
/*
* We need to advance the stream's pointer to the start of the
* next header. Files are padded with newlines to an even-byte
@@ -680,13 +640,14 @@ ArchStatMember(char *archive, char *member, Boolean hash)
* '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);
+ size = (size_t)strtol(arh.ar_size, NULL, 10);
memcpy(memName, arh.AR_NAME, sizeof(arh.AR_NAME));
- for (cp = &memName[AR_MAX_NAME_LEN]; *cp == ' '; cp--) {
- continue;
+ nameend = memName + AR_MAX_NAME_LEN;
+ while (*nameend == ' ') {
+ nameend--;
}
- cp[1] = '\0';
+ nameend[1] = '\0';
#ifdef SVR4ARCHIVES
/*
@@ -706,8 +667,8 @@ ArchStatMember(char *archive, char *member, Boolean hash)
}
}
else {
- if (cp[0] == '/')
- cp[0] = '\0';
+ if (nameend[0] == '/')
+ nameend[0] = '\0';
}
#endif
@@ -719,11 +680,11 @@ ArchStatMember(char *archive, char *member, Boolean hash)
if (strncmp(memName, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 &&
isdigit((unsigned char)memName[sizeof(AR_EFMT1) - 1])) {
- unsigned int elen = atoi(&memName[sizeof(AR_EFMT1)-1]);
+ int elen = atoi(&memName[sizeof(AR_EFMT1)-1]);
- if (elen > MAXPATHLEN)
+ if ((unsigned int)elen > MAXPATHLEN)
goto badarch;
- if (fread(memName, elen, 1, arch) != 1)
+ if (fread(memName, (size_t)elen, 1, arch) != 1)
goto badarch;
memName[elen] = '\0';
if (fseek(arch, -elen, SEEK_CUR) != 0)
@@ -738,13 +699,13 @@ ArchStatMember(char *archive, char *member, Boolean hash)
Hash_SetValue(he, bmake_malloc(sizeof(struct ar_hdr)));
memcpy(Hash_GetValue(he), &arh, sizeof(struct ar_hdr));
}
- if (fseek(arch, (size + 1) & ~1, SEEK_CUR) != 0)
+ if (fseek(arch, ((long)size + 1) & ~1, SEEK_CUR) != 0)
goto badarch;
}
fclose(arch);
- (void)Lst_AtEnd(archives, ar);
+ Lst_Append(archives, ar);
/*
* Now that the archive has been read and cached, we can look into
@@ -774,17 +735,14 @@ badarch:
* If it is "//", then load the table of filenames
* If it is "/<offset>", then try to substitute the long file name
* from offset of a table previously read.
+ * If a table is read, the file pointer is moved to the next archive
+ * member.
*
* 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
@@ -834,7 +792,7 @@ ArchSVR4Entry(Arch *ar, char *name, size_t size, FILE *arch)
}
if (DEBUG(ARCH)) {
fprintf(debug_file, "Found svr4 archive name table with %lu entries\n",
- (unsigned long)entry);
+ (unsigned long)entry);
}
return 0;
}
@@ -861,8 +819,7 @@ ArchSVR4Entry(Arch *ar, char *name, size_t size, FILE *arch)
fprintf(debug_file, "Replaced %s with %s\n", name, &ar->fnametab[entry]);
}
- (void)strncpy(name, &ar->fnametab[entry], MAXPATHLEN);
- name[MAXPATHLEN] = '\0';
+ snprintf(name, MAXPATHLEN + 1, "%s", &ar->fnametab[entry]);
return 1;
}
#endif
@@ -874,6 +831,7 @@ ArchSVR4Entry(Arch *ar, char *name, size_t size, FILE *arch)
* Locate a member of an archive, given the path of the archive and
* the path of the desired member. If the archive is to be modified,
* the mode should be "r+", if not, it should be "r".
+ * The passed struct ar_hdr structure is filled in.
*
* Input:
* archive Path to the archive
@@ -886,21 +844,17 @@ ArchSVR4Entry(Arch *ar, char *name, size_t size, FILE *arch)
* An FILE *, opened for reading and writing, positioned at the
* start of the member's struct ar_hdr, or NULL if the member was
* nonexistent. The current struct ar_hdr for member.
- *
- * Side Effects:
- * The passed struct ar_hdr structure is filled in.
- *
*-----------------------------------------------------------------------
*/
static FILE *
-ArchFindMember(char *archive, char *member, struct ar_hdr *arhPtr,
+ArchFindMember(const char *archive, const char *member, struct ar_hdr *arhPtr,
const char *mode)
{
FILE * arch; /* Stream to archive */
int size; /* Size of archive member */
- char *cp; /* Useful character pointer */
char magic[SARMAG];
size_t len, tlen;
+ const char * base;
arch = fopen(archive, mode);
if (arch == NULL) {
@@ -912,7 +866,7 @@ ArchFindMember(char *archive, char *member, struct ar_hdr *arhPtr,
* can handle...
*/
if ((fread(magic, SARMAG, 1, arch) != 1) ||
- (strncmp(magic, ARMAG, SARMAG) != 0)) {
+ (strncmp(magic, ARMAG, SARMAG) != 0)) {
fclose(arch);
return NULL;
}
@@ -923,9 +877,9 @@ ArchFindMember(char *archive, char *member, struct ar_hdr *arhPtr,
* to point 'member' to the final component, if there is one, to make
* the comparisons easier...
*/
- cp = strrchr(member, '/');
- if (cp != NULL) {
- member = cp + 1;
+ base = strrchr(member, '/');
+ if (base != NULL) {
+ member = base + 1;
}
len = tlen = strlen(member);
if (len > sizeof(arhPtr->AR_NAME)) {
@@ -958,7 +912,7 @@ ArchFindMember(char *archive, char *member, struct ar_hdr *arhPtr,
* the file at the actual member, rather than its header, but
* not here...
*/
- if (fseek(arch, -sizeof(struct ar_hdr), SEEK_CUR) != 0) {
+ if (fseek(arch, -(long)sizeof(struct ar_hdr), SEEK_CUR) != 0) {
fclose(arch);
return NULL;
}
@@ -974,14 +928,14 @@ ArchFindMember(char *archive, char *member, struct ar_hdr *arhPtr,
sizeof(AR_EFMT1) - 1) == 0 &&
isdigit((unsigned char)arhPtr->AR_NAME[sizeof(AR_EFMT1) - 1])) {
- unsigned int elen = atoi(&arhPtr->AR_NAME[sizeof(AR_EFMT1)-1]);
+ int elen = atoi(&arhPtr->AR_NAME[sizeof(AR_EFMT1)-1]);
char ename[MAXPATHLEN + 1];
- if (elen > MAXPATHLEN) {
+ if ((unsigned int)elen > MAXPATHLEN) {
fclose(arch);
return NULL;
}
- if (fread(ename, elen, 1, arch) != 1) {
+ if (fread(ename, (size_t)elen, 1, arch) != 1) {
fclose(arch);
return NULL;
}
@@ -991,7 +945,7 @@ ArchFindMember(char *archive, char *member, struct ar_hdr *arhPtr,
}
if (strncmp(ename, member, len) == 0) {
/* Found as extended name */
- if (fseek(arch, -sizeof(struct ar_hdr) - elen,
+ if (fseek(arch, -(long)sizeof(struct ar_hdr) - elen,
SEEK_CUR) != 0) {
fclose(arch);
return NULL;
@@ -1035,18 +989,15 @@ skip:
*-----------------------------------------------------------------------
* Arch_Touch --
* Touch a member of an archive.
+ * The modification time of the entire archive is also changed.
+ * For a library, this could necessitate the re-ranlib'ing of the
+ * whole thing.
*
* Input:
* gn Node of member to touch
*
* Results:
* The 'time' field of the member's header is updated.
- *
- * Side Effects:
- * The modification time of the entire archive is also changed.
- * For a library, this could necessitate the re-ranlib'ing of the
- * whole thing.
- *
*-----------------------------------------------------------------------
*/
void
@@ -1060,8 +1011,8 @@ Arch_Touch(GNode *gn)
Var_Value(MEMBER, gn, &p2),
&arh, "r+");
- free(p1);
- free(p2);
+ bmake_free(p1);
+ bmake_free(p2);
snprintf(arh.AR_DATE, sizeof(arh.AR_DATE), "%-12ld", (long) now);
@@ -1071,37 +1022,24 @@ Arch_Touch(GNode *gn)
}
}
-/*-
- *-----------------------------------------------------------------------
- * Arch_TouchLib --
- * Given a node which represents a library, touch the thing, making
- * sure that the table of contents also is touched.
+/* Given a node which represents a library, touch the thing, making sure that
+ * the table of contents also is touched.
+ *
+ * Both the modification time of the library and of the RANLIBMAG member are
+ * set to 'now'.
*
* Input:
* gn The node of the library to touch
- *
- * Results:
- * None.
- *
- * Side Effects:
- * Both the modification time of the library and of the RANLIBMAG
- * member are set to 'now'.
- *
- *-----------------------------------------------------------------------
*/
void
-#if !defined(RANLIBMAG)
-Arch_TouchLib(GNode *gn MAKE_ATTR_UNUSED)
-#else
Arch_TouchLib(GNode *gn)
-#endif
{
#ifdef RANLIBMAG
FILE * arch; /* Stream open to archive */
struct ar_hdr arh; /* Header describing table of contents */
struct utimbuf times; /* Times for utime() call */
- arch = ArchFindMember(gn->path, UNCONST(RANLIBMAG), &arh, "r+");
+ arch = ArchFindMember(gn->path, RANLIBMAG, &arh, "r+");
snprintf(arh.AR_DATE, sizeof(arh.AR_DATE), "%-12ld", (long) now);
if (arch != NULL) {
@@ -1111,25 +1049,16 @@ Arch_TouchLib(GNode *gn)
times.actime = times.modtime = now;
utime(gn->path, &times);
}
+#else
+ (void)gn;
#endif
}
-/*-
- *-----------------------------------------------------------------------
- * Arch_MTime --
- * Return the modification time of a member of an archive.
+/* Return the modification time of a member of an archive. The mtime field
+ * of the given node is filled in with the value returned by the function.
*
* Input:
* gn Node describing archive member
- *
- * Results:
- * The modification time(seconds).
- *
- * Side Effects:
- * The mtime field of the given node is filled in with the value
- * returned by the function.
- *
- *-----------------------------------------------------------------------
*/
time_t
Arch_MTime(GNode *gn)
@@ -1142,8 +1071,8 @@ Arch_MTime(GNode *gn)
Var_Value(MEMBER, gn, &p2),
TRUE);
- free(p1);
- free(p2);
+ bmake_free(p1);
+ bmake_free(p2);
if (arhPtr != NULL) {
modTime = (time_t)strtol(arhPtr->AR_DATE, NULL, 10);
@@ -1155,34 +1084,17 @@ Arch_MTime(GNode *gn)
return modTime;
}
-/*-
- *-----------------------------------------------------------------------
- * Arch_MemMTime --
- * Given a non-existent archive member's node, get its modification
- * time from its archived form, if it exists.
- *
- * Results:
- * The modification time.
- *
- * Side Effects:
- * The mtime field is filled in.
- *
- *-----------------------------------------------------------------------
- */
+/* Given a non-existent archive member's node, get its modification time from
+ * its archived form, if it exists. gn->mtime is filled in as well. */
time_t
Arch_MemMTime(GNode *gn)
{
LstNode ln;
GNode *pgn;
- char *nameStart,
- *nameEnd;
- if (Lst_Open(gn->parents) != SUCCESS) {
- gn->mtime = 0;
- return 0;
- }
+ Lst_Open(gn->parents);
while ((ln = Lst_Next(gn->parents)) != NULL) {
- pgn = (GNode *)Lst_Datum(ln);
+ pgn = LstNode_Datum(ln);
if (pgn->type & OP_ARCHV) {
/*
@@ -1192,12 +1104,13 @@ Arch_MemMTime(GNode *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, ')');
+ const char *nameStart = strchr(pgn->name, '(') + 1;
+ const char *nameEnd = strchr(nameStart, ')');
+ size_t nameLen = (size_t)(nameEnd - nameStart);
if ((pgn->flags & REMAKE) &&
- strncmp(nameStart, gn->name, nameEnd - nameStart) == 0) {
- gn->mtime = Arch_MTime(pgn);
+ strncmp(nameStart, gn->name, nameLen) == 0) {
+ gn->mtime = Arch_MTime(pgn);
}
} else if (pgn->flags & REMAKE) {
/*
@@ -1214,29 +1127,20 @@ Arch_MemMTime(GNode *gn)
return gn->mtime;
}
-/*-
- *-----------------------------------------------------------------------
- * Arch_FindLib --
- * Search for a library along the given search path.
+/* Search for a library along the given search path.
+ *
+ * The node's 'path' field is set to the found path (including the
+ * actual file name, not -l...). If the system can handle the -L
+ * flag when linking (or we cannot find the library), we assume that
+ * the user has placed the .LIBS variable in the final linking
+ * command (or the linker will know where to find it) and set the
+ * TARGET variable for this node to be the node's name. Otherwise,
+ * we set the TARGET variable to be the full path of the library,
+ * as returned by Dir_FindFile.
*
* Input:
* gn Node of library to find
* path Search path
- *
- * Results:
- * None.
- *
- * Side Effects:
- * The node's 'path' field is set to the found path (including the
- * actual file name, not -l...). If the system can handle the -L
- * flag when linking (or we cannot find the library), we assume that
- * the user has placed the .LIBRARIES variable in the final linking
- * command (or the linker will know where to find it) and set the
- * TARGET variable for this node to be the node's name. Otherwise,
- * we set the TARGET variable to be the full path of the library,
- * as returned by Dir_FindFile.
- *
- *-----------------------------------------------------------------------
*/
void
Arch_FindLib(GNode *gn, Lst path)
@@ -1258,44 +1162,38 @@ Arch_FindLib(GNode *gn, Lst path)
#endif /* LIBRARIES */
}
-/*-
- *-----------------------------------------------------------------------
- * Arch_LibOODate --
- * Decide if a node with the OP_LIB attribute is out-of-date. Called
- * from Make_OODate to make its life easier.
- *
- * There are several ways for a library to be out-of-date that are
- * not available to ordinary files. In addition, there are ways
- * that are open to regular files that are not available to
- * libraries. A library that is only used as a source is never
- * considered out-of-date by itself. This does not preclude the
- * library's modification time from making its parent be out-of-date.
- * A library will be considered out-of-date for any of these reasons,
- * given that it is a target on a dependency line somewhere:
- * Its modification time is less than that of one of its
- * sources (gn->mtime < gn->cmgn->mtime).
- * Its modification time is greater than the time at which the
- * make began (i.e. it's been modified in the course
- * of the make, probably by archiving).
- * The modification time of one of its sources is greater than
- * the one of its RANLIBMAG member (i.e. its table of contents
- * is out-of-date). We don't compare of the archive time
- * vs. TOC time because they can be too close. In my
- * opinion we should not bother with the TOC at all since
- * this is used by 'ar' rules that affect the data contents
- * of the archive, not by ranlib rules, which affect the
- * TOC.
+/* Decide if a node with the OP_LIB attribute is out-of-date. Called from
+ * Make_OODate to make its life easier.
+ * The library will be hashed if it hasn't been already.
+ *
+ * There are several ways for a library to be out-of-date that are
+ * not available to ordinary files. In addition, there are ways
+ * that are open to regular files that are not available to
+ * libraries. A library that is only used as a source is never
+ * considered out-of-date by itself. This does not preclude the
+ * library's modification time from making its parent be out-of-date.
+ * A library will be considered out-of-date for any of these reasons,
+ * given that it is a target on a dependency line somewhere:
+ *
+ * Its modification time is less than that of one of its sources
+ * (gn->mtime < gn->cmgn->mtime).
+ *
+ * Its modification time is greater than the time at which the make
+ * began (i.e. it's been modified in the course of the make, probably
+ * by archiving).
+ *
+ * The modification time of one of its sources is greater than the one
+ * of its RANLIBMAG member (i.e. its table of contents is out-of-date).
+ * We don't compare of the archive time vs. TOC time because they can be
+ * too close. In my opinion we should not bother with the TOC at all
+ * since this is used by 'ar' rules that affect the data contents of the
+ * archive, not by ranlib rules, which affect the TOC.
*
* Input:
* gn The library's graph node
*
* Results:
* TRUE if the library is out-of-date. FALSE otherwise.
- *
- * Side Effects:
- * The library will be hashed if it hasn't been already.
- *
- *-----------------------------------------------------------------------
*/
Boolean
Arch_LibOODate(GNode *gn)
@@ -1315,7 +1213,7 @@ Arch_LibOODate(GNode *gn)
struct ar_hdr *arhPtr; /* Header for __.SYMDEF */
int modTimeTOC; /* The table-of-contents's mod time */
- arhPtr = ArchStatMember(gn->path, UNCONST(RANLIBMAG), FALSE);
+ arhPtr = ArchStatMember(gn->path, RANLIBMAG, FALSE);
if (arhPtr != NULL) {
modTimeTOC = (int)strtol(arhPtr->AR_DATE, NULL, 10);
@@ -1340,40 +1238,14 @@ Arch_LibOODate(GNode *gn)
return oodate;
}
-/*-
- *-----------------------------------------------------------------------
- * Arch_Init --
- * Initialize things for this module.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * The 'archives' list is initialized.
- *
- *-----------------------------------------------------------------------
- */
+/* Initialize things for this module. */
void
Arch_Init(void)
{
- archives = Lst_Init(FALSE);
+ archives = Lst_Init();
}
-
-
-/*-
- *-----------------------------------------------------------------------
- * Arch_End --
- * Cleanup things for this module.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * The 'archives' list is freed
- *
- *-----------------------------------------------------------------------
- */
+/* Clean up things for this module. */
void
Arch_End(void)
{
@@ -1382,35 +1254,22 @@ Arch_End(void)
#endif
}
-/*-
- *-----------------------------------------------------------------------
- * Arch_IsLib --
- * Check if the node is a library
- *
- * Results:
- * True or False.
- *
- * Side Effects:
- * None.
- *
- *-----------------------------------------------------------------------
- */
-int
+Boolean
Arch_IsLib(GNode *gn)
{
static const char armag[] = "!<arch>\n";
- char buf[sizeof(armag)-1];
+ 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)) {
+ if (read(fd, buf, sizeof buf) != sizeof buf) {
(void)close(fd);
return FALSE;
}
(void)close(fd);
- return memcmp(buf, armag, sizeof(buf)) == 0;
+ return memcmp(buf, armag, sizeof buf) == 0;
}
diff --git a/contrib/bmake/bmake.1 b/contrib/bmake/bmake.1
index c468cfe053d7..2cc452f2d171 100644
--- a/contrib/bmake/bmake.1
+++ b/contrib/bmake/bmake.1
@@ -1,4 +1,4 @@
-.\" $NetBSD: make.1,v 1.282 2020/06/06 20:28:42 wiz Exp $
+.\" $NetBSD: make.1,v 1.289 2020/08/28 17:15:04 rillig Exp $
.\"
.\" Copyright (c) 1990, 1993
.\" The Regents of the University of California. All rights reserved.
@@ -29,7 +29,7 @@
.\"
.\" from: @(#)make.1 8.4 (Berkeley) 3/19/94
.\"
-.Dd June 5, 2020
+.Dd August 28, 2020
.Dt BMAKE 1
.Os
.Sh NAME
@@ -166,8 +166,15 @@ 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 h
+Print debugging information about hash table operations.
.It Ar j
Print debugging information about running multiple shells.
+.It Ar L
+Turn on lint checks.
+This will throw errors for variable assignments that do not parse
+correctly, at the time of assignment so the file and line number
+are available.
.It Ar l
Print commands in Makefiles regardless of whether or not they are prefixed by
.Ql @
@@ -311,7 +318,8 @@ 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).
+source (see below) or the command is prefixed with
+.Ql Ic + .
.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
@@ -412,37 +420,44 @@ 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:
+and are customarily created from them.
+A target is considered out-of-date if it does not exist, or if its
+modification time is less than that of any of its sources.
+An out-of-date target will be re-created, but not until all sources
+have been examined and themselves re-created as needed.
+Three operators may be used:
.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
+Many dependency lines may name this target but only one may have
+attached shell commands.
+All sources named in all dependency lines are considered together,
+and if needed the attached shell commands are run to create or
+re-create the target.
+If
.Nm
-is interrupted.
+is interrupted, the target is removed.
.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.
+The same, but the target is always re-created whether or not it is out
+of date.
.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
+Any dependency line may have attached shell commands, but each one
+is handled independently: its sources are considered and the attached
+shell commands are run if the target is out of date with respect to
+(only) those sources.
+Thus, different groups of the attached shell commands may be run
+depending on the circumstances.
+Furthermore, unlike
+.Ic \&:,
+for dependency lines with no sources, the attached shell
+commands are always run.
+Also unlike
+.Ic \&:,
+the target will not be removed if
.Nm
is interrupted.
.El
+All dependency lines mentioning a particular target must use the same
+operator.
.Pp
Targets and sources may contain the shell wildcard values
.Ql \&? ,
@@ -608,7 +623,7 @@ 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!
+braces, parentheses, or whitespace are really best avoided!
.Pp
If the result of expanding a variable contains a dollar sign
.Pq Ql \&$
@@ -1126,6 +1141,9 @@ is set to the value of
for all programs which
.Nm
executes.
+.It Ev .SHELL
+The pathname of the shell used to run target scripts.
+It is read-only.
.It Ev .TARGETS
The list of targets explicitly specified on the command line, if any.
.It Ev VPATH
@@ -1171,7 +1189,7 @@ 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
+Selects only those words that match
.Ar pattern .
The standard shell wildcard characters
.Pf ( Ql * ,
@@ -1195,11 +1213,11 @@ This is identical to
but selects all words which do not match
.Ar pattern .
.It Cm \&:O
-Order every word in variable alphabetically.
+Orders every word in variable alphabetically.
.It Cm \&:Or
-Order every word in variable in reverse alphabetical order.
+Orders every word in variable in reverse alphabetical order.
.It Cm \&:Ox
-Randomize words in variable.
+Shuffles the 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 \&:=
@@ -1249,7 +1267,7 @@ If a
.Va utc
value is not provided or is 0, the current time is used.
.It Cm \&:hash
-Compute a 32-bit hash of the value and encode it as hex digits.
+Computes a 32-bit hash of the value and encode it as hex digits.
.It Cm \&:localtime[=utc]
The value is a format string for
.Xr strftime 3 ,
@@ -1259,7 +1277,7 @@ If a
.Va utc
value is not provided or is 0, the current time is used.
.It Cm \&:tA
-Attempt to convert variable to an absolute path using
+Attempts to convert variable to an absolute path using
.Xr realpath 3 ,
if that fails, the value is unchanged.
.It Cm \&:tl
@@ -1271,7 +1289,7 @@ This modifier sets the separator to the character
If
.Ar c
is omitted, then no separator is used.
-The common escapes (including octal numeric codes), work as expected.
+The common escapes (including octal numeric codes) work as expected.
.It Cm \&:tu
Converts variable to upper-case letters.
.It Cm \&:tW
@@ -1287,21 +1305,21 @@ See also
.Sm off
.It Cm \&:S No \&/ Ar old_string No \&/ Ar new_string No \&/ Op Cm 1gW
.Sm on
-Modify the first occurrence of
+Modifies the first occurrence of
.Ar old_string
-in the variable's value, replacing it with
+in each word of 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
+is appended to the last delimiter 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 appended to the last delimiter of the pattern, only the first occurrence
is affected.
If a
.Ql W
-is appended to the last slash of the pattern,
+is appended to the last delimiter of the pattern,
then the value is treated as a single word
(possibly containing embedded white space).
If
@@ -1370,13 +1388,6 @@ 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.
.Pp
As for the
.Cm \&:S
@@ -1387,9 +1398,9 @@ and
are subjected to variable expansion before being parsed as
regular expressions.
.It Cm \&:T
-Replaces each word in the variable with its last component.
+Replaces each word in the variable with its last path component.
.It Cm \&:u
-Remove adjacent duplicate words (like
+Removes adjacent duplicate words (like
.Xr uniq 1 ) .
.Sm off
.It Cm \&:\&? Ar true_string Cm \&: Ar false_string
@@ -1405,7 +1416,7 @@ 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:
+to determine if any words match "42" you need to use something like:
.Dl ${"${NUMBERS:M42}" != \&"\&":?match:no} .
.It Ar :old_string=new_string
This is the
@@ -1449,7 +1460,7 @@ in either the
or
.Ar old_string ,
only the first instance is treated specially (as the pattern character);
-all subsequent instances are treated as regular characters
+all subsequent instances are treated as regular characters.
.Pp
Variable expansion occurs in the normal fashion inside both
.Ar old_string
@@ -1466,11 +1477,10 @@ 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
+loops, expansion occurs at the time of reference.
+Assigns
.Ar temp
-to each word in the variable and evaluate
+to each word in the variable and evaluates
.Ar string .
The ODE convention is that
.Ar temp
@@ -1481,7 +1491,7 @@ For example.
However a single character variable is often more readable:
.Dl ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@}
.It Cm \&:_[=var]
-Save the current variable value in
+Saves the current variable value in
.Ql $_
or the named
.Va var
@@ -1502,7 +1512,7 @@ is used to save the result of the
modifier which is later referenced using the index values from
.Ql :range .
.It Cm \&:U Ns Ar newval
-If the variable is undefined
+If the variable is undefined,
.Ar newval
is the value.
If the variable is defined, the existing value is returned.
@@ -1512,7 +1522,7 @@ It is handy for setting per-target CFLAGS for instance:
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
+If the variable is defined,
.Ar newval
is the value.
.It Cm \&:L
@@ -1641,7 +1651,7 @@ 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
+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
@@ -1687,7 +1697,7 @@ The possible conditionals are as follows:
The message is printed along with the name of the makefile and line number,
then
.Nm
-will exit.
+will exit immediately.
.It Ic .export Ar variable ...
Export the specified global variable.
If no variable list is provided, all globals are exported
@@ -1876,7 +1886,7 @@ 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
+variable is being compared against 0, or an empty string in the case
of a string comparison.
.Pp
When
@@ -1917,7 +1927,7 @@ 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 Aq make-lines
.It Ic \&.endfor
.El
.Pp
@@ -1929,7 +1939,7 @@ On each iteration of the loop, one word is taken and assigned to each
in order, and these
.Ic variables
are substituted into the
-.Ic make-rules
+.Ic make-lines
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
diff --git a/contrib/bmake/bmake.cat1 b/contrib/bmake/bmake.cat1
index feb93698e34f..564e811737da 100644
--- a/contrib/bmake/bmake.cat1
+++ b/contrib/bmake/bmake.cat1
@@ -89,9 +89,15 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
g3 Print the input graph before exiting on error.
+ h Print debugging information about hash table operations.
+
j Print debugging information about running multiple
shells.
+ L Turn on lint checks. This will throw errors for variable
+ assignments that do not parse correctly, at the time of
+ assignment so the file and line number are available.
+
l Print commands in Makefiles regardless of whether or not
they are prefixed by `@' or other "quiet" flags. Also
known as "loud" behavior.
@@ -188,7 +194,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
-n 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).
+ cial source (see below) or the command is prefixed with `+'.
-N Display the commands which would have been executed, but do not
actually execute any of them; useful for debugging top-level
@@ -260,25 +266,31 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
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 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 bmake 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 bmake 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 bmake is interrupted.
+ on the sources and are customarily created from them. A target is con-
+ sidered out-of-date if it does not exist, or if its modification time is
+ less than that of any of its sources. An out-of-date target will be re-
+ created, but not until all sources have been examined and themselves re-
+ created as needed. Three operators may be used:
+
+ : Many dependency lines may name this target but only one may have
+ attached shell commands. All sources named in all dependency lines
+ are considered together, and if needed the attached shell commands
+ are run to create or re-create the target. If bmake is inter-
+ rupted, the target is removed.
+
+ ! The same, but the target is always re-created whether or not it is
+ out of date.
+
+ :: Any dependency line may have attached shell commands, but each one
+ is handled independently: its sources are considered and the
+ attached shell commands are run if the target is out of date with
+ respect to (only) those sources. Thus, different groups of the
+ attached shell commands may be run depending on the circumstances.
+ Furthermore, unlike :, for dependency lines with no sources, the
+ attached shell commands are always run. Also unlike :, the target
+ will not be removed if bmake is interrupted.
+ All dependency lines mentioning a particular target must use the same
+ operator.
Targets and sources may contain the shell wildcard values `?', `*', `[]',
and `{}'. The values `?', `*', and `[]' may only be used as part of the
@@ -374,7 +386,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
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
+ taining dollar, braces, parentheses, or whitespace are really best
avoided!
If the result of expanding a variable contains a dollar sign (`$') the
@@ -729,6 +741,9 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
contains a variable transform. `PWD' is set to the value
of `.OBJDIR' for all programs which bmake executes.
+ .SHELL The pathname of the shell used to run target scripts. It
+ is read-only.
+
.TARGETS The list of targets explicitly specified on the command
line, if any.
@@ -765,7 +780,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
ponent.
:Mpattern
- Select only those words that match pattern. The standard shell
+ Selects only those words that match pattern. The standard shell
wildcard characters (`*', `?', and `[]') may be used. The wildcard
characters may be escaped with a backslash (`\'). As a consequence
of the way values are split into words, matched, and then joined, a
@@ -779,11 +794,11 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
This is identical to `:M', but selects all words which do not match
pattern.
- :O Order every word in variable alphabetically.
+ :O Orders every word in variable alphabetically.
- :Or Order every word in variable in reverse alphabetical order.
+ :Or Orders every word in variable in reverse alphabetical order.
- :Ox Randomize words in variable. The results will be different each
+ :Ox Shuffles the 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 behavior. For example,
@@ -821,13 +836,13 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
utc value is not provided or is 0, the current time is used.
:hash
- Compute a 32-bit hash of the value and encode it as hex digits.
+ Computes a 32-bit hash of the value and encode it as hex digits.
:localtime[=utc]
The value is a format string for strftime(3), using localtime(3).
If a utc value is not provided or is 0, the current time is used.
- :tA Attempt to convert variable to an absolute path using realpath(3),
+ :tA Attempts to convert variable to an absolute path using realpath(3),
if that fails, the value is unchanged.
:tl Converts variable to lower-case letters.
@@ -836,7 +851,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
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.
+ octal numeric codes) work as expected.
:tu Converts variable to upper-case letters.
@@ -847,20 +862,20 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
white space. See also `:[@]'.
:S/old_string/new_string/[1gW]
- Modify the first occurrence of old_string in the variable's value,
- replacing it with new_string. 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 old_string begins with a caret
- (`^'), old_string is anchored at the beginning of each word. If
- old_string ends with a dollar sign (`$'), it is anchored at the end
- of each word. Inside new_string, an ampersand (`&') is replaced by
- old_string (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
- (`\').
+ Modifies the first occurrence of old_string in each word of the
+ variable's value, replacing it with new_string. If a `g' is
+ appended to the last delimiter of the pattern, all occurrences in
+ each word are replaced. If a `1' is appended to the last delimiter
+ of the pattern, only the first occurrence is affected. If a `W' is
+ appended to the last delimiter of the pattern, then the value is
+ treated as a single word (possibly containing embedded white space).
+ If old_string begins with a caret (`^'), old_string is anchored at
+ the beginning of each word. If old_string ends with a dollar sign
+ (`$'), it is anchored at the end of each word. Inside new_string,
+ an ampersand (`&') is replaced by old_string (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
old_string and new_string with the single exception that a backslash
@@ -878,16 +893,13 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
instances of the search pattern pattern as occur in the word or
words it is found in; the `W' modifier causes the value to be
treated as a single 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.
As for the :S modifier, the pattern and replacement are subjected to
variable expansion before being parsed as regular expressions.
- :T Replaces each word in the variable with its last component.
+ :T Replaces each word in the variable with its last path component.
- :u Remove adjacent duplicate words (like uniq(1)).
+ :u Removes adjacent duplicate words (like uniq(1)).
:?true_string:false_string
If the variable name (not its value), when parsed as a .if condi-
@@ -898,7 +910,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
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
+ which actually tests defined(NUMBERS), to determine if any words
match "42" you need to use something like:
${"${NUMBERS:M42}" != "":?match:no}.
@@ -916,7 +928,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
pattern matching character (%) in either the new_string or
old_string, only the first instance is treated specially (as the
pattern character); all subsequent instances are treated as regular
- characters
+ characters.
Variable expansion occurs in the normal fashion inside both
old_string and new_string with the single exception that a backslash
@@ -925,17 +937,17 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
:@temp@string@
This is the loop expansion mechanism from the OSF Development Envi-
- ronment (ODE) make. Unlike .for loops expansion occurs at the time
- of reference. Assign temp to each word in the variable and evaluate
- string. The ODE convention is that temp should start and end with a
- period. For example.
+ ronment (ODE) make. Unlike .for loops, expansion occurs at the time
+ of reference. Assigns temp to each word in the variable and evalu-
+ ates string. The ODE convention is that temp should start and end
+ with a period. For example.
${LINKS:@.LINK.@${LN} ${TARGET} ${.LINK.}@}
However a single character variable is often more readable:
${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@}
:_[=var]
- Save the current variable value in `$_' or the named var for later
+ Saves the current variable value in `$_' or the named var for later
reference. Example usage:
M_cmpv.units = 1 1000 1000000
@@ -948,7 +960,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
later referenced using the index values from `:range'.
:Unewval
- If the variable is undefined newval is the value. If the variable
+ If the variable is undefined, 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:
@@ -957,7 +969,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
${VAR:D:Unewval}
:Dnewval
- If the variable is defined newval is the value.
+ If the variable is defined, newval is the value.
:L The name of the variable is the value.
@@ -1033,7 +1045,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
# Returns the number of words in the value.
INCLUDE STATEMENTS, CONDITIONALS AND FOR LOOPS
- Makefile inclusion, conditional structures and for loops reminiscent of
+ Makefile inclusion, conditional structures and for loops reminiscent of
the C programming language are provided in bmake. All such structures
are identified by a line beginning with a single dot (`.') character.
Files are included with either .include <file> or .include "file". Vari-
@@ -1057,7 +1069,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
.error message
The message is printed along with the name of the makefile and
- line number, then bmake will exit.
+ line number, then bmake will exit immediately.
.export variable ...
Export the specified global variable. If no variable list is
@@ -1191,7 +1203,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
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
+ is assumed that the expanded variable is being compared against 0, or an
empty string in the case of a string comparison.
When bmake is evaluating one of these conditional expressions, and it
@@ -1210,12 +1222,12 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
The syntax of a for loop is:
.for variable [variable ...] in expression
- <make-rules>
+ <make-lines>
.endfor
After the for expression is evaluated, it is split into words. On each
iteration of the loop, one word is taken and assigned to each variable,
- in order, and these variables are substituted into the make-rules inside
+ in order, and these variables are substituted into the make-lines 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.
@@ -1556,4 +1568,4 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
There is no way of escaping a space character in a filename.
-FreeBSD 11.3 June 5, 2020 FreeBSD 11.3
+FreeBSD 11.3 August 28, 2020 FreeBSD 11.3
diff --git a/contrib/bmake/bsd.after-import.mk b/contrib/bmake/bsd.after-import.mk
index 23f30aa6c638..0d48f3c26648 100644
--- a/contrib/bmake/bsd.after-import.mk
+++ b/contrib/bmake/bsd.after-import.mk
@@ -1,4 +1,4 @@
-# $Id: bsd.after-import.mk,v 1.15 2018/12/30 17:14:24 sjg Exp $
+# $Id: bsd.after-import.mk,v 1.16 2020/07/12 03:39:01 sjg Exp $
# This makefile is for use when integrating bmake into a BSD build
# system. Use this makefile after importing bmake.
@@ -9,7 +9,7 @@
# The goal is to allow the benefits of autoconf without
# the overhead of running configure.
-all: _makefile
+all: _makefile _utmakefile
all: after-import
# we rely on bmake
@@ -37,7 +37,9 @@ SRCTOP := ${srctop}
.endif
# This lets us match what boot-strap does
-.if !defined(HOST_OS)
+.if defined(.MAKE.OS)
+HOST_OS:= ${.MAKE.OS}
+.elif !defined(HOST_OS)
HOST_OS!= uname
.endif
@@ -107,5 +109,18 @@ _makefile: bootstrap ${MAKEFILE}
@cmp -s ${.TARGET} ${.CURDIR}/Makefile || \
mv ${.TARGET} ${.CURDIR}/Makefile
+_utmakefile: bootstrap ${MAKEFILE}
+ @echo Generating ${.CURDIR}/unit-tests/Makefile
+ @mkdir -p ${.CURDIR}/unit-tests
+ @(echo '# This is a generated file, do NOT edit!'; \
+ echo '# See ${_this:S,${SRCTOP}/,,}'; \
+ echo '#'; echo '# $$${HOST_OS}$$'; \
+ ${MAKEFILE_SED} \
+ -e '/^UNIT_TESTS/s,=.*,= $${srcdir},' \
+ ${BMAKE_SRC}/unit-tests/Makefile ) > ${.TARGET}
+ @cmp -s ${.TARGET} ${.CURDIR}/unit-tests/Makefile || \
+ mv ${.TARGET} ${.CURDIR}/unit-tests/Makefile
+
+
.include <bsd.obj.mk>
diff --git a/contrib/bmake/buf.c b/contrib/bmake/buf.c
index 360b8cedde13..f96d8fbf9792 100644
--- a/contrib/bmake/buf.c
+++ b/contrib/bmake/buf.c
@@ -1,4 +1,4 @@
-/* $NetBSD: buf.c,v 1.26 2020/07/03 08:02:55 rillig Exp $ */
+/* $NetBSD: buf.c,v 1.37 2020/08/23 08:21:50 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -70,173 +70,123 @@
*/
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: buf.c,v 1.26 2020/07/03 08:02:55 rillig Exp $";
+static char rcsid[] = "$NetBSD: buf.c,v 1.37 2020/08/23 08:21:50 rillig Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)buf.c 8.1 (Berkeley) 6/6/93";
#else
-__RCSID("$NetBSD: buf.c,v 1.26 2020/07/03 08:02:55 rillig Exp $");
+__RCSID("$NetBSD: buf.c,v 1.37 2020/08/23 08:21:50 rillig 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
+/* Functions for automatically-expanded null-terminated buffers. */
-#define BUF_DEF_SIZE 256 /* Default buffer size */
+#include <limits.h>
+#include "make.h"
-/*-
- *-----------------------------------------------------------------------
- * Buf_Expand_1 --
- * Extend buffer for single byte add.
- *
- *-----------------------------------------------------------------------
- */
+/* Extend the buffer for adding a single byte. */
void
Buf_Expand_1(Buffer *bp)
{
- bp->size += max(bp->size, 16);
+ 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?
- *
- *-----------------------------------------------------------------------
- */
+/* Add the given bytes to the buffer. */
void
-Buf_AddBytes(Buffer *bp, int numBytes, const Byte *bytesPtr)
+Buf_AddBytes(Buffer *bp, const char *bytesPtr, size_t numBytes)
{
- int count = bp->count;
- Byte *ptr;
+ size_t count = bp->count;
+ char *ptr;
if (__predict_false(count + numBytes >= bp->size)) {
- bp->size += max(bp->size, numBytes + 16);
+ 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);
+ ptr[numBytes] = '\0';
}
-/*-
- *-----------------------------------------------------------------------
- * Buf_GetAll --
- * Get all the available data at once.
- *
- * Results:
- * A pointer to the data and the number of bytes available.
- *
- * Side Effects:
- * None.
- *
- *-----------------------------------------------------------------------
- */
-Byte *
-Buf_GetAll(Buffer *bp, int *numBytesPtr)
+/* Add the bytes between start and end to the buffer. */
+void
+Buf_AddBytesBetween(Buffer *bp, const char *start, const char *end)
{
+ Buf_AddBytes(bp, start, (size_t)(end - start));
+}
+
+/* Add the given string to the buffer. */
+void
+Buf_AddStr(Buffer *bp, const char *str)
+{
+ Buf_AddBytes(bp, str, strlen(str));
+}
+/* Add the given number to the buffer. */
+void
+Buf_AddInt(Buffer *bp, int n)
+{
+ enum {
+ bits = sizeof(int) * CHAR_BIT,
+ max_octal_digits = (bits + 2) / 3,
+ max_decimal_digits = /* at most */ max_octal_digits,
+ max_sign_chars = 1,
+ buf_size = max_sign_chars + max_decimal_digits + 1
+ };
+ char buf[buf_size];
+
+ size_t len = (size_t)snprintf(buf, sizeof buf, "%d", n);
+ Buf_AddBytes(bp, buf, len);
+}
+
+/* Get the data (usually a string) from the buffer.
+ * The returned data is valid until the next modifying operation
+ * on the buffer.
+ *
+ * Returns the pointer to the data and optionally the length of the
+ * data in the buffer. */
+char *
+Buf_GetAll(Buffer *bp, size_t *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.
- *
- *-----------------------------------------------------------------------
- */
+/* Mark the buffer as empty, so it can be filled with data again. */
void
Buf_Empty(Buffer *bp)
{
-
bp->count = 0;
- *bp->buffer = 0;
+ bp->buffer[0] = '\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.
- *
- *-----------------------------------------------------------------------
- */
+/* Initialize a buffer.
+ * If the given initial size is 0, a reasonable default is used. */
void
-Buf_Init(Buffer *bp, int size)
+Buf_Init(Buffer *bp, size_t size)
{
if (size <= 0) {
- size = BUF_DEF_SIZE;
+ size = 256;
}
bp->size = size;
bp->count = 0;
bp->buffer = bmake_malloc(size);
- *bp->buffer = 0;
+ bp->buffer[0] = '\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 *
+/* Reset the buffer.
+ * If freeData is TRUE, the data from the buffer is freed as well.
+ * Otherwise it is kept and returned. */
+char *
Buf_Destroy(Buffer *buf, Boolean freeData)
{
- Byte *data;
-
- data = buf->buffer;
+ char *data = buf->buffer;
if (freeData) {
free(data);
data = NULL;
@@ -249,42 +199,24 @@ Buf_Destroy(Buffer *buf, Boolean freeData)
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 */
+# define BUF_COMPACT_LIMIT 128 /* worthwhile saving */
#endif
-Byte *
+/* Reset the buffer and return its data.
+ *
+ * If the buffer size is much greater than its content,
+ * a new buffer will be allocated and the old one freed. */
+char *
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;
- }
+ char *data = bmake_realloc(buf->buffer, buf->count + 1);
+ data[buf->count] = '\0';
+ Buf_Destroy(buf, FALSE);
+ return data;
}
#endif
return Buf_Destroy(buf, FALSE);
diff --git a/contrib/bmake/buf.h b/contrib/bmake/buf.h
index 7bd2d2b7940b..1dc3cdbf58f3 100644
--- a/contrib/bmake/buf.h
+++ b/contrib/bmake/buf.h
@@ -1,4 +1,4 @@
-/* $NetBSD: buf.h,v 1.19 2017/05/31 22:02:06 maya Exp $ */
+/* $NetBSD: buf.h,v 1.28 2020/09/01 17:38:26 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -72,48 +72,54 @@
* from: @(#)buf.h 8.1 (Berkeley) 6/6/93
*/
-/*-
- * buf.h --
- * Header for users of the buf library.
- */
+/* Automatically growing null-terminated buffers of characters. */
#ifndef MAKE_BUF_H
#define MAKE_BUF_H
-typedef char Byte;
+#include <stddef.h>
+/* An automatically growing null-terminated buffer of characters. */
typedef struct Buffer {
- int size; /* Current size of the buffer */
- int count; /* Number of bytes in buffer */
- Byte *buffer; /* The buffer itself (zero terminated) */
+ size_t size; /* Allocated size of the buffer, including the null */
+ size_t count; /* Number of bytes in buffer, excluding the null */
+ char *buffer; /* The buffer itself (always null-terminated) */
} Buffer;
-/* If we aren't on netbsd, __predict_false() might not be defined. */
+/* 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)
+void Buf_Expand_1(Buffer *);
-#define BUF_ERROR 256
+/* Buf_AddByte adds a single byte to a buffer. */
+static inline void MAKE_ATTR_UNUSED
+Buf_AddByte(Buffer *bp, char byte)
+{
+ size_t 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;
+}
-#define Buf_Size(bp) ((bp)->count)
+static inline size_t MAKE_ATTR_UNUSED
+Buf_Size(const Buffer *bp)
+{
+ return bp->count;
+}
-void Buf_Expand_1(Buffer *);
-void Buf_AddBytes(Buffer *, int, const Byte *);
-Byte *Buf_GetAll(Buffer *, int *);
+void Buf_AddBytes(Buffer *, const char *, size_t);
+void Buf_AddBytesBetween(Buffer *, const char *, const char *);
+void Buf_AddStr(Buffer *, const char *);
+void Buf_AddInt(Buffer *, int);
+char *Buf_GetAll(Buffer *, size_t *);
void Buf_Empty(Buffer *);
-void Buf_Init(Buffer *, int);
-Byte *Buf_Destroy(Buffer *, Boolean);
-Byte *Buf_DestroyCompact(Buffer *);
+void Buf_Init(Buffer *, size_t);
+char *Buf_Destroy(Buffer *, Boolean);
+char *Buf_DestroyCompact(Buffer *);
#endif /* MAKE_BUF_H */
diff --git a/contrib/bmake/compat.c b/contrib/bmake/compat.c
index cd88884736f1..150696ad6713 100644
--- a/contrib/bmake/compat.c
+++ b/contrib/bmake/compat.c
@@ -1,4 +1,4 @@
-/* $NetBSD: compat.c,v 1.113 2020/07/03 08:13:23 rillig Exp $ */
+/* $NetBSD: compat.c,v 1.139 2020/08/30 20:08:47 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -70,14 +70,14 @@
*/
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: compat.c,v 1.113 2020/07/03 08:13:23 rillig Exp $";
+static char rcsid[] = "$NetBSD: compat.c,v 1.139 2020/08/30 20:08:47 rillig Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)compat.c 8.2 (Berkeley) 3/19/94";
#else
-__RCSID("$NetBSD: compat.c,v 1.113 2020/07/03 08:13:23 rillig Exp $");
+__RCSID("$NetBSD: compat.c,v 1.139 2020/08/30 20:08:47 rillig Exp $");
#endif
#endif /* not lint */
#endif
@@ -129,34 +129,24 @@ static void
CompatDeleteTarget(GNode *gn)
{
if ((gn != NULL) && !Targ_Precious (gn)) {
- char *p1;
- char *file = Var_Value(TARGET, gn, &p1);
+ char *p1;
+ const char *file = Var_Value(TARGET, gn, &p1);
if (!noExecute && eunlink(file) != -1) {
Error("*** %s removed", file);
}
- free(p1);
+ bmake_free(p1);
}
}
-/*-
- *-----------------------------------------------------------------------
- * CompatInterrupt --
- * Interrupt the creation of the current target and remove it if
- * it ain't precious.
- *
- * Results:
- * None.
+/* Interrupt the creation of the current target and remove it if it ain't
+ * precious. Then exit.
*
- * Side Effects:
- * The target is removed and the process exits. If .INTERRUPT exists,
- * its commands are run first WITH INTERRUPTS IGNORED..
+ * If .INTERRUPT exists, its commands are run first WITH INTERRUPTS IGNORED.
*
* XXX: is .PRECIOUS supposed to inhibit .INTERRUPT? I doubt it, but I've
* left the logic alone for now. - dholland 20160826
- *
- *-----------------------------------------------------------------------
*/
static void
CompatInterrupt(int signo)
@@ -190,7 +180,7 @@ CompatInterrupt(int signo)
kill(myPid, signo);
}
}
-
+
/*-
*-----------------------------------------------------------------------
* CompatRunCommand --
@@ -215,7 +205,7 @@ CompatRunCommand(void *cmdp, void *gnp)
char *cmdStart; /* Start of expanded command */
char *cp, *bp;
Boolean silent, /* Don't print command */
- doIt; /* Execute even if -n */
+ 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 */
@@ -224,21 +214,17 @@ CompatRunCommand(void *cmdp, void *gnp)
LstNode cmdNode; /* Node where current command is located */
const char ** volatile av; /* Argument vector for thing to exec */
char ** volatile mav;/* Copy of the argument vector for freeing */
- int argc; /* Number of arguments in av or 0 if not
- * dynamically allocated */
- Boolean local; /* TRUE if command should be executed
- * locally */
Boolean useShell; /* TRUE if command should be executed
* using a shell */
char * volatile cmd = (char *)cmdp;
GNode *gn = (GNode *)gnp;
- silent = gn->type & OP_SILENT;
+ silent = (gn->type & OP_SILENT) != 0;
errCheck = !(gn->type & OP_IGNORE);
doIt = FALSE;
- cmdNode = Lst_Member(gn->commands, cmd);
- cmdStart = Var_Subst(NULL, cmd, gn, VARF_WANTRES);
+ cmdNode = Lst_FindDatum(gn->commands, cmd);
+ cmdStart = Var_Subst(cmd, gn, VARE_WANTRES);
/*
* brk_string will return an argv with a NULL in av[0], thus causing
@@ -252,10 +238,11 @@ CompatRunCommand(void *cmdp, void *gnp)
return 0;
}
cmd = cmdStart;
- Lst_Replace(cmdNode, cmdStart);
+ LstNode_Set(cmdNode, cmdStart);
if ((gn->type & OP_SAVE_CMDS) && (gn != ENDNode)) {
- (void)Lst_AtEnd(ENDNode->commands, cmdStart);
+ assert(ENDNode != NULL);
+ Lst_Append(ENDNode->commands, cmdStart);
return 0;
}
if (strcmp(cmdStart, "...") == 0) {
@@ -266,7 +253,7 @@ CompatRunCommand(void *cmdp, void *gnp)
while ((*cmd == '@') || (*cmd == '-') || (*cmd == '+')) {
switch (*cmd) {
case '@':
- silent = DEBUG(LOUD) ? FALSE : TRUE;
+ silent = !DEBUG(LOUD);
break;
case '-':
errCheck = FALSE;
@@ -330,7 +317,6 @@ CompatRunCommand(void *cmdp, void *gnp)
if (DEBUG(JOB))
fprintf(debug_file, "Execute: '%s'\n", cmd);
-again:
if (useShell) {
/*
* We need to pass the command off to the shell, typically
@@ -352,9 +338,8 @@ again:
else
shargv[shargc++] = "-c";
shargv[shargc++] = cmd;
- shargv[shargc++] = NULL;
+ shargv[shargc] = NULL;
av = shargv;
- argc = 0;
bp = NULL;
mav = NULL;
} else {
@@ -362,16 +347,12 @@ again:
* No meta-characters, so no need to exec a shell. Break the command
* into words to form an argument vector we can execute.
*/
- mav = brk_string(cmd, &argc, TRUE, &bp);
- if (mav == NULL) {
- useShell = 1;
- goto again;
- }
+ Words words = Str_Words(cmd, FALSE);
+ mav = words.words;
+ bp = words.freeIt;
av = (void *)mav;
}
- local = TRUE;
-
#ifdef USE_META
if (useMeta) {
meta_compat_start();
@@ -392,10 +373,7 @@ again:
meta_compat_child();
}
#endif
- if (local)
- (void)execvp(av[0], (char *const *)UNCONST(av));
- else
- (void)execv(av[0], (char *const *)UNCONST(av));
+ (void)execvp(av[0], (char *const *)UNCONST(av));
execError("exec", av[0]);
_exit(1);
}
@@ -403,7 +381,9 @@ again:
free(mav);
free(bp);
- Lst_Replace(cmdNode, NULL);
+ /* XXX: Memory management looks suspicious here. */
+ /* XXX: Setting a list item to NULL is unexpected. */
+ LstNode_SetNull(cmdNode);
#ifdef USE_META
if (useMeta) {
@@ -436,18 +416,18 @@ again:
#endif
if (status != 0) {
if (DEBUG(ERROR)) {
- fprintf(debug_file, "\n*** Failed target: %s\n*** Failed command: ",
+ fprintf(debug_file, "\n*** Failed target: %s\n*** Failed command: ",
gn->name);
- for (cp = cmd; *cp; ) {
- if (isspace((unsigned char)*cp)) {
+ for (cp = cmd; *cp; ) {
+ if (isspace((unsigned char)*cp)) {
fprintf(debug_file, " ");
- while (isspace((unsigned char)*cp))
+ while (isspace((unsigned char)*cp))
cp++;
} else {
fprintf(debug_file, "%c", *cp);
- cp++;
+ cp++;
}
- }
+ }
fprintf(debug_file, "\n");
}
printf("*** Error code %d", status);
@@ -502,7 +482,7 @@ again:
return status;
}
-
+
/*-
*-----------------------------------------------------------------------
* Compat_Make --
@@ -544,14 +524,14 @@ Compat_Make(void *gnp, void *pgnp)
Lst_ForEach(gn->children, Compat_Make, gn);
if ((gn->flags & REMAKE) == 0) {
gn->made = ABORTED;
- pgn->flags &= ~REMAKE;
+ pgn->flags &= ~(unsigned)REMAKE;
goto cohorts;
}
- if (Lst_Member(gn->iParents, pgn) != NULL) {
+ if (Lst_FindDatum(gn->implicitParents, pgn) != NULL) {
char *p1;
Var_Set(IMPSRC, Var_Value(TARGET, gn, &p1), pgn);
- free(p1);
+ bmake_free(p1);
}
/*
@@ -614,7 +594,7 @@ Compat_Make(void *gnp, void *pgnp)
Lst_ForEach(gn->commands, CompatRunCommand, gn);
curTarg = NULL;
} else {
- Job_Touch(gn, gn->type & OP_SILENT);
+ Job_Touch(gn, (gn->type & OP_SILENT) != 0);
}
} else {
gn->made = ERROR;
@@ -640,7 +620,7 @@ Compat_Make(void *gnp, void *pgnp)
Make_TimeStamp(pgn, gn);
}
} else if (keepgoing) {
- pgn->flags &= ~REMAKE;
+ pgn->flags &= ~(unsigned)REMAKE;
} else {
PrintOnError(gn, "\nStop.");
exit(1);
@@ -650,18 +630,19 @@ Compat_Make(void *gnp, void *pgnp)
* Already had an error when making this beastie. Tell the parent
* to abort.
*/
- pgn->flags &= ~REMAKE;
+ pgn->flags &= ~(unsigned)REMAKE;
} else {
- if (Lst_Member(gn->iParents, pgn) != NULL) {
+ if (Lst_FindDatum(gn->implicitParents, pgn) != NULL) {
char *p1;
- Var_Set(IMPSRC, Var_Value(TARGET, gn, &p1), pgn);
- free(p1);
+ const char *target = Var_Value(TARGET, gn, &p1);
+ Var_Set(IMPSRC, target != NULL ? target : "", pgn);
+ bmake_free(p1);
}
switch(gn->made) {
case BEINGMADE:
Error("Graph cycles through %s", gn->name);
gn->made = ERROR;
- pgn->flags &= ~REMAKE;
+ pgn->flags &= ~(unsigned)REMAKE;
break;
case MADE:
if ((gn->type & OP_EXEC) == 0) {
@@ -683,22 +664,11 @@ cohorts:
Lst_ForEach(gn->cohorts, Compat_Make, pgnp);
return 0;
}
-
-/*-
- *-----------------------------------------------------------------------
- * Compat_Run --
- * Initialize this mode and start making.
+
+/* Initialize this module and start making.
*
* Input:
- * targs List of target nodes to re-create
- *
- * Results:
- * None.
- *
- * Side Effects:
- * Guess what?
- *
- *-----------------------------------------------------------------------
+ * targs The target nodes to re-create
*/
void
Compat_Run(Lst targs)
@@ -732,10 +702,10 @@ Compat_Run(Lst targs)
gn = Targ_FindNode(".BEGIN", TARG_NOCREATE);
if (gn != NULL) {
Compat_Make(gn, gn);
- if (gn->made == ERROR) {
- PrintOnError(gn, "\nStop.");
- exit(1);
- }
+ if (gn->made == ERROR) {
+ PrintOnError(gn, "\nStop.");
+ exit(1);
+ }
}
}
@@ -756,8 +726,8 @@ Compat_Run(Lst targs)
* could not be made due to errors.
*/
errors = 0;
- while (!Lst_IsEmpty (targs)) {
- gn = (GNode *)Lst_DeQueue(targs);
+ while (!Lst_IsEmpty(targs)) {
+ gn = Lst_Dequeue(targs);
Compat_Make(gn, gn);
if (gn->made == UPTODATE) {
diff --git a/contrib/bmake/cond.c b/contrib/bmake/cond.c
index 321d9a92e75b..7aa072d4eb22 100644
--- a/contrib/bmake/cond.c
+++ b/contrib/bmake/cond.c
@@ -1,4 +1,4 @@
-/* $NetBSD: cond.c,v 1.79 2020/07/09 22:34:08 sjg Exp $ */
+/* $NetBSD: cond.c,v 1.106 2020/08/29 13:38:48 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -70,14 +70,14 @@
*/
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: cond.c,v 1.79 2020/07/09 22:34:08 sjg Exp $";
+static char rcsid[] = "$NetBSD: cond.c,v 1.106 2020/08/29 13:38:48 rillig Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)cond.c 8.2 (Berkeley) 1/2/94";
#else
-__RCSID("$NetBSD: cond.c,v 1.79 2020/07/09 22:34:08 sjg Exp $");
+__RCSID("$NetBSD: cond.c,v 1.106 2020/08/29 13:38:48 rillig Exp $");
#endif
#endif /* not lint */
#endif
@@ -91,14 +91,10 @@ __RCSID("$NetBSD: cond.c,v 1.79 2020/07/09 22:34:08 sjg Exp $");
*
*/
-#include <assert.h>
-#include <ctype.h>
-#include <errno.h> /* For strtoul() error checking */
+#include <errno.h>
-#include "make.h"
-#include "hash.h"
-#include "dir.h"
-#include "buf.h"
+#include "make.h"
+#include "dir.h"
/*
* The parsing of conditional expressions is based on this grammar:
@@ -121,8 +117,7 @@ __RCSID("$NetBSD: cond.c,v 1.79 2020/07/09 22:34:08 sjg Exp $");
* T -> ! T
* op -> == | != | > | < | >= | <=
*
- * 'symbol' is some other symbol to which the default function (condDefProc)
- * is applied.
+ * 'symbol' is some other symbol to which the default function is applied.
*
* Tokens are scanned from the 'condExpr' string. The scanner (CondToken)
* will return TOK_AND for '&' and '&&', TOK_OR for '|' and '||',
@@ -141,46 +136,16 @@ typedef enum {
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(Boolean, 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 CondEvalResult 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
+static const struct If *if_info; /* Info for current statement */
+static const 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 unsigned int cond_depth = 0; /* current .if nesting level */
+static unsigned int cond_min_depth = 0; /* depth at makefile open */
/*
* Indicate when we should be strict about lhs of comparisons.
@@ -194,58 +159,38 @@ static Boolean lhsStrict;
static int
istoken(const char *str, const char *tok, size_t len)
{
- return strncmp(str, tok, len) == 0 && !isalpha((unsigned char)str[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.
- *
- *-----------------------------------------------------------------------
- */
+/* Push back the most recent token read. We only need one level of
+ * this, so the thing is just stored in 'condPushback'. */
static void
CondPushBack(Token t)
{
condPushBack = t;
}
-
+
/*-
- *-----------------------------------------------------------------------
- * CondGetArg --
- * Find the argument of a built-in function.
+ * Parse 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.
- *
- *-----------------------------------------------------------------------
+ * The length of the argument.
+ * *argPtr receives the argument as string.
+ * *linePtr is updated to point behind the ')' of the function call.
*/
static int
-CondGetArg(Boolean doEval, char **linePtr, char **argPtr, const char *func)
+CondGetArg(Boolean doEval, const char **linePtr, char **argPtr,
+ const char *func)
{
- char *cp;
- int argLen;
- Buffer buf;
- int paren_depth;
- char ch;
+ const char *cp;
+ Buffer buf;
+ int paren_depth;
+ char ch;
+ size_t argLen;
cp = *linePtr;
if (func != NULL)
- /* Skip opening '(' - verfied by caller */
+ /* Skip opening '(' - verified by caller */
cp++;
if (*cp == '\0') {
@@ -283,23 +228,19 @@ CondGetArg(Boolean doEval, char **linePtr, char **argPtr, const char *func)
* 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, VARF_UNDEFERR|
- (doEval ? VARF_WANTRES : 0),
- &len, &freeIt);
- Buf_AddBytes(&buf, strlen(cp2), cp2);
+ int len;
+ void *freeIt;
+ VarEvalFlags eflags = VARE_UNDEFERR | (doEval ? VARE_WANTRES : 0);
+ const char *cp2 = Var_Parse(cp, VAR_CMD, eflags, &len, &freeIt);
+ Buf_AddStr(&buf, cp2);
free(freeIt);
cp += len;
continue;
}
if (ch == '(')
paren_depth++;
- else
- if (ch == ')' && --paren_depth < 0)
- break;
+ else if (ch == ')' && --paren_depth < 0)
+ break;
Buf_AddByte(&buf, *cp);
cp++;
}
@@ -313,105 +254,49 @@ CondGetArg(Boolean doEval, char **linePtr, char **argPtr, const char *func)
if (func != NULL && *cp++ != ')') {
Parse_Error(PARSE_WARNING, "Missing closing parenthesis for %s()",
- func);
+ 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.
- *
- *-----------------------------------------------------------------------
- */
+
+/* Test whether the given variable is defined. */
static Boolean
CondDoDefined(int argLen MAKE_ATTR_UNUSED, const char *arg)
{
- char *p1;
- Boolean result;
-
- if (Var_Value(arg, VAR_CMD, &p1) != NULL) {
- result = TRUE;
- } else {
- result = FALSE;
- }
-
- free(p1);
+ char *freeIt;
+ Boolean result = Var_Value(arg, VAR_CMD, &freeIt) != NULL;
+ bmake_free(freeIt);
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)
+
+/* Wrapper around Str_Match, to be used by Lst_Find. */
+static Boolean
+CondFindStrMatch(const void *string, const void *pattern)
{
- return !Str_Match(string, 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.
- *
- *-----------------------------------------------------------------------
- */
+
+/* See if the given target is being made. */
static Boolean
CondDoMake(int argLen MAKE_ATTR_UNUSED, const char *arg)
{
- return Lst_Find(create, arg, CondStrMatch) != NULL;
+ return Lst_Find(create, CondFindStrMatch, arg) != NULL;
}
-
-/*-
- *-----------------------------------------------------------------------
- * CondDoExists --
- * See if the given file exists.
- *
- * Results:
- * TRUE if the file exists and FALSE if it does not.
- *
- * Side Effects:
- * None.
- *
- *-----------------------------------------------------------------------
- */
+
+/* See if the given file exists. */
static Boolean
CondDoExists(int argLen MAKE_ATTR_UNUSED, const char *arg)
{
Boolean result;
- char *path;
+ char *path;
path = Dir_FindFile(arg, dirSearchPath);
if (DEBUG(COND)) {
fprintf(debug_file, "exists(%s) result is \"%s\"\n",
- arg, path ? path : "");
+ arg, path ? path : "");
}
if (path != NULL) {
result = TRUE;
@@ -421,68 +306,39 @@ CondDoExists(int argLen MAKE_ATTR_UNUSED, const char *arg)
}
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.
- *
- *-----------------------------------------------------------------------
- */
+
+/* See if the given node exists and is an actual target. */
static Boolean
CondDoTarget(int argLen MAKE_ATTR_UNUSED, const char *arg)
{
- GNode *gn;
+ 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.
- *
- *-----------------------------------------------------------------------
- */
+/* See if the given node exists and is an actual target with commands
+ * associated with it. */
static Boolean
CondDoCommands(int argLen MAKE_ATTR_UNUSED, const char *arg)
{
- GNode *gn;
+ 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.
+ * 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
- *
- *-----------------------------------------------------------------------
+ * Returns TRUE if the conversion succeeded.
*/
static Boolean
-CondCvtArg(char *str, double *value)
+CondCvtArg(const char *str, double *value)
{
char *eptr, ech;
unsigned long l_val;
@@ -510,33 +366,28 @@ CondCvtArg(char *str, double *value)
}
/*-
- *-----------------------------------------------------------------------
- * CondGetString --
- * Get a string from a variable reference or an optionally quoted
- * string. This is called for the lhs and rhs of string compares.
+ * 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.
+ * Returns the string, absent any quotes, or NULL on error.
+ * Sets quoted if the string was quoted.
+ * Sets freeIt if needed.
*
* Side Effects:
- * Moves condExpr to end of this token.
- *
- *
- *-----------------------------------------------------------------------
+ * Moves condExpr past the end of this token.
*/
/* coverity:[+alloc : arg-*2] */
-static char *
+static const char *
CondGetString(Boolean doEval, Boolean *quoted, void **freeIt, Boolean strictLHS)
{
Buffer buf;
- char *cp;
- char *str;
- int len;
- int qt;
- char *start;
+ const char *cp;
+ const char *str;
+ int len;
+ Boolean qt;
+ const char *start;
+ VarEvalFlags eflags;
Buf_Init(&buf, 0);
str = NULL;
@@ -573,9 +424,9 @@ CondGetString(Boolean doEval, Boolean *quoted, void **freeIt, Boolean strictLHS)
break;
case '$':
/* if we are in quotes, then an undefined variable is ok */
- str = Var_Parse(condExpr, VAR_CMD,
- ((!qt && doEval) ? VARF_UNDEFERR : 0) |
- (doEval ? VARF_WANTRES : 0), &len, freeIt);
+ eflags = ((!qt && doEval) ? VARE_UNDEFERR : 0) |
+ (doEval ? VARE_WANTRES : 0);
+ str = Var_Parse(condExpr, VAR_CMD, eflags, &len, freeIt);
if (str == var_Error) {
if (*freeIt) {
free(*freeIt);
@@ -596,7 +447,7 @@ CondGetString(Boolean doEval, Boolean *quoted, void **freeIt, Boolean strictLHS)
*/
if ((condExpr == start + len) &&
(*condExpr == '\0' ||
- isspace((unsigned char) *condExpr) ||
+ isspace((unsigned char)*condExpr) ||
strchr("!=><)", *condExpr))) {
goto cleanup;
}
@@ -610,12 +461,12 @@ CondGetString(Boolean doEval, Boolean *quoted, void **freeIt, Boolean strictLHS)
free(*freeIt);
*freeIt = NULL;
}
- str = NULL; /* not finished yet */
- condExpr--; /* don't skip over next char */
+ str = NULL; /* not finished yet */
+ condExpr--; /* don't skip over next char */
break;
default:
if (strictLHS && !qt && *start != '$' &&
- !isdigit((unsigned char) *start)) {
+ !isdigit((unsigned char)*start)) {
/* lhs must be quoted, a variable reference or number */
if (*freeIt) {
free(*freeIt);
@@ -628,43 +479,51 @@ CondGetString(Boolean doEval, Boolean *quoted, void **freeIt, Boolean strictLHS)
break;
}
}
- got_str:
- str = Buf_GetAll(&buf, NULL);
- *freeIt = str;
- cleanup:
+got_str:
+ *freeIt = Buf_GetAll(&buf, NULL);
+ str = *freeIt;
+cleanup:
Buf_Destroy(&buf, FALSE);
return str;
}
-
+
+/* The different forms of #if's. */
+static const struct If {
+ const char *form; /* Form of if */
+ size_t 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 }
+};
+
/*-
- *-----------------------------------------------------------------------
- * CondToken --
- * Return the next token from the input.
- *
- * Results:
- * A Token for the next lexical token in the stream.
+ * Return the next token from the input.
*
* 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;
+ Token t;
+ const char *lhs;
+ const char *rhs;
+ const char *op;
+ void *lhsFree;
+ void *rhsFree;
Boolean lhsQuoted;
Boolean rhsQuoted;
- double left, right;
+ double left, right;
t = TOK_ERROR;
rhs = NULL;
- lhsFree = rhsFree = FALSE;
+ lhsFree = rhsFree = NULL;
lhsQuoted = rhsQuoted = FALSE;
/*
@@ -678,7 +537,7 @@ compare_expression(Boolean doEval)
/*
* Skip whitespace to get to the operator
*/
- while (isspace((unsigned char) *condExpr))
+ while (isspace((unsigned char)*condExpr))
condExpr++;
/*
@@ -688,39 +547,39 @@ compare_expression(Boolean doEval)
*/
op = condExpr;
switch (*condExpr) {
- case '!':
- case '=':
- case '<':
- case '>':
- if (condExpr[1] == '=') {
- condExpr += 2;
- } else {
- condExpr += 1;
- }
- break;
- default:
- if (!doEval) {
- t = TOK_FALSE;
- goto done;
- }
- /* For .ifxxx "..." check for non-empty string. */
- if (lhsQuoted) {
- t = lhs[0] != 0;
- goto done;
- }
- /* For .ifxxx <number> compare against zero */
- if (CondCvtArg(lhs, &left)) {
- t = left != 0.0;
- goto done;
- }
- /* For .if ${...} check for non-empty string (defProc is ifdef). */
- if (if_info->form[0] == 0) {
- t = lhs[0] != 0;
- goto done;
- }
- /* Otherwise action default test ... */
- t = if_info->defProc(strlen(lhs), lhs) != if_info->doNot;
+ case '!':
+ case '=':
+ case '<':
+ case '>':
+ if (condExpr[1] == '=') {
+ condExpr += 2;
+ } else {
+ condExpr += 1;
+ }
+ break;
+ default:
+ if (!doEval) {
+ t = TOK_FALSE;
goto done;
+ }
+ /* For .ifxxx "..." check for non-empty string. */
+ if (lhsQuoted) {
+ t = lhs[0] != 0;
+ goto done;
+ }
+ /* For .ifxxx <number> compare against zero */
+ if (CondCvtArg(lhs, &left)) {
+ t = left != 0.0;
+ goto done;
+ }
+ /* For .if ${...} check for non-empty string (defProc is ifdef). */
+ if (if_info->form[0] == 0) {
+ t = lhs[0] != 0;
+ goto done;
+ }
+ /* Otherwise action default test ... */
+ t = if_info->defProc(strlen(lhs), lhs) != if_info->doNot;
+ goto done;
}
while (isspace((unsigned char)*condExpr))
@@ -742,16 +601,16 @@ compare_expression(Boolean doEval)
}
if (rhsQuoted || lhsQuoted) {
-do_string_compare:
+ do_string_compare:
if (((*op != '!') && (*op != '=')) || (op[1] != '=')) {
Parse_Error(PARSE_WARNING,
- "String comparison operator should be either == or !=");
+ "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);
+ lhs, rhs, op);
}
/*
* Null-terminate rhs and perform the comparison.
@@ -773,9 +632,9 @@ do_string_compare:
if (DEBUG(COND)) {
fprintf(debug_file, "left = %f, right = %f, op = %.2s\n", left,
- right, op);
+ right, op);
}
- switch(op[0]) {
+ switch (op[0]) {
case '!':
if (op[1] != '=') {
Parse_Error(PARSE_WARNING,
@@ -816,21 +675,23 @@ done:
}
static int
-get_mpt_arg(Boolean doEval, char **linePtr, char **argPtr, const char *func MAKE_ATTR_UNUSED)
+get_mpt_arg(Boolean doEval, const char **linePtr, char **argPtr,
+ const char *func MAKE_ATTR_UNUSED)
{
/*
* Use Var_Parse to parse the spec in parens and return
* TOK_TRUE if the resulting string is empty.
*/
- int length;
- void *freeIt;
- char *val;
- char *cp = *linePtr;
+ int length;
+ void *val_freeIt;
+ const char *val;
+ const 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, doEval ? VARF_WANTRES : 0, &length, &freeIt);
+ val = Var_Parse(cp - 1, VAR_CMD, doEval ? VARE_WANTRES : 0, &length,
+ &val_freeIt);
/*
* Advance *linePtr to beyond the closing ). Note that
* we subtract one because 'length' is calculated from 'cp - 1'.
@@ -838,12 +699,12 @@ get_mpt_arg(Boolean doEval, char **linePtr, char **argPtr, const char *func MAKE
*linePtr = cp - 1 + length;
if (val == var_Error) {
- free(freeIt);
+ free(val_freeIt);
return -1;
}
/* A variable is empty when it just contains spaces... 4/15/92, christos */
- while (isspace(*(unsigned char *)val))
+ while (isspace((unsigned char)val[0]))
val++;
/*
@@ -851,7 +712,7 @@ get_mpt_arg(Boolean doEval, char **linePtr, char **argPtr, const char *func MAKE
* true/false here.
*/
length = *val ? 2 : 1;
- free(freeIt);
+ free(val_freeIt);
return length;
}
@@ -865,32 +726,32 @@ static Token
compare_function(Boolean doEval)
{
static const struct fn_def {
- const char *fn_name;
- int fn_name_len;
- int (*fn_getarg)(Boolean, char **, char **, const char *);
- Boolean (*fn_proc)(int, const char *);
+ const char *fn_name;
+ size_t fn_name_len;
+ int (*fn_getarg)(Boolean, const 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 },
+ { "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;
+ Token t;
+ char *arg = NULL;
+ int arglen;
+ const char *cp = condExpr;
+ const 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))
+ while (isspace((unsigned char)*cp))
cp++;
if (*cp != '(')
break;
@@ -921,7 +782,7 @@ compare_function(Boolean doEval)
* expression.
*/
arglen = CondGetArg(doEval, &cp, &arg, NULL);
- for (cp1 = cp; isspace(*(unsigned char *)cp1); cp1++)
+ for (cp1 = cp; isspace((unsigned char)*cp1); cp1++)
continue;
if (*cp1 == '=' || *cp1 == '!')
return compare_expression(doEval);
@@ -1015,7 +876,7 @@ CondToken(Boolean doEval)
static Token
CondT(Boolean doEval)
{
- Token t;
+ Token t;
t = CondToken(doEval);
@@ -1045,7 +906,7 @@ CondT(Boolean doEval)
}
return t;
}
-
+
/*-
*-----------------------------------------------------------------------
* CondF --
@@ -1063,7 +924,7 @@ CondT(Boolean doEval)
static Token
CondF(Boolean doEval)
{
- Token l, o;
+ Token l, o;
l = CondT(doEval);
if (l != TOK_ERROR) {
@@ -1091,7 +952,7 @@ CondF(Boolean doEval)
}
return l;
}
-
+
/*-
*-----------------------------------------------------------------------
* CondE --
@@ -1109,7 +970,7 @@ CondF(Boolean doEval)
static Token
CondE(Boolean doEval)
{
- Token l, o;
+ Token l, o;
l = CondF(doEval);
if (l != TOK_ERROR) {
@@ -1139,6 +1000,31 @@ CondE(Boolean doEval)
return l;
}
+static CondEvalResult
+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_EvalExpression --
@@ -1153,16 +1039,16 @@ CondE(Boolean doEval)
* (*value) is set to the boolean value of the condition
*
* Side Effects:
- * None.
- *
+ * Any effects from evaluating the variables.
*-----------------------------------------------------------------------
*/
-int
-Cond_EvalExpression(const struct If *info, char *line, Boolean *value, int eprint, Boolean strictLHS)
+CondEvalResult
+Cond_EvalExpression(const struct If *info, char *line, Boolean *value,
+ int eprint, Boolean strictLHS)
{
static const struct If *dflt_info;
const struct If *sv_if_info = if_info;
- char *sv_condExpr = condExpr;
+ const char *sv_condExpr = condExpr;
Token sv_condPushBack = condPushBack;
int rval;
@@ -1173,7 +1059,7 @@ Cond_EvalExpression(const struct If *info, char *line, Boolean *value, int eprin
if (info == NULL && (info = dflt_info) == NULL) {
/* Scan for the entry for .if - it can't be first */
- for (info = ifs; ; info++)
+ for (info = ifs;; info++)
if (info->form[0] == 0)
break;
dflt_info = info;
@@ -1196,32 +1082,7 @@ Cond_EvalExpression(const struct If *info, char *line, Boolean *value, int eprin
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 --
@@ -1241,35 +1102,31 @@ do_Cond_EvalExpression(Boolean *value)
* 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)
+ * to detect spurious .else lines (as are SKIP_TO_ELSE and SKIP_TO_ENDIF),
* otherwise .else could be treated as '.elif 1'.
- *
*-----------------------------------------------------------------------
*/
-int
+CondEvalResult
Cond_Eval(char *line)
{
-#define MAXIF 128 /* maximum depth of .if'ing */
-#define MAXIF_BUMP 32 /* how much to grow by */
+ enum { MAXIF = 128 }; /* maximum depth of .if'ing */
+ enum { MAXIF_BUMP = 32 }; /* how much to grow by */
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_ELSE, /* has been true, but not seen '.else' */
SKIP_TO_ENDIF /* nothing else to execute */
};
static enum if_states *cond_state = NULL;
static unsigned int max_if_depth = MAXIF;
const struct If *ifp;
- Boolean isElif;
- Boolean value;
- int level; /* Level at which to report errors. */
- enum if_states state;
+ Boolean isElif;
+ Boolean value;
+ int level; /* Level at which to report errors. */
+ enum if_states state;
level = PARSE_FATAL;
if (!cond_state) {
@@ -1292,7 +1149,8 @@ Cond_Eval(char *line)
}
/* Return state for previous conditional */
cond_depth--;
- return cond_state[cond_depth] <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP;
+ return cond_state[cond_depth] <= ELSE_ACTIVE
+ ? COND_PARSE : COND_SKIP;
}
/* Quite likely this is 'else' or 'elif' */
@@ -1336,7 +1194,7 @@ Cond_Eval(char *line)
* function is, etc. -- by looking in the table of valid "ifs"
*/
line += 2;
- for (ifp = ifs; ; ifp++) {
+ for (ifp = ifs;; ifp++) {
if (ifp->form == NULL)
return COND_INVALID;
if (istoken(ifp->form, line, ifp->formlen)) {
@@ -1372,8 +1230,8 @@ Cond_Eval(char *line)
* can need more than the default.
*/
max_if_depth += MAXIF_BUMP;
- cond_state = bmake_realloc(cond_state, max_if_depth *
- sizeof(*cond_state));
+ cond_state = bmake_realloc(cond_state,
+ max_if_depth * sizeof(*cond_state));
}
state = cond_state[cond_depth];
cond_depth++;
@@ -1400,21 +1258,6 @@ Cond_Eval(char *line)
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)
{
diff --git a/contrib/bmake/dir.c b/contrib/bmake/dir.c
index 5c5e7e5c14f2..4a561deca6fc 100644
--- a/contrib/bmake/dir.c
+++ b/contrib/bmake/dir.c
@@ -1,4 +1,4 @@
-/* $NetBSD: dir.c,v 1.76 2020/07/03 08:13:23 rillig Exp $ */
+/* $NetBSD: dir.c,v 1.135 2020/09/02 04:32:13 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -70,14 +70,14 @@
*/
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: dir.c,v 1.76 2020/07/03 08:13:23 rillig Exp $";
+static char rcsid[] = "$NetBSD: dir.c,v 1.135 2020/09/02 04:32:13 rillig Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)dir.c 8.2 (Berkeley) 1/2/94";
#else
-__RCSID("$NetBSD: dir.c,v 1.76 2020/07/03 08:13:23 rillig Exp $");
+__RCSID("$NetBSD: dir.c,v 1.135 2020/09/02 04:32:13 rillig Exp $");
#endif
#endif /* not lint */
#endif
@@ -143,10 +143,20 @@ __RCSID("$NetBSD: dir.c,v 1.76 2020/07/03 08:13:23 rillig Exp $");
#include <stdio.h>
#include "make.h"
-#include "hash.h"
#include "dir.h"
#include "job.h"
+
+#define DIR_DEBUG0(fmt) \
+ if (!DEBUG(DIR)) (void) 0; else fprintf(debug_file, fmt)
+
+#define DIR_DEBUG1(fmt, arg1) \
+ if (!DEBUG(DIR)) (void) 0; else fprintf(debug_file, fmt, arg1)
+
+#define DIR_DEBUG2(fmt, arg1, arg2) \
+ if (!DEBUG(DIR)) (void) 0; else fprintf(debug_file, fmt, arg1, arg2)
+
+
/*
* A search path consists of a Lst of Path structures. A Path structure
* has in it the name of the directory and a hash table of all the files
@@ -217,37 +227,35 @@ __RCSID("$NetBSD: dir.c,v 1.76 2020/07/03 08:13:23 rillig Exp $");
* in a cache for when Dir_MTime was actually called.
*/
-Lst dirSearchPath; /* main search path */
+Lst dirSearchPath; /* main search path */
-static Lst openDirectories; /* the list of all open directories */
+static Lst openDirectories; /* the list of all open directories */
/*
* Variables for gathering statistics on the efficiency of the hashing
* mechanism.
*/
-static int hits, /* Found in directory cache */
- misses, /* Sad, but not evil misses */
- nearmisses, /* Found under search path */
- bigmisses; /* Sought by itself */
-
-static Path *dot; /* contents of current directory */
-static Path *cur; /* contents of current directory, if not dot */
-static Path *dotLast; /* a fake path entry indicating we need to
- * look for . last */
-static Hash_Table mtimes; /* Results of doing a last-resort stat in
- * Dir_FindFile -- if we have to go to the
- * system to find the file, we might as well
- * have its mtime on record. XXX: If this is done
- * way early, there's a chance other rules will
- * have already updated the file, in which case
- * we'll update it again. Generally, there won't
- * be two rules to update a single file, so this
- * should be ok, but... */
-
-static Hash_Table lmtimes; /* same as mtimes but for lstat */
-
-static int DirFindName(const void *, const void *);
-static int DirMatchFiles(const char *, Path *, Lst);
+static int hits; /* Found in directory cache */
+static int misses; /* Sad, but not evil misses */
+static int nearmisses; /* Found under search path */
+static int 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 */
+
+/* Results of doing a last-resort stat in Dir_FindFile -- if we have to go to
+ * the system to find the file, we might as well have its mtime on record.
+ *
+ * XXX: If this is done way early, there's a chance other rules will have
+ * already updated the file, in which case we'll update it again. Generally,
+ * there won't be two rules to update a single file, so this should be ok,
+ * but... */
+static Hash_Table mtimes;
+
+static Hash_Table lmtimes; /* same as mtimes but for lstat */
+
static void DirExpandCurly(const char *, const char *, Lst, Lst);
static void DirExpandInt(const char *, Lst, Lst);
static int DirPrintWord(void *, void *);
@@ -259,23 +267,28 @@ static char *DirLookupAbs(Path *, const char *, const char *);
/*
- * We use stat(2) a lot, cache the results
+ * We use stat(2) a lot, cache the results.
* mtime and mode are all we care about.
*/
struct cache_st {
- time_t lmtime; /* lstat */
- time_t mtime; /* stat */
- mode_t mode;
+ time_t lmtime; /* lstat */
+ time_t mtime; /* stat */
+ mode_t mode;
};
/* minimize changes below */
-#define CST_LSTAT 1
-#define CST_UPDATE 2
+typedef enum {
+ CST_LSTAT = 0x01, /* call lstat(2) instead of stat(2) */
+ CST_UPDATE = 0x02 /* ignore existing cached entry */
+} CachedStatsFlags;
+/* Returns 0 and the result of stat(2) or lstat(2) in *mst, or -1 on error. */
static int
-cached_stats(Hash_Table *htp, const char *pathname, struct stat *st, int flags)
+cached_stats(Hash_Table *htp, const char *pathname, struct make_stat *mst,
+ CachedStatsFlags flags)
{
Hash_Entry *entry;
+ struct stat sys_st;
struct cache_st *cst;
int rc;
@@ -284,83 +297,74 @@ cached_stats(Hash_Table *htp, const char *pathname, struct stat *st, int flags)
entry = Hash_FindEntry(htp, pathname);
- if (entry && (flags & CST_UPDATE) == 0) {
- cst = entry->clientPtr;
+ if (entry && !(flags & CST_UPDATE)) {
+ cst = Hash_GetValue(entry);
- memset(st, 0, sizeof(*st));
- st->st_mode = cst->mode;
- st->st_mtime = (flags & CST_LSTAT) ? cst->lmtime : cst->mtime;
- if (st->st_mtime) {
- if (DEBUG(DIR)) {
- fprintf(debug_file, "Using cached time %s for %s\n",
- Targ_FmtTime(st->st_mtime), pathname);
- }
+ mst->mst_mode = cst->mode;
+ mst->mst_mtime = (flags & CST_LSTAT) ? cst->lmtime : cst->mtime;
+ if (mst->mst_mtime) {
+ DIR_DEBUG2("Using cached time %s for %s\n",
+ Targ_FmtTime(mst->mst_mtime), pathname);
return 0;
}
}
- rc = (flags & CST_LSTAT) ? lstat(pathname, st) : stat(pathname, st);
+ rc = (flags & CST_LSTAT)
+ ? lstat(pathname, &sys_st)
+ : stat(pathname, &sys_st);
if (rc == -1)
return -1;
- if (st->st_mtime == 0)
- st->st_mtime = 1; /* avoid confusion with missing file */
+ if (sys_st.st_mtime == 0)
+ sys_st.st_mtime = 1; /* avoid confusion with missing file */
+
+ mst->mst_mode = sys_st.st_mode;
+ mst->mst_mtime = sys_st.st_mtime;
- if (!entry)
+ if (entry == NULL)
entry = Hash_CreateEntry(htp, pathname, NULL);
- if (!entry->clientPtr) {
- entry->clientPtr = bmake_malloc(sizeof(*cst));
- memset(entry->clientPtr, 0, sizeof(*cst));
+ if (Hash_GetValue(entry) == NULL) {
+ Hash_SetValue(entry, bmake_malloc(sizeof(*cst)));
+ memset(Hash_GetValue(entry), 0, sizeof(*cst));
}
- cst = entry->clientPtr;
- if ((flags & CST_LSTAT)) {
- cst->lmtime = st->st_mtime;
+ cst = Hash_GetValue(entry);
+ if (flags & CST_LSTAT) {
+ cst->lmtime = sys_st.st_mtime;
} else {
- cst->mtime = st->st_mtime;
- }
- cst->mode = st->st_mode;
- if (DEBUG(DIR)) {
- fprintf(debug_file, " Caching %s for %s\n",
- Targ_FmtTime(st->st_mtime), pathname);
+ cst->mtime = sys_st.st_mtime;
}
+ cst->mode = sys_st.st_mode;
+ DIR_DEBUG2(" Caching %s for %s\n",
+ Targ_FmtTime(sys_st.st_mtime), pathname);
return 0;
}
int
-cached_stat(const char *pathname, void *st)
+cached_stat(const char *pathname, struct make_stat *st)
{
return cached_stats(&mtimes, pathname, st, 0);
}
int
-cached_lstat(const char *pathname, void *st)
+cached_lstat(const char *pathname, struct make_stat *st)
{
return cached_stats(&lmtimes, pathname, st, CST_LSTAT);
}
-/*-
- *-----------------------------------------------------------------------
- * Dir_Init --
- * initialize things for this module
- *
- * Results:
- * none
- *
- * Side Effects:
- * some directories may be opened.
- *-----------------------------------------------------------------------
- */
+/* Initialize things for this module. */
void
-Dir_Init(const char *cdname)
+Dir_Init(void)
+{
+ dirSearchPath = Lst_Init();
+ openDirectories = Lst_Init();
+ Hash_InitTable(&mtimes, 0);
+ Hash_InitTable(&lmtimes, 0);
+}
+
+void
+Dir_InitDir(const char *cdname)
{
- if (!cdname) {
- dirSearchPath = Lst_Init(FALSE);
- openDirectories = Lst_Init(FALSE);
- Hash_InitTable(&mtimes, 0);
- Hash_InitTable(&lmtimes, 0);
- return;
- }
Dir_InitCur(cdname);
dotLast = bmake_malloc(sizeof(Path));
@@ -371,7 +375,7 @@ Dir_Init(const char *cdname)
}
/*
- * Called by Dir_Init() and whenever .CURDIR is assigned to.
+ * Called by Dir_InitDir and whenever .CURDIR is assigned to.
*/
void
Dir_InitCur(const char *cdname)
@@ -397,18 +401,8 @@ Dir_InitCur(const char *cdname)
}
}
-/*-
- *-----------------------------------------------------------------------
- * Dir_InitDot --
- * (re)initialize "dot" (current/object directory) path hash
- *
- * Results:
- * none
- *
- * Side Effects:
- * some directories may be opened.
- *-----------------------------------------------------------------------
- */
+/* (Re)initialize "dot" (current/object directory) path hash.
+ * Some directories may be opened. */
void
Dir_InitDot(void)
{
@@ -416,8 +410,8 @@ Dir_InitDot(void)
LstNode ln;
/* Remove old entry from openDirectories, but do not destroy. */
- ln = Lst_Member(openDirectories, dot);
- (void)Lst_Remove(openDirectories, ln);
+ ln = Lst_FindDatum(openDirectories, dot);
+ Lst_Remove(openDirectories, ln);
}
dot = Dir_AddDir(NULL, ".");
@@ -432,21 +426,10 @@ Dir_InitDot(void)
* to make sure it's not destroyed.
*/
dot->refCount += 1;
- Dir_SetPATH(); /* initialize */
+ Dir_SetPATH(); /* initialize */
}
-/*-
- *-----------------------------------------------------------------------
- * Dir_End --
- * cleanup things for this module
- *
- * Results:
- * none
- *
- * Side Effects:
- * none
- *-----------------------------------------------------------------------
- */
+/* Clean up things for this module. */
void
Dir_End(void)
{
@@ -460,9 +443,9 @@ Dir_End(void)
Dir_Destroy(dotLast);
Dir_Destroy(dot);
Dir_ClearPath(dirSearchPath);
- Lst_Destroy(dirSearchPath, NULL);
+ Lst_Free(dirSearchPath);
Dir_ClearPath(openDirectories);
- Lst_Destroy(openDirectories, NULL);
+ Lst_Free(openDirectories);
Hash_DeleteTable(&mtimes);
#endif
}
@@ -475,122 +458,100 @@ Dir_End(void)
void
Dir_SetPATH(void)
{
- LstNode ln; /* a list element */
+ LstNode ln; /* a list element */
Path *p;
- Boolean hasLastDot = FALSE; /* true we should search dot last */
+ Boolean hasLastDot = FALSE; /* true if 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);
- }
+ Lst_Open(dirSearchPath);
+ if ((ln = Lst_First(dirSearchPath)) != NULL) {
+ p = LstNode_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);
- }
+ 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);
- }
+ while ((ln = Lst_Next(dirSearchPath)) != NULL) {
+ p = LstNode_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);
+ if (hasLastDot) {
+ if (dot)
+ Var_Append(".PATH", dot->name, VAR_GLOBAL);
+ if (cur)
+ Var_Append(".PATH", cur->name, VAR_GLOBAL);
}
+ Lst_Close(dirSearchPath);
}
-/*-
- *-----------------------------------------------------------------------
- * DirFindName --
- * See if the Path structure describes the same directory as the
- * given one by comparing their names. Called from Dir_AddDir via
- * Lst_Find when searching the list of open directories.
- *
- * Input:
- * p Current name
- * dname Desired name
- *
- * Results:
- * 0 if it is the same. Non-zero otherwise
- *
- * Side Effects:
- * None
- *-----------------------------------------------------------------------
- */
-static int
-DirFindName(const void *p, const void *dname)
+/* See if the Path structure describes the same directory as the
+ * given one by comparing their names. Called from Dir_AddDir via
+ * Lst_Find when searching the list of open directories. */
+static Boolean
+DirFindName(const void *p, const void *desiredName)
{
- return strcmp(((const Path *)p)->name, dname);
+ return strcmp(((const Path *)p)->name, desiredName) == 0;
}
-/*-
- *-----------------------------------------------------------------------
- * 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!
+/* See if the given name has any wildcard characters in it. Be careful not to
+ * expand unmatching brackets or braces.
+ *
+ * XXX: This code is not 100% correct ([^]] fails etc.). I really don't think
+ * that make(1) should be expanding patterns, because then you have to set a
+ * mechanism for escaping the expansion!
*
* Input:
* name name to check
*
* Results:
* returns TRUE if the word should be expanded, FALSE otherwise
- *
- * Side Effects:
- * none
- *-----------------------------------------------------------------------
*/
Boolean
-Dir_HasWildcards(char *name)
+Dir_HasWildcards(const char *name)
{
- char *cp;
- int wild = 0, brace = 0, bracket = 0;
+ const char *cp;
+ Boolean wild = FALSE;
+ int braces = 0, brackets = 0;
for (cp = name; *cp; cp++) {
- switch(*cp) {
+ switch (*cp) {
case '{':
- brace++;
- wild = 1;
- break;
+ braces++;
+ wild = TRUE;
+ break;
case '}':
- brace--;
- break;
+ braces--;
+ break;
case '[':
- bracket++;
- wild = 1;
- break;
+ brackets++;
+ wild = TRUE;
+ break;
case ']':
- bracket--;
- break;
+ brackets--;
+ break;
case '?':
case '*':
- wild = 1;
- break;
+ wild = TRUE;
+ break;
default:
- break;
+ break;
}
}
- return wild && bracket == 0 && brace == 0;
+ return wild && brackets == 0 && braces == 0;
}
/*-
@@ -607,20 +568,17 @@ Dir_HasWildcards(char *name)
* p Directory to search
* expansion Place to store the results
*
- * Results:
- * Always returns 0
- *
* Side Effects:
* File names are added to the expansions lst. The directory will be
* fully hashed when this is done.
*-----------------------------------------------------------------------
*/
-static int
+static void
DirMatchFiles(const char *pattern, Path *p, Lst expansions)
{
- Hash_Search search; /* Index into the directory's table */
- Hash_Entry *entry; /* Current entry in the table */
- Boolean isDot; /* TRUE if the directory being searched is . */
+ 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');
@@ -638,13 +596,75 @@ DirMatchFiles(const char *pattern, Path *p, Lst expansions)
((entry->name[0] != '.') ||
(pattern[0] == '.')))
{
- (void)Lst_AtEnd(expansions,
- (isDot ? bmake_strdup(entry->name) :
- str_concat(p->name, entry->name,
- STR_ADDSLASH)));
+ Lst_Append(expansions,
+ (isDot ? bmake_strdup(entry->name) :
+ str_concat3(p->name, "/", entry->name)));
}
}
- return 0;
+}
+
+/* Find the next closing brace in the string, taking nested braces into
+ * account. */
+static const char *
+closing_brace(const char *p)
+{
+ int nest = 0;
+ while (*p != '\0') {
+ if (*p == '}' && nest == 0)
+ break;
+ if (*p == '{')
+ nest++;
+ if (*p == '}')
+ nest--;
+ p++;
+ }
+ return p;
+}
+
+/* Find the next closing brace or comma in the string, taking nested braces
+ * into account. */
+static const char *
+separator_comma(const char *p)
+{
+ int nest = 0;
+ while (*p != '\0') {
+ if ((*p == '}' || *p == ',') && nest == 0)
+ break;
+ if (*p == '{')
+ nest++;
+ if (*p == '}')
+ nest--;
+ p++;
+ }
+ return p;
+}
+
+static Boolean
+contains_wildcard(const char *p)
+{
+ for (; *p != '\0'; p++) {
+ switch (*p) {
+ case '*':
+ case '?':
+ case '{':
+ case '[':
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static char *
+concat3(const char *a, size_t a_len, const char *b, size_t b_len,
+ const char *c, size_t c_len)
+{
+ size_t s_len = a_len + b_len + c_len;
+ char *s = bmake_malloc(s_len + 1);
+ memcpy(s, a, a_len);
+ memcpy(s + a_len, b, b_len);
+ memcpy(s + a_len + b_len, c, c_len);
+ s[s_len] = '\0';
+ return s;
}
/*-
@@ -672,91 +692,41 @@ DirMatchFiles(const char *pattern, Path *p, Lst expansions)
static void
DirExpandCurly(const char *word, const char *brace, Lst path, Lst expansions)
{
- const char *end; /* Character after the closing brace */
- const char *cp; /* Current position in brace clause */
- const char *start; /* Start of current piece of brace clause */
- int bracelevel; /* Number of braces we've seen. If we see a
- * right brace when this is 0, we've hit the
- * end of the clause. */
- char *file; /* Current expansion */
- int otherLen; /* The length of the other pieces of the
- * expansion (chars before and after the
- * clause in 'word') */
- char *cp2; /* Pointer for checking for wildcards in
- * expansion before calling Dir_Expand */
-
- start = brace+1;
+ const char *prefix, *middle, *piece, *middle_end, *suffix;
+ size_t prefix_len, suffix_len;
- /*
- * Find the end of the brace clause first, being wary of nested brace
- * clauses.
- */
- for (end = start, bracelevel = 0; *end != '\0'; end++) {
- if (*end == '{') {
- bracelevel++;
- } else if ((*end == '}') && (bracelevel-- == 0)) {
- break;
- }
- }
- if (*end == '\0') {
- Error("Unterminated {} clause \"%s\"", start);
+ /* Split the word into prefix '{' middle '}' suffix. */
+
+ middle = brace + 1;
+ middle_end = closing_brace(middle);
+ if (*middle_end == '\0') {
+ Error("Unterminated {} clause \"%s\"", middle);
return;
- } else {
- end++;
}
- otherLen = brace - word + strlen(end);
- for (cp = start; cp < end; cp++) {
- /*
- * Find the end of this piece of the clause.
- */
- bracelevel = 0;
- while (*cp != ',') {
- if (*cp == '{') {
- bracelevel++;
- } else if ((*cp == '}') && (bracelevel-- <= 0)) {
- break;
- }
- cp++;
- }
- /*
- * Allocate room for the combination and install the three pieces.
- */
- file = bmake_malloc(otherLen + cp - start + 1);
- if (brace != word) {
- strncpy(file, word, brace-word);
- }
- if (cp != start) {
- strncpy(&file[brace-word], start, cp-start);
- }
- strcpy(&file[(brace-word)+(cp-start)], end);
+ prefix = word;
+ prefix_len = (size_t)(brace - prefix);
+ suffix = middle_end + 1;
+ suffix_len = strlen(suffix);
- /*
- * See if the result has any wildcards in it. If we find one, call
- * Dir_Expand right away, telling it to place the result on our list
- * of expansions.
- */
- for (cp2 = file; *cp2 != '\0'; cp2++) {
- switch(*cp2) {
- case '*':
- case '?':
- case '{':
- case '[':
- Dir_Expand(file, path, expansions);
- goto next;
- }
- }
- if (*cp2 == '\0') {
- /*
- * Hit the end w/o finding any wildcards, so stick the expansion
- * on the end of the list.
- */
- (void)Lst_AtEnd(expansions, file);
- } else {
- next:
+ /* Split the middle into pieces, separated by commas. */
+
+ piece = middle;
+ while (piece < middle_end + 1) {
+ const char *piece_end = separator_comma(piece);
+ size_t piece_len = (size_t)(piece_end - piece);
+
+ char *file = concat3(prefix, prefix_len, piece, piece_len,
+ suffix, suffix_len);
+
+ if (contains_wildcard(file)) {
+ Dir_Expand(file, path, expansions);
free(file);
+ } else {
+ Lst_Append(expansions, file);
}
- start = cp+1;
+
+ piece = piece_end + 1; /* skip over the comma or closing brace */
}
}
@@ -784,32 +754,18 @@ DirExpandCurly(const char *word, const char *brace, Lst path, Lst expansions)
static void
DirExpandInt(const char *word, Lst path, Lst expansions)
{
- LstNode ln; /* Current node */
- Path *p; /* Directory in the node */
+ LstNode ln; /* Current node */
- if (Lst_Open(path) == SUCCESS) {
- while ((ln = Lst_Next(path)) != NULL) {
- p = (Path *)Lst_Datum(ln);
- DirMatchFiles(word, p, expansions);
- }
- Lst_Close(path);
+ Lst_Open(path);
+ while ((ln = Lst_Next(path)) != NULL) {
+ Path *p = LstNode_Datum(ln);
+ DirMatchFiles(word, p, expansions);
}
+ Lst_Close(path);
}
-/*-
- *-----------------------------------------------------------------------
- * DirPrintWord --
- * Print a word in the list of expansions. Callback for Dir_Expand
- * when DEBUG(DIR), via Lst_ForEach.
- *
- * Results:
- * === 0
- *
- * Side Effects:
- * The passed word is printed, followed by a space.
- *
- *-----------------------------------------------------------------------
- */
+/* Print a word in the list of expansions.
+ * Callback for Dir_Expand when DEBUG(DIR), via Lst_ForEach. */
static int
DirPrintWord(void *word, void *dummy MAKE_ATTR_UNUSED)
{
@@ -836,16 +792,18 @@ DirPrintWord(void *word, void *dummy MAKE_ATTR_UNUSED)
*
* Side Effects:
* Directories may be opened. Who knows?
+ * Undefined behavior if the word is really in read-only memory.
*-----------------------------------------------------------------------
*/
void
Dir_Expand(const char *word, Lst path, Lst expansions)
{
- const char *cp;
+ const char *cp;
- if (DEBUG(DIR)) {
- fprintf(debug_file, "Expanding \"%s\"... ", word);
- }
+ assert(path != NULL);
+ assert(expansions != NULL);
+
+ DIR_DEBUG1("Expanding \"%s\"... ", word);
cp = strchr(word, '{');
if (cp) {
@@ -872,13 +830,12 @@ Dir_Expand(const char *word, Lst path, Lst expansions)
/*
* Back up to the start of the component
*/
- char *dirpath;
-
while (cp > word && *cp != '/') {
cp--;
}
if (cp != word) {
char sc;
+ char *dirpath;
/*
* If the glob isn't in the first component, try and find
* all the components up to the one with a wildcard.
@@ -898,10 +855,10 @@ Dir_Expand(const char *word, Lst path, Lst expansions)
char *dp = &dirpath[strlen(dirpath) - 1];
if (*dp == '/')
*dp = '\0';
- path = Lst_Init(FALSE);
+ path = Lst_Init();
(void)Dir_AddDir(path, dirpath);
- DirExpandInt(cp+1, path, expansions);
- Lst_Destroy(path, NULL);
+ DirExpandInt(cp + 1, path, expansions);
+ Lst_Free(path);
}
} else {
/*
@@ -948,21 +905,17 @@ Dir_Expand(const char *word, Lst path, Lst expansions)
*/
static char *
DirLookup(Path *p, const char *name MAKE_ATTR_UNUSED, const char *cp,
- Boolean hasSlash MAKE_ATTR_UNUSED)
+ Boolean hasSlash MAKE_ATTR_UNUSED)
{
- char *file; /* the current filename to check */
+ char *file; /* the current filename to check */
- if (DEBUG(DIR)) {
- fprintf(debug_file, " %s ...\n", p->name);
- }
+ DIR_DEBUG1(" %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);
- }
+ file = str_concat3(p->name, "/", cp);
+ DIR_DEBUG1(" returning %s\n", file);
p->hits += 1;
hits += 1;
return file;
@@ -986,11 +939,11 @@ DirLookup(Path *p, const char *name MAKE_ATTR_UNUSED, const char *cp,
static char *
DirLookupSubdir(Path *p, const char *name)
{
- struct stat stb; /* Buffer for stat, if necessary */
- char *file; /* the current filename to check */
+ struct make_stat mst;
+ char *file; /* the current filename to check */
if (p != dot) {
- file = str_concat(p->name, name, STR_ADDSLASH);
+ file = str_concat3(p->name, "/", name);
} else {
/*
* Checking in dot -- DON'T put a leading ./ on the thing.
@@ -998,11 +951,9 @@ DirLookupSubdir(Path *p, const char *name)
file = bmake_strdup(name);
}
- if (DEBUG(DIR)) {
- fprintf(debug_file, "checking %s ...\n", file);
- }
+ DIR_DEBUG1("checking %s ...\n", file);
- if (cached_stat(file, &stb) == 0) {
+ if (cached_stat(file, &mst) == 0) {
nearmisses += 1;
return file;
}
@@ -1028,40 +979,34 @@ DirLookupSubdir(Path *p, const char *name)
static char *
DirLookupAbs(Path *p, const char *name, const char *cp)
{
- char *p1; /* pointer into p->name */
- const char *p2; /* pointer into name */
+ char *p1; /* pointer into p->name */
+ const char *p2; /* pointer into name */
- if (DEBUG(DIR)) {
- fprintf(debug_file, " %s ...\n", p->name);
- }
+ DIR_DEBUG1(" %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 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("");
- }
+ if (Hash_FindEntry(&p->files, cp) == NULL) {
+ DIR_DEBUG0(" 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);
+ p->hits += 1;
+ hits += 1;
+ DIR_DEBUG1(" returning %s\n", name);
+ return bmake_strdup(name);
}
/*-
@@ -1081,25 +1026,20 @@ static char *
DirFindDot(Boolean hasSlash MAKE_ATTR_UNUSED, const char *name, const char *cp)
{
- if (Hash_FindEntry(&dot->files, cp) != NULL) {
- if (DEBUG(DIR)) {
- fprintf(debug_file, " in '.'\n");
- }
- hits += 1;
- dot->hits += 1;
- return bmake_strdup(name);
- }
- if (cur &&
- Hash_FindEntry(&cur->files, cp) != NULL) {
- if (DEBUG(DIR)) {
- fprintf(debug_file, " in ${.CURDIR} = %s\n", cur->name);
- }
- hits += 1;
- cur->hits += 1;
- return str_concat(cur->name, cp, STR_ADDSLASH);
- }
+ if (Hash_FindEntry(&dot->files, cp) != NULL) {
+ DIR_DEBUG0(" in '.'\n");
+ hits += 1;
+ dot->hits += 1;
+ return bmake_strdup(name);
+ }
+ if (cur && Hash_FindEntry(&cur->files, cp) != NULL) {
+ DIR_DEBUG1(" in ${.CURDIR} = %s\n", cur->name);
+ hits += 1;
+ cur->hits += 1;
+ return str_concat3(cur->name, "/", cp);
+ }
- return NULL;
+ return NULL;
}
/*-
@@ -1127,14 +1067,14 @@ DirFindDot(Boolean hasSlash MAKE_ATTR_UNUSED, const char *name, const char *cp)
char *
Dir_FindFile(const char *name, Lst path)
{
- LstNode ln; /* a list element */
- char *file; /* the current filename to check */
- Path *p; /* current path member */
- const char *cp; /* Terminal name of file */
- Boolean hasLastDot = FALSE; /* true we should search dot last */
- Boolean hasSlash; /* true if 'name' contains a / */
- struct stat stb; /* Buffer for stat, if necessary */
- const char *trailing_dot = ".";
+ 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 make_stat mst; /* Buffer for stat, if necessary */
+ const char *trailing_dot = ".";
/*
* Find the final component of the name and note whether it has a
@@ -1149,29 +1089,23 @@ Dir_FindFile(const char *name, Lst path)
cp = name;
}
- if (DEBUG(DIR)) {
- fprintf(debug_file, "Searching for %s ...", name);
- }
+ DIR_DEBUG1("Searching for %s ...", name);
- if (Lst_Open(path) == FAILURE) {
- if (DEBUG(DIR)) {
- fprintf(debug_file, "couldn't open path, file not found\n");
- }
+ if (path == NULL) {
+ DIR_DEBUG0("couldn't open path, file not found\n");
misses += 1;
return NULL;
}
+ Lst_Open(path);
if ((ln = Lst_First(path)) != NULL) {
- p = (Path *)Lst_Datum(ln);
+ p = LstNode_Datum(ln);
if (p == dotLast) {
hasLastDot = TRUE;
- if (DEBUG(DIR))
- fprintf(debug_file, "[dot last]...");
+ DIR_DEBUG0("[dot last]...");
}
}
- if (DEBUG(DIR)) {
- fprintf(debug_file, "\n");
- }
+ DIR_DEBUG0("\n");
/*
* If there's no leading directory components or if the leading
@@ -1179,41 +1113,39 @@ Dir_FindFile(const char *name, Lst path)
* of each of the directories on the search path.
*/
if (!hasSlash || (cp - name == 2 && *name == '.')) {
- /*
- * We look through all the directories on the path seeking one which
- * contains the final component of the given name. If such a beast
- * is found, we concatenate the directory name and the final
- * component and return the resulting string. If we don't find any
- * such thing, we go on to phase two...
- *
- * No matter what, we always look for the file in the current
- * directory before anywhere else (unless we found the magic
- * DOTLAST path, in which case we search it last) and we *do not*
- * add the ./ to it if it exists.
- * This is so there are no conflicts between what the user
- * specifies (fish.c) and what pmake finds (./fish.c).
- */
- if (!hasLastDot &&
- (file = DirFindDot(hasSlash, name, cp)) != NULL) {
- Lst_Close(path);
- return file;
- }
+ /*
+ * We look through all the directories on the path seeking one which
+ * contains the final component of the given name. If such a beast
+ * is found, we concatenate the directory name and the final
+ * component and return the resulting string. If we don't find any
+ * such thing, we go on to phase two...
+ *
+ * No matter what, we always look for the file in the current
+ * directory before anywhere else (unless we found the magic
+ * DOTLAST path, in which case we search it last) and we *do not*
+ * add the ./ to it if it exists.
+ * This is so there are no conflicts between what the user
+ * specifies (fish.c) and what pmake finds (./fish.c).
+ */
+ if (!hasLastDot && (file = DirFindDot(hasSlash, name, cp)) != NULL) {
+ Lst_Close(path);
+ return file;
+ }
- while ((ln = Lst_Next(path)) != NULL) {
- p = (Path *)Lst_Datum(ln);
- if (p == dotLast)
- continue;
- if ((file = DirLookup(p, name, cp, hasSlash)) != NULL) {
- Lst_Close(path);
- return file;
- }
+ while ((ln = Lst_Next(path)) != NULL) {
+ p = LstNode_Datum(ln);
+ if (p == dotLast)
+ continue;
+ if ((file = DirLookup(p, name, cp, hasSlash)) != NULL) {
+ Lst_Close(path);
+ return file;
}
+ }
- if (hasLastDot &&
- (file = DirFindDot(hasSlash, name, cp)) != NULL) {
- Lst_Close(path);
- return file;
- }
+ if (hasLastDot && (file = DirFindDot(hasSlash, name, cp)) != NULL) {
+ Lst_Close(path);
+ return file;
+ }
}
Lst_Close(path);
@@ -1232,9 +1164,7 @@ Dir_FindFile(const char *name, Lst path)
* This phase is only performed if the file is *not* absolute.
*/
if (!hasSlash) {
- if (DEBUG(DIR)) {
- fprintf(debug_file, " failed.\n");
- }
+ DIR_DEBUG0(" failed.\n");
misses += 1;
return NULL;
}
@@ -1245,30 +1175,28 @@ Dir_FindFile(const char *name, Lst path)
}
if (name[0] != '/') {
- Boolean checkedDot = FALSE;
+ Boolean checkedDot = FALSE;
- if (DEBUG(DIR)) {
- fprintf(debug_file, " Trying subdirectories...\n");
- }
+ DIR_DEBUG0(" Trying subdirectories...\n");
if (!hasLastDot) {
- if (dot) {
- checkedDot = TRUE;
- if ((file = DirLookupSubdir(dot, name)) != NULL)
- return file;
- }
- if (cur && (file = DirLookupSubdir(cur, name)) != NULL)
- return file;
+ 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);
+ Lst_Open(path);
while ((ln = Lst_Next(path)) != NULL) {
- p = (Path *)Lst_Datum(ln);
+ p = LstNode_Datum(ln);
if (p == dotLast)
continue;
if (p == dot) {
- if (checkedDot)
- continue;
+ if (checkedDot)
+ continue;
checkedDot = TRUE;
}
if ((file = DirLookupSubdir(p, name)) != NULL) {
@@ -1279,13 +1207,13 @@ Dir_FindFile(const char *name, Lst path)
Lst_Close(path);
if (hasLastDot) {
- if (dot && !checkedDot) {
- checkedDot = TRUE;
- if ((file = DirLookupSubdir(dot, name)) != NULL)
- return file;
- }
- if (cur && (file = DirLookupSubdir(cur, name)) != NULL)
- return file;
+ if (dot && !checkedDot) {
+ checkedDot = TRUE;
+ if ((file = DirLookupSubdir(dot, name)) != NULL)
+ return file;
+ }
+ if (cur && (file = DirLookupSubdir(cur, name)) != NULL)
+ return file;
}
if (checkedDot) {
@@ -1293,9 +1221,7 @@ Dir_FindFile(const char *name, Lst path)
* Already checked by the given name, since . was in the path,
* so no point in proceeding...
*/
- if (DEBUG(DIR)) {
- fprintf(debug_file, " Checked . already, returning NULL\n");
- }
+ DIR_DEBUG0(" Checked . already, returning NULL\n");
return NULL;
}
@@ -1310,12 +1236,10 @@ Dir_FindFile(const char *name, Lst path)
* 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");
- }
+ DIR_DEBUG0(" Trying exact path matches...\n");
- if (!hasLastDot && cur && ((file = DirLookupAbs(cur, name, cp))
- != NULL)) {
+ if (!hasLastDot && cur &&
+ ((file = DirLookupAbs(cur, name, cp)) != NULL)) {
if (file[0] == '\0') {
free(file);
return NULL;
@@ -1323,9 +1247,9 @@ Dir_FindFile(const char *name, Lst path)
return file;
}
- (void)Lst_Open(path);
+ Lst_Open(path);
while ((ln = Lst_Next(path)) != NULL) {
- p = (Path *)Lst_Datum(ln);
+ p = LstNode_Datum(ln);
if (p == dotLast)
continue;
if ((file = DirLookupAbs(p, name, cp)) != NULL) {
@@ -1339,8 +1263,8 @@ Dir_FindFile(const char *name, Lst path)
}
Lst_Close(path);
- if (hasLastDot && cur && ((file = DirLookupAbs(cur, name, cp))
- != NULL)) {
+ if (hasLastDot && cur &&
+ ((file = DirLookupAbs(cur, name, cp)) != NULL)) {
if (file[0] == '\0') {
free(file);
return NULL;
@@ -1380,7 +1304,7 @@ Dir_FindFile(const char *name, Lst path)
if (ln == NULL) {
return NULL;
} else {
- p = (Path *)Lst_Datum(ln);
+ p = LstNode_Datum(ln);
}
if (Hash_FindEntry(&p->files, cp) != NULL) {
@@ -1389,18 +1313,14 @@ Dir_FindFile(const char *name, Lst path)
return NULL;
}
#else /* !notdef */
- if (DEBUG(DIR)) {
- fprintf(debug_file, " Looking for \"%s\" ...\n", name);
- }
+ DIR_DEBUG1(" Looking for \"%s\" ...\n", name);
bigmisses += 1;
- if (cached_stat(name, &stb) == 0) {
+ if (cached_stat(name, &mst) == 0) {
return bmake_strdup(name);
}
- if (DEBUG(DIR)) {
- fprintf(debug_file, " failed. Returning NULL\n");
- }
+ DIR_DEBUG0(" failed. Returning NULL\n");
return NULL;
#endif /* notdef */
}
@@ -1416,7 +1336,7 @@ Dir_FindFile(const char *name, Lst path)
* 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
+ * result_len the length of the result buffer
* (typically MAXPATHLEN + 1)
*
* Results:
@@ -1426,62 +1346,57 @@ Dir_FindFile(const char *name, Lst path)
* 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) {
+Boolean
+Dir_FindHereOrAbove(const char *here, const char *search_path,
+ char *result, int result_len)
+{
+ struct make_stat mst;
+ char dirbase[MAXPATHLEN + 1], *dirbase_end;
+ char try[MAXPATHLEN + 1], *try_end;
- /* try and stat(2) it ... */
- snprintf(try, sizeof(try), "%s/%s", dirbase, search_path);
- if (cached_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! */
- }
+ /* copy out our starting point */
+ snprintf(dirbase, sizeof(dirbase), "%s", here);
+ dirbase_end = dirbase + strlen(dirbase);
- /*
- * done!
- */
- snprintf(result, rlen, "%s", try);
- return 1;
- }
+ /* loop until we determine a result */
+ while (TRUE) {
- /*
- * nope, we didn't find it. if we used up dirbase we've
- * reached the root and failed.
- */
- if (db_end == dirbase)
- break; /* failed! */
+ /* try and stat(2) it ... */
+ snprintf(try, sizeof(try), "%s/%s", dirbase, search_path);
+ if (cached_stat(try, &mst) != -1) {
+ /*
+ * success! if we found a file, chop off
+ * the filename so we return a directory.
+ */
+ if ((mst.mst_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! */
+ }
- /*
- * truncate dirbase from the end to move up a dir
- */
- while (db_end > dirbase && *db_end != '/')
- db_end--;
- *db_end = 0; /* chop! */
+ snprintf(result, result_len, "%s", try);
+ return TRUE;
+ }
- } /* while (1) */
+ /*
+ * nope, we didn't find it. if we used up dirbase we've
+ * reached the root and failed.
+ */
+ if (dirbase_end == dirbase)
+ break; /* failed! */
/*
- * we failed...
+ * truncate dirbase from the end to move up a dir
*/
- return 0;
+ while (dirbase_end > dirbase && *dirbase_end != '/')
+ dirbase_end--;
+ *dirbase_end = '\0'; /* chop! */
+
+ } /* while (TRUE) */
+
+ return FALSE;
}
/*-
@@ -1505,8 +1420,8 @@ Dir_FindHereOrAbove(char *here, char *search_path, char *result, int rlen) {
int
Dir_MTime(GNode *gn, Boolean recheck)
{
- char *fullName; /* the full pathname of name */
- struct stat stb; /* buffer for finding the mod time */
+ char *fullName; /* the full pathname of name */
+ struct make_stat mst; /* buffer for finding the mod time */
if (gn->type & OP_ARCHV) {
return Arch_MTime(gn);
@@ -1519,7 +1434,7 @@ Dir_MTime(GNode *gn, Boolean recheck)
else {
fullName = Dir_FindFile(gn->name, Suff_FindPath(gn));
if (fullName == NULL && gn->flags & FROM_DEPEND &&
- !Lst_IsEmpty(gn->iParents)) {
+ !Lst_IsEmpty(gn->implicitParents)) {
char *cp;
cp = strrchr(gn->name, '/');
@@ -1539,15 +1454,15 @@ Dir_MTime(GNode *gn, Boolean recheck)
gn->path = bmake_strdup(fullName);
if (!Job_RunTarget(".STALE", gn->fname))
fprintf(stdout,
- "%s: %s, %d: ignoring stale %s for %s, "
- "found %s\n", progname, gn->fname, gn->lineno,
- makeDependfile, gn->name, fullName);
+ "%s: %s, %d: ignoring stale %s for %s, "
+ "found %s\n", progname, gn->fname,
+ gn->lineno,
+ makeDependfile, gn->name, fullName);
}
}
}
- if (DEBUG(DIR))
- fprintf(debug_file, "Found '%s' as '%s'\n",
- gn->name, fullName ? fullName : "(not found)" );
+ DIR_DEBUG2("Found '%s' as '%s'\n",
+ gn->name, fullName ? fullName : "(not found)");
}
} else {
fullName = gn->path;
@@ -1557,13 +1472,13 @@ Dir_MTime(GNode *gn, Boolean recheck)
fullName = bmake_strdup(gn->name);
}
- if (cached_stats(&mtimes, fullName, &stb, recheck ? CST_UPDATE : 0) < 0) {
+ if (cached_stats(&mtimes, fullName, &mst, recheck ? CST_UPDATE : 0) < 0) {
if (gn->type & OP_MEMBER) {
if (fullName != gn->path)
free(fullName);
return Arch_MemMTime(gn);
} else {
- stb.st_mtime = 0;
+ mst.mst_mtime = 0;
}
}
@@ -1571,7 +1486,7 @@ Dir_MTime(GNode *gn, Boolean recheck)
gn->path = fullName;
}
- gn->mtime = stb.st_mtime;
+ gn->mtime = mst.mst_mtime;
return gn->mtime;
}
@@ -1585,6 +1500,8 @@ Dir_MTime(GNode *gn, Boolean recheck)
* Input:
* path the path to which the directory should be
* added
+ * XXX: Why would this ever be NULL, and what does
+ * that mean?
* name the name of the directory to add
*
* Results:
@@ -1598,78 +1515,70 @@ Dir_MTime(GNode *gn, Boolean recheck)
Path *
Dir_AddDir(Lst path, const char *name)
{
- LstNode ln = NULL; /* node in case Path structure is found */
- Path *p = NULL; /* pointer to new Path structure */
- DIR *d; /* for reading directory */
- struct dirent *dp; /* entry in directory */
+ LstNode ln = NULL; /* node in case Path structure is found */
+ Path *p = NULL; /* pointer to new Path structure */
+ DIR *d; /* for reading directory */
+ struct dirent *dp; /* entry in directory */
- if (strcmp(name, ".DOTLAST") == 0) {
- ln = Lst_Find(path, name, DirFindName);
+ if (path != NULL && strcmp(name, ".DOTLAST") == 0) {
+ ln = Lst_Find(path, DirFindName, name);
if (ln != NULL)
- return (Path *)Lst_Datum(ln);
- else {
- dotLast->refCount += 1;
- (void)Lst_AtFront(path, dotLast);
- }
+ return LstNode_Datum(ln);
+
+ dotLast->refCount++;
+ Lst_Prepend(path, dotLast);
}
- if (path)
- ln = Lst_Find(openDirectories, name, DirFindName);
+ if (path != NULL)
+ ln = Lst_Find(openDirectories, DirFindName, name);
if (ln != NULL) {
- p = (Path *)Lst_Datum(ln);
- if (path && Lst_Member(path, p) == NULL) {
+ p = LstNode_Datum(ln);
+ if (Lst_FindDatum(path, p) == NULL) {
p->refCount += 1;
- (void)Lst_AtEnd(path, p);
- }
- } else {
- if (DEBUG(DIR)) {
- fprintf(debug_file, "Caching %s ...", name);
+ Lst_Append(path, p);
}
+ return p;
+ }
+
+ DIR_DEBUG1("Caching %s ...", name);
- if ((d = opendir(name)) != NULL) {
- p = bmake_malloc(sizeof(Path));
- p->name = bmake_strdup(name);
- p->hits = 0;
- p->refCount = 1;
- Hash_InitTable(&p->files, -1);
+ if ((d = opendir(name)) != NULL) {
+ p = bmake_malloc(sizeof(Path));
+ p->name = bmake_strdup(name);
+ p->hits = 0;
+ p->refCount = 1;
+ Hash_InitTable(&p->files, -1);
- while ((dp = readdir(d)) != NULL) {
+ while ((dp = readdir(d)) != NULL) {
#if defined(sun) && defined(d_ino) /* d_ino is a sunos4 #define for d_fileno */
- /*
- * The sun directory library doesn't check for a 0 inode
- * (0-inode slots just take up space), so we have to do
- * it ourselves.
- */
- if (dp->d_fileno == 0) {
- continue;
- }
-#endif /* sun && d_ino */
- (void)Hash_CreateEntry(&p->files, dp->d_name, NULL);
+ /*
+ * The sun directory library doesn't check for a 0 inode
+ * (0-inode slots just take up space), so we have to do
+ * it ourselves.
+ */
+ if (dp->d_fileno == 0) {
+ continue;
}
- (void)closedir(d);
- (void)Lst_AtEnd(openDirectories, p);
- if (path != NULL)
- (void)Lst_AtEnd(path, p);
- }
- if (DEBUG(DIR)) {
- fprintf(debug_file, "done\n");
+#endif /* sun && d_ino */
+ (void)Hash_CreateEntry(&p->files, dp->d_name, NULL);
}
+ (void)closedir(d);
+ Lst_Append(openDirectories, p);
+ if (path != NULL)
+ Lst_Append(path, p);
}
+ DIR_DEBUG0("done\n");
return p;
}
/*-
*-----------------------------------------------------------------------
* Dir_CopyDir --
- * Callback function for duplicating a search path via Lst_Duplicate.
+ * Callback function for duplicating a search path via Lst_Copy.
* Ups the reference count for the directory.
*
* Results:
* Returns the Path it was given.
- *
- * Side Effects:
- * The refCount of the path is incremented.
- *
*-----------------------------------------------------------------------
*/
void *
@@ -1704,25 +1613,23 @@ Dir_CopyDir(void *p)
char *
Dir_MakeFlags(const char *flag, Lst path)
{
- char *str; /* the string which will be returned */
- char *s1, *s2;/* the current directory preceded by 'flag' */
- LstNode ln; /* the node of the current directory */
- Path *p; /* the structure describing the current directory */
+ Buffer buf;
+ LstNode ln; /* the node of the current directory */
- str = bmake_strdup("");
+ Buf_Init(&buf, 0);
- if (Lst_Open(path) == SUCCESS) {
+ if (path != NULL) {
+ Lst_Open(path);
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);
+ Path *p = LstNode_Datum(ln);
+ Buf_AddStr(&buf, " ");
+ Buf_AddStr(&buf, flag);
+ Buf_AddStr(&buf, p->name);
}
Lst_Close(path);
}
- return str;
+ return Buf_Destroy(&buf, FALSE);
}
/*-
@@ -1746,14 +1653,14 @@ Dir_MakeFlags(const char *flag, Lst path)
void
Dir_Destroy(void *pp)
{
- Path *p = (Path *)pp;
+ Path *p = (Path *)pp;
p->refCount -= 1;
if (p->refCount == 0) {
- LstNode ln;
+ LstNode ln;
- ln = Lst_Member(openDirectories, p);
- (void)Lst_Remove(openDirectories, ln);
+ ln = Lst_FindDatum(openDirectories, p);
+ Lst_Remove(openDirectories, ln);
Hash_DeleteTable(&p->files);
free(p->name);
@@ -1781,9 +1688,8 @@ Dir_Destroy(void *pp)
void
Dir_ClearPath(Lst path)
{
- Path *p;
while (!Lst_IsEmpty(path)) {
- p = (Path *)Lst_DeQueue(path);
+ Path *p = Lst_Dequeue(path);
Dir_Destroy(p);
}
}
@@ -1811,37 +1717,43 @@ void
Dir_Concat(Lst path1, Lst path2)
{
LstNode ln;
- Path *p;
+ Path *p;
- for (ln = Lst_First(path2); ln != NULL; ln = Lst_Succ(ln)) {
- p = (Path *)Lst_Datum(ln);
- if (Lst_Member(path1, p) == NULL) {
+ for (ln = Lst_First(path2); ln != NULL; ln = LstNode_Next(ln)) {
+ p = LstNode_Datum(ln);
+ if (Lst_FindDatum(path1, p) == NULL) {
p->refCount += 1;
- (void)Lst_AtEnd(path1, p);
+ Lst_Append(path1, p);
}
}
}
+static int
+percentage(int num, int den)
+{
+ return den != 0 ? num * 100 / den : 0;
+}
+
/********** DEBUG INFO **********/
void
Dir_PrintDirectories(void)
{
- LstNode ln;
- Path *p;
+ LstNode ln;
fprintf(debug_file, "#*** Directory Cache:\n");
- fprintf(debug_file, "# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n",
- hits, misses, nearmisses, bigmisses,
- (hits+bigmisses+nearmisses ?
- hits * 100 / (hits + bigmisses + nearmisses) : 0));
+ fprintf(debug_file,
+ "# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n",
+ hits, misses, nearmisses, bigmisses,
+ percentage(hits, hits + bigmisses + nearmisses));
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_Open(openDirectories);
+ while ((ln = Lst_Next(openDirectories)) != NULL) {
+ Path *p = LstNode_Datum(ln);
+ fprintf(debug_file, "# %-20s %10d\t%4d\n", p->name, p->refCount,
+ p->hits);
}
+ Lst_Close(openDirectories);
}
static int
diff --git a/contrib/bmake/dir.h b/contrib/bmake/dir.h
index 52ab35e2fd25..10da0eb8ba78 100644
--- a/contrib/bmake/dir.h
+++ b/contrib/bmake/dir.h
@@ -1,4 +1,4 @@
-/* $NetBSD: dir.h,v 1.18 2017/05/31 22:02:06 maya Exp $ */
+/* $NetBSD: dir.h,v 1.23 2020/09/02 04:08:54 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -72,29 +72,34 @@
* from: @(#)dir.h 8.1 (Berkeley) 6/6/93
*/
-/* dir.h --
- */
-
#ifndef MAKE_DIR_H
#define MAKE_DIR_H
-typedef struct Path {
+/* A cache of a directory, remembering all the files that exist in that
+ * directory. */
+typedef struct {
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 */
+ Hash_Table files; /* Hash set of files in directory */
} Path;
-void Dir_Init(const char *);
+struct make_stat {
+ time_t mst_mtime;
+ mode_t mst_mode;
+};
+
+void Dir_Init(void);
+void Dir_InitDir(const char *);
void Dir_InitCur(const char *);
void Dir_InitDot(void);
void Dir_End(void);
void Dir_SetPATH(void);
-Boolean Dir_HasWildcards(char *);
+Boolean Dir_HasWildcards(const char *);
void Dir_Expand(const char *, Lst, Lst);
char *Dir_FindFile(const char *, Lst);
-int Dir_FindHereOrAbove(char *, char *, char *, int);
+Boolean Dir_FindHereOrAbove(const char *, const char *, char *, int);
int Dir_MTime(GNode *, Boolean);
Path *Dir_AddDir(Lst, const char *);
char *Dir_MakeFlags(const char *, Lst);
@@ -103,6 +108,9 @@ void Dir_Concat(Lst, Lst);
void Dir_PrintDirectories(void);
void Dir_PrintPath(Lst);
void Dir_Destroy(void *);
-void * Dir_CopyDir(void *);
+void *Dir_CopyDir(void *);
+
+int cached_lstat(const char *, struct make_stat *);
+int cached_stat(const char *, struct make_stat *);
#endif /* MAKE_DIR_H */
diff --git a/contrib/bmake/enum.c b/contrib/bmake/enum.c
new file mode 100755
index 000000000000..9dec4f3a5f6a
--- /dev/null
+++ b/contrib/bmake/enum.c
@@ -0,0 +1,100 @@
+/* $NetBSD: enum.c,v 1.6 2020/09/01 20:34:51 rillig Exp $ */
+
+/*
+ Copyright (c) 2020 Roland Illig <rillig@NetBSD.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must 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 HOLDER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (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: enum.c,v 1.6 2020/09/01 20:34:51 rillig Exp $";
+#else
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: enum.c,v 1.6 2020/09/01 20:34:51 rillig Exp $");
+#endif
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "enum.h"
+
+/* Convert a bitset into a string representation, showing the names of the
+ * individual bits.
+ *
+ * Optionally, shortcuts for groups of bits can be added. To have an effect,
+ * they need to be listed before their individual bits. */
+const char *
+Enum_FlagsToString(char *buf, size_t buf_size,
+ int value, const EnumToStringSpec *spec)
+{
+ const char *buf_start = buf;
+ const char *sep = "";
+ size_t sep_len = 0;
+
+ for (; spec->es_value != 0; spec++) {
+ size_t name_len;
+
+ if ((value & spec->es_value) != spec->es_value)
+ continue;
+ value &= ~spec->es_value;
+
+ assert(buf_size >= sep_len + 1);
+ memcpy(buf, sep, sep_len);
+ buf += sep_len;
+ buf_size -= sep_len;
+
+ name_len = strlen(spec->es_name);
+ assert(buf_size >= name_len + 1);
+ memcpy(buf, spec->es_name, name_len);
+ buf += name_len;
+ buf_size -= name_len;
+
+ sep = ENUM__SEP;
+ sep_len = sizeof ENUM__SEP - 1;
+ }
+
+ /* If this assertion fails, the listed enum values are incomplete. */
+ assert(value == 0);
+
+ if (buf == buf_start)
+ return "none";
+
+ assert(buf_size >= 1);
+ buf[0] = '\0';
+ return buf_start;
+}
+
+/* Convert a fixed-value enum into a string representation. */
+const char *
+Enum_ValueToString(int value, const EnumToStringSpec *spec)
+{
+ for (; spec->es_name[0] != '\0'; spec++) {
+ if (value == spec->es_value)
+ return spec->es_name;
+ }
+ abort(/* unknown enum value */);
+}
diff --git a/contrib/bmake/enum.h b/contrib/bmake/enum.h
new file mode 100755
index 000000000000..d6a94f11e7cd
--- /dev/null
+++ b/contrib/bmake/enum.h
@@ -0,0 +1,193 @@
+/* $NetBSD: enum.h,v 1.9 2020/09/01 20:34:51 rillig Exp $ */
+
+/*
+ Copyright (c) 2020 Roland Illig <rillig@NetBSD.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must 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 HOLDER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (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_ENUM_H
+#define MAKE_ENUM_H
+
+/* Generate string representations for bitmasks and simple enums. */
+
+#include <stddef.h>
+
+typedef struct {
+ int es_value;
+ const char *es_name;
+} EnumToStringSpec;
+
+const char *Enum_FlagsToString(char *, size_t, int, const EnumToStringSpec *);
+const char *Enum_ValueToString(int, const EnumToStringSpec *);
+
+/* For Enum_FlagsToString, the separator between flags. */
+#define ENUM__SEP "|"
+
+/* Generate the string that joins all possible flags, to see how large the
+ * buffer must be. */
+#define ENUM__JOIN_STR_1(v1) \
+ #v1
+#define ENUM__JOIN_STR_2(v1, v2) \
+ ENUM__JOIN_STR_1(v1) ENUM__SEP \
+ ENUM__JOIN_STR_1(v2)
+#define ENUM__JOIN_STR_4(v1, v2, v3, v4) \
+ ENUM__JOIN_STR_2(v1, v2) ENUM__SEP \
+ ENUM__JOIN_STR_2(v3, v4)
+#define ENUM__JOIN_STR_8(v1, v2, v3, v4, v5, v6, v7, v8) \
+ ENUM__JOIN_STR_4(v1, v2, v3, v4) ENUM__SEP \
+ ENUM__JOIN_STR_4(v5, v6, v7, v8)
+#define ENUM__JOIN_STR_16(v01, v02, v03, v04, v05, v06, v07, v08, \
+ v09, v10, v11, v12, v13, v14, v15, v16) \
+ ENUM__JOIN_STR_8(v01, v02, v03, v04, v05, v06, v07, v08) ENUM__SEP \
+ ENUM__JOIN_STR_8(v09, v10, v11, v12, v13, v14, v15, v16)
+
+#define ENUM__JOIN_2(part1, part2) \
+ part1 ENUM__SEP part2
+#define ENUM__JOIN_3(part1, part2, part3) \
+ part1 ENUM__SEP part2 ENUM__SEP part3
+#define ENUM__JOIN_4(part1, part2, part3, part4) \
+ part1 ENUM__SEP part2 ENUM__SEP part3 ENUM__SEP part4
+#define ENUM__JOIN_5(part1, part2, part3, part4, part5) \
+ part1 ENUM__SEP part2 ENUM__SEP part3 ENUM__SEP part4 ENUM__SEP part5
+
+/* List the pairs of enum value and corresponding name. */
+#define ENUM__SPEC_1(v1) \
+ { v1, #v1 }
+#define ENUM__SPEC_2(v1, v2) \
+ ENUM__SPEC_1(v1), \
+ ENUM__SPEC_1(v2)
+#define ENUM__SPEC_4(v1, v2, v3, v4) \
+ ENUM__SPEC_2(v1, v2), \
+ ENUM__SPEC_2(v3, v4)
+#define ENUM__SPEC_8(v1, v2, v3, v4, v5, v6, v7, v8) \
+ ENUM__SPEC_4(v1, v2, v3, v4), \
+ ENUM__SPEC_4(v5, v6, v7, v8)
+#define ENUM__SPEC_16(v01, v02, v03, v04, v05, v06, v07, v08, \
+ v09, v10, v11, v12, v13, v14, v15, v16) \
+ ENUM__SPEC_8(v01, v02, v03, v04, v05, v06, v07, v08), \
+ ENUM__SPEC_8(v09, v10, v11, v12, v13, v14, v15, v16)
+
+#define ENUM__SPECS_2(part1, part2) \
+ { part1, part2, { 0, "" } }
+#define ENUM__SPECS_3(part1, part2, part3) \
+ { part1, part2, part3, { 0, "" } }
+#define ENUM__SPECS_4(part1, part2, part3, part4) \
+ { part1, part2, part3, part4, { 0, "" } }
+#define ENUM__SPECS_5(part1, part2, part3, part4, part5) \
+ { part1, part2, part3, part4, part5, { 0, "" } }
+
+/* Declare the necessary data structures for calling Enum_ValueToString. */
+#define ENUM__VALUE_RTTI(typnam, specs) \
+ static const EnumToStringSpec typnam ## _ ## ToStringSpecs[] = specs
+
+/* Declare the necessary data structures for calling Enum_FlagsToString. */
+#define ENUM__FLAGS_RTTI(typnam, specs, joined) \
+ static const EnumToStringSpec typnam ## _ ## ToStringSpecs[] = specs; \
+ enum { typnam ## _ ## ToStringSize = sizeof joined }
+
+/* Declare the necessary data structures for calling Enum_FlagsToString
+ * for an enum with 3 flags. */
+#define ENUM_FLAGS_RTTI_3(typnam, v1, v2, v3) \
+ ENUM__FLAGS_RTTI(typnam, \
+ ENUM__SPECS_2( \
+ ENUM__SPEC_2(v1, v2), \
+ ENUM__SPEC_1(v3)), \
+ ENUM__JOIN_2( \
+ ENUM__JOIN_STR_2(v1, v2), \
+ ENUM__JOIN_STR_1(v3)))
+
+/* Declare the necessary data structures for calling Enum_FlagsToString
+ * for an enum with 8 flags. */
+#define ENUM_FLAGS_RTTI_8(typnam, v1, v2, v3, v4, v5, v6, v7, v8) \
+ ENUM__FLAGS_RTTI(typnam, \
+ ENUM__SPECS_2( \
+ ENUM__SPEC_4(v1, v2, v3, v4), \
+ ENUM__SPEC_4(v5, v6, v7, v8)), \
+ ENUM__JOIN_2( \
+ ENUM__JOIN_STR_4(v1, v2, v3, v4), \
+ ENUM__JOIN_STR_4(v5, v6, v7, v8)))
+
+/* Declare the necessary data structures for calling Enum_ValueToString
+ * for an enum with 8 constants. */
+#define ENUM_VALUE_RTTI_8(typnam, v1, v2, v3, v4, v5, v6, v7, v8) \
+ ENUM__VALUE_RTTI(typnam, \
+ ENUM__SPECS_2( \
+ ENUM__SPEC_4(v1, v2, v3, v4), \
+ ENUM__SPEC_4(v5, v6, v7, v8)))
+
+/* Declare the necessary data structures for calling Enum_FlagsToString
+ * for an enum with 10 flags. */
+#define ENUM_FLAGS_RTTI_10(typnam, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) \
+ ENUM__FLAGS_RTTI(typnam, \
+ ENUM__SPECS_2( \
+ ENUM__SPEC_8(v1, v2, v3, v4, v5, v6, v7, v8), \
+ ENUM__SPEC_2(v9, v10)), \
+ ENUM__JOIN_2( \
+ ENUM__JOIN_STR_8(v1, v2, v3, v4, v5, v6, v7, v8), \
+ ENUM__JOIN_STR_2(v9, v10)))
+
+/* Declare the necessary data structures for calling Enum_FlagsToString
+ * for an enum with 31 flags. */
+#define ENUM_FLAGS_RTTI_31(typnam, \
+ v01, v02, v03, v04, v05, v06, v07, v08, \
+ v09, v10, v11, v12, v13, v14, v15, v16, \
+ v17, v18, v19, v20, v21, v22, v23, v24, \
+ v25, v26, v27, v28, v29, v30, v31) \
+ ENUM__FLAGS_RTTI(typnam, \
+ ENUM__SPECS_5( \
+ ENUM__SPEC_16(v01, v02, v03, v04, v05, v06, v07, v08, \
+ v09, v10, v11, v12, v13, v14, v15, v16), \
+ ENUM__SPEC_8(v17, v18, v19, v20, v21, v22, v23, v24), \
+ ENUM__SPEC_4(v25, v26, v27, v28), \
+ ENUM__SPEC_2(v29, v30), \
+ ENUM__SPEC_1(v31)), \
+ ENUM__JOIN_5( \
+ ENUM__JOIN_STR_16(v01, v02, v03, v04, v05, v06, v07, v08, \
+ v09, v10, v11, v12, v13, v14, v15, v16), \
+ ENUM__JOIN_STR_8(v17, v18, v19, v20, v21, v22, v23, v24), \
+ ENUM__JOIN_STR_4(v25, v26, v27, v28), \
+ ENUM__JOIN_STR_2(v29, v30), \
+ ENUM__JOIN_STR_1(v31)))
+
+/* Declare the necessary data structures for calling Enum_FlagsToString
+ * for an enum with 32 flags. */
+#define ENUM_FLAGS_RTTI_32(typnam, \
+ v01, v02, v03, v04, v05, v06, v07, v08, \
+ v09, v10, v11, v12, v13, v14, v15, v16, \
+ v17, v18, v19, v20, v21, v22, v23, v24, \
+ v25, v26, v27, v28, v29, v30, v31, v32) \
+ ENUM__FLAGS_RTTI(typnam, \
+ ENUM__SPECS_2( \
+ ENUM__SPEC_16(v01, v02, v03, v04, v05, v06, v07, v08, \
+ v09, v10, v11, v12, v13, v14, v15, v16), \
+ ENUM__SPEC_16(v17, v18, v19, v20, v21, v22, v23, v24, \
+ v25, v26, v27, v28, v29, v30, v31, v32)), \
+ ENUM__JOIN_2( \
+ ENUM__JOIN_STR_16(v01, v02, v03, v04, v05, v06, v07, v08, \
+ v09, v10, v11, v12, v13, v14, v15, v16), \
+ ENUM__JOIN_STR_16(v17, v18, v19, v20, v21, v22, v23, v24, \
+ v25, v26, v27, v28, v29, v30, v31, v32)))
+
+#endif
diff --git a/contrib/bmake/for.c b/contrib/bmake/for.c
index f20a0f58b06b..2e9963f2a7f6 100644
--- a/contrib/bmake/for.c
+++ b/contrib/bmake/for.c
@@ -1,4 +1,4 @@
-/* $NetBSD: for.c,v 1.54 2020/07/03 08:13:23 rillig Exp $ */
+/* $NetBSD: for.c,v 1.67 2020/08/30 19:56:02 rillig Exp $ */
/*
* Copyright (c) 1992, The Regents of the University of California.
@@ -30,14 +30,14 @@
*/
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: for.c,v 1.54 2020/07/03 08:13:23 rillig Exp $";
+static char rcsid[] = "$NetBSD: for.c,v 1.67 2020/08/30 19:56:02 rillig Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)for.c 8.1 (Berkeley) 6/6/93";
#else
-__RCSID("$NetBSD: for.c,v 1.54 2020/07/03 08:13:23 rillig Exp $");
+__RCSID("$NetBSD: for.c,v 1.67 2020/08/30 19:56:02 rillig Exp $");
#endif
#endif /* not lint */
#endif
@@ -52,13 +52,7 @@ __RCSID("$NetBSD: for.c,v 1.54 2020/07/03 08:13:23 rillig Exp $");
*
*/
-#include <assert.h>
-#include <ctype.h>
-
#include "make.h"
-#include "hash.h"
-#include "dir.h"
-#include "buf.h"
#include "strlist.h"
#define FOR_SUB_ESCAPE_CHAR 1
@@ -83,34 +77,22 @@ __RCSID("$NetBSD: for.c,v 1.54 2020/07/03 08:13:23 rillig Exp $");
* For_Run.
*/
-static int forLevel = 0; /* Nesting level */
+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;
+typedef struct {
+ 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;
+static For *accumFor; /* Loop being accumulated */
- new_ptr = bmake_malloc(len + 1);
- memcpy(new_ptr, ptr, len);
- new_ptr[len] = 0;
- return new_ptr;
-}
static void
For_Free(For *arg)
@@ -148,14 +130,13 @@ For_Eval(char *line)
{
For *new_for;
char *ptr = line, *sub;
- int len;
+ size_t len;
int escapes;
unsigned char ch;
- char **words, *word_buf;
- int n, nwords;
+ Words words;
/* Skip the '.' and any following whitespace */
- for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++)
+ for (ptr++; *ptr && isspace((unsigned char)*ptr); ptr++)
continue;
/*
@@ -163,8 +144,8 @@ For_Eval(char *line)
* 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) {
+ !isspace((unsigned char)ptr[3])) {
+ if (ptr[0] == 'e' && strncmp(ptr + 1, "ndfor", 5) == 0) {
Parse_Error(PARSE_FATAL, "for-less endfor");
return -1;
}
@@ -181,7 +162,7 @@ For_Eval(char *line)
/* Grab the variables. Terminate on "in". */
for (;; ptr += len) {
- while (*ptr && isspace((unsigned char) *ptr))
+ while (*ptr && isspace((unsigned char)*ptr))
ptr++;
if (*ptr == '\0') {
Parse_Error(PARSE_FATAL, "missing `in' in for");
@@ -196,7 +177,7 @@ For_Eval(char *line)
}
if (len == 1)
new_for->short_var = 1;
- strlist_add_str(&new_for->vars, make_str(ptr, len), len);
+ strlist_add_str(&new_for->vars, bmake_strldup(ptr, len), len);
}
if (strlist_num(&new_for->vars) == 0) {
@@ -205,7 +186,7 @@ For_Eval(char *line)
return -1;
}
- while (*ptr && isspace((unsigned char) *ptr))
+ while (*ptr && isspace((unsigned char)*ptr))
ptr++;
/*
@@ -216,23 +197,25 @@ For_Eval(char *line)
* 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, VARF_WANTRES);
+ sub = Var_Subst(ptr, VAR_GLOBAL, VARE_WANTRES);
/*
* Split into words allowing for quoted strings.
*/
- words = brk_string(sub, &nwords, FALSE, &word_buf);
+ words = Str_Words(sub, FALSE);
free(sub);
- if (words != NULL) {
- for (n = 0; n < nwords; n++) {
- ptr = words[n];
+ {
+ size_t n;
+
+ for (n = 0; n < words.len; n++) {
+ ptr = words.words[n];
if (!*ptr)
continue;
escapes = 0;
while ((ch = *ptr++)) {
- switch(ch) {
+ switch (ch) {
case ':':
case '$':
case '\\':
@@ -250,17 +233,17 @@ For_Eval(char *line)
* We have to dup words[n] to maintain the semantics of
* strlist.
*/
- strlist_add_str(&new_for->items, bmake_strdup(words[n]), escapes);
+ strlist_add_str(&new_for->items, bmake_strdup(words.words[n]),
+ escapes);
}
- free(words);
- free(word_buf);
+ Words_Free(words);
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);
+ "Wrong number of words (%zu) in .for substitution list"
+ " with %zu vars", len, n);
/*
* Return 'success' so that the body of the .for loop is
* accumulated.
@@ -288,49 +271,35 @@ For_Accum(char *line)
if (*ptr == '.') {
- for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++)
+ for (ptr++; *ptr && isspace((unsigned char)*ptr); ptr++)
continue;
if (strncmp(ptr, "endfor", 6) == 0 &&
- (isspace((unsigned char) ptr[6]) || !ptr[6])) {
+ (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])) {
+ 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_AddStr(&accumFor->buf, 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
+static size_t
for_var_len(const char *var)
{
char ch, var_start, var_end;
int depth;
- int len;
+ size_t len;
var_start = *var;
if (var_start == 0)
@@ -360,24 +329,24 @@ for_var_len(const char *var)
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;
+ const char *item = strlist_str(items, item_no);
+
/* 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);
+ (ech == ')' ? ~FOR_SUB_ESCAPE_BRACE : ~FOR_SUB_ESCAPE_PAREN))) {
+ Buf_AddStr(cmds, item);
return;
}
/* Escape ':', '$', '\\' and 'ech' - removed by :U processing */
while ((ch = *item++) != 0) {
if (ch == '$') {
- len = for_var_len(item);
+ size_t len = for_var_len(item);
if (len != 0) {
- Buf_AddBytes(cmds, len + 1, item - 1);
+ Buf_AddBytes(cmds, item - 1, len + 1);
item += len;
continue;
}
@@ -392,13 +361,14 @@ static char *
For_Iterate(void *v_arg, size_t *ret_len)
{
For *arg = v_arg;
- int i, len;
+ int i;
char *var;
char *cp;
char *cmd_cp;
char *body_end;
char ch;
Buffer cmds;
+ size_t cmd_len;
if (arg->sub_next + strlist_num(&arg->vars) > strlist_num(&arg->items)) {
/* No more iterations */
@@ -421,9 +391,9 @@ For_Iterate(void *v_arg, size_t *ret_len)
* 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);
+ cmd_cp = Buf_GetAll(&arg->buf, &cmd_len);
+ body_end = cmd_cp + cmd_len;
+ Buf_Init(&cmds, cmd_len + 256);
for (cp = cmd_cp; (cp = strchr(cp, '$')) != NULL;) {
char ech;
ch = *++cp;
@@ -431,15 +401,15 @@ For_Iterate(void *v_arg, size_t *ret_len)
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)
+ size_t vlen = strlist_info(&arg->vars, i);
+ if (memcmp(cp, var, vlen) != 0)
continue;
- if (cp[len] != ':' && cp[len] != ech && cp[len] != '\\')
+ if (cp[vlen] != ':' && cp[vlen] != ech && cp[vlen] != '\\')
continue;
/* Found a variable match. Replace with :U<value> */
- Buf_AddBytes(&cmds, cp - cmd_cp, cmd_cp);
- Buf_AddBytes(&cmds, 2, ":U");
- cp += len;
+ Buf_AddBytesBetween(&cmds, cmd_cp, cp);
+ Buf_AddStr(&cmds, ":U");
+ cp += vlen;
cmd_cp = cp;
for_substitute(&cmds, &arg->items, arg->sub_next + i, ech);
break;
@@ -457,15 +427,15 @@ For_Iterate(void *v_arg, size_t *ret_len)
if (var[0] != ch || var[1] != 0)
continue;
/* Found a variable match. Replace with ${:U<value>} */
- Buf_AddBytes(&cmds, cp - cmd_cp, cmd_cp);
- Buf_AddBytes(&cmds, 3, "{:U");
+ Buf_AddBytesBetween(&cmds, cmd_cp, cp);
+ Buf_AddStr(&cmds, "{:U");
cmd_cp = ++cp;
for_substitute(&cmds, &arg->items, arg->sub_next + i, /*{*/ '}');
- Buf_AddBytes(&cmds, 1, "}");
+ Buf_AddByte(&cmds, '}');
break;
}
}
- Buf_AddBytes(&cmds, body_end - cmd_cp, cmd_cp);
+ Buf_AddBytesBetween(&cmds, cmd_cp, body_end);
cp = Buf_Destroy(&cmds, FALSE);
if (DEBUG(FOR))
@@ -478,6 +448,7 @@ For_Iterate(void *v_arg, size_t *ret_len)
return cp;
}
+/* Run the for loop, imitating the actions of an include file. */
void
For_Run(int lineno)
{
@@ -487,9 +458,9 @@ For_Run(int lineno)
accumFor = NULL;
if (strlist_num(&arg->items) == 0) {
- /* Nothing to expand - possibly due to an earlier syntax error. */
- For_Free(arg);
- return;
+ /* 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/contrib/bmake/hash.c b/contrib/bmake/hash.c
index f2bbebc9ece7..290bed6b3f5a 100644
--- a/contrib/bmake/hash.c
+++ b/contrib/bmake/hash.c
@@ -1,4 +1,4 @@
-/* $NetBSD: hash.c,v 1.22 2020/07/03 17:03:09 rillig Exp $ */
+/* $NetBSD: hash.c,v 1.29 2020/09/01 21:11:31 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -70,14 +70,14 @@
*/
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: hash.c,v 1.22 2020/07/03 17:03:09 rillig Exp $";
+static char rcsid[] = "$NetBSD: hash.c,v 1.29 2020/09/01 21:11:31 rillig Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)hash.c 8.1 (Berkeley) 6/6/93";
#else
-__RCSID("$NetBSD: hash.c,v 1.22 2020/07/03 17:03:09 rillig Exp $");
+__RCSID("$NetBSD: hash.c,v 1.29 2020/09/01 21:11:31 rillig Exp $");
#endif
#endif /* not lint */
#endif
@@ -89,9 +89,7 @@ __RCSID("$NetBSD: hash.c,v 1.22 2020/07/03 17:03:09 rillig Exp $");
* table. Hash tables grow automatically as the amount of
* information increases.
*/
-#include "sprite.h"
#include "make.h"
-#include "hash.h"
/*
* Forward references to local procedures that are used before they're
@@ -107,29 +105,26 @@ static void RebuildTable(Hash_Table *);
#define rebuildLimit 3
-/*
- *---------------------------------------------------------
- *
- * Hash_InitTable --
- *
- * This routine just sets up the hash table.
+/* The hash function(s) */
+
+#ifndef HASH
+/* The default: this one matches Gosling's emacs */
+#define HASH(h, key, p) do { \
+ for (h = 0, p = key; *p;) \
+ h = (h << 5) - h + *p++; \
+ } while (0)
+
+#endif
+
+/* Sets up the hash table.
*
* Input:
- * t Structure to to hold table.
+ * t Structure to to hold the table.
* numBuckets How many buckets to create for starters. This
* number is rounded up to a power of two. If
* <= 0, a reasonable default is chosen. The
* table will grow in size later as needed.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * Memory is allocated for the initial bucket area.
- *
- *---------------------------------------------------------
*/
-
void
Hash_InitTable(Hash_Table *t, int numBuckets)
{
@@ -146,118 +141,85 @@ Hash_InitTable(Hash_Table *t, int numBuckets)
continue;
}
t->numEntries = 0;
- t->size = i;
- t->mask = i - 1;
- t->bucketPtr = hp = bmake_malloc(sizeof(*hp) * i);
+ t->maxchain = 0;
+ t->bucketsSize = i;
+ t->bucketsMask = i - 1;
+ t->buckets = hp = bmake_malloc(sizeof(*hp) * i);
while (--i >= 0)
*hp++ = NULL;
}
-/*
- *---------------------------------------------------------
- *
- * Hash_DeleteTable --
- *
- * This routine removes everything from a hash table
- * and frees up the memory space it occupied (except for
- * the space in the Hash_Table structure).
- *
- * Results:
- * None.
- *
- * Side Effects:
- * Lots of memory is freed up.
- *
- *---------------------------------------------------------
- */
-
+/* Removes everything from the hash table and frees up the memory space it
+ * occupied (except for the space in the Hash_Table structure). */
void
Hash_DeleteTable(Hash_Table *t)
{
struct Hash_Entry **hp, *h, *nexth = NULL;
int i;
- for (hp = t->bucketPtr, i = t->size; --i >= 0;) {
+ for (hp = t->buckets, i = t->bucketsSize; --i >= 0;) {
for (h = *hp++; h != NULL; h = nexth) {
nexth = h->next;
free(h);
}
}
- free(t->bucketPtr);
+ free(t->buckets);
/*
* Set up the hash table to cause memory faults on any future access
* attempts until re-initialization.
*/
- t->bucketPtr = NULL;
+ t->buckets = NULL;
}
-/*
- *---------------------------------------------------------
- *
- * Hash_FindEntry --
- *
- * Searches a hash table for an entry corresponding to key.
+/* Searches the hash table for an entry corresponding to the key.
*
* Input:
* t Hash table to search.
* key A hash key.
*
* Results:
- * The return value is a pointer to the entry for key,
- * if key was present in the table. If key was not
- * present, NULL is returned.
- *
- * Side Effects:
- * None.
- *
- *---------------------------------------------------------
+ * Returns a pointer to the entry for key, or NULL if the table contains
+ * no entry for the key.
*/
-
Hash_Entry *
Hash_FindEntry(Hash_Table *t, const char *key)
{
Hash_Entry *e;
unsigned h;
const char *p;
+ int chainlen;
- if (t == NULL || t->bucketPtr == NULL) {
+ if (t == NULL || t->buckets == NULL) {
return NULL;
}
- for (h = 0, p = key; *p;)
- h = (h << 5) - h + *p++;
+ HASH(h, key, p);
p = key;
- for (e = t->bucketPtr[h & t->mask]; e != NULL; e = e->next)
+ chainlen = 0;
+#ifdef DEBUG_HASH_LOOKUP
+ if (DEBUG(HASH))
+ fprintf(debug_file, "%s: %p h=%x key=%s\n", __func__,
+ t, h, key);
+#endif
+ for (e = t->buckets[h & t->bucketsMask]; e != NULL; e = e->next) {
+ chainlen++;
if (e->namehash == h && strcmp(e->name, p) == 0)
- return e;
- return NULL;
+ break;
+ }
+ if (chainlen > t->maxchain)
+ t->maxchain = chainlen;
+ return e;
}
-/*
- *---------------------------------------------------------
- *
- * Hash_CreateEntry --
- *
- * Searches a hash table for an entry corresponding to
- * key. If no entry is found, then one is created.
+/* Searches the hash table for an entry corresponding to the 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,
+ * newPtr Filled with TRUE if new entry created,
* FALSE otherwise.
- *
- * Results:
- * The return value is a pointer to the entry. If *newPtr
- * isn't NULL, then *newPtr is filled in with TRUE if a
- * new entry was created, and FALSE if an entry already existed
- * with the given key.
- *
- * Side Effects:
- * Memory may be allocated, and the hash buckets may be modified.
- *---------------------------------------------------------
*/
-
Hash_Entry *
Hash_CreateEntry(Hash_Table *t, const char *key, Boolean *newPtr)
{
@@ -265,33 +227,44 @@ Hash_CreateEntry(Hash_Table *t, const char *key, Boolean *newPtr)
unsigned h;
const char *p;
int keylen;
+ int chainlen;
struct Hash_Entry **hp;
/*
* Hash the key. As a side effect, save the length (strlen) of the
* key in case we need to create the entry.
*/
- for (h = 0, p = key; *p;)
- h = (h << 5) - h + *p++;
+ HASH(h, key, p);
keylen = p - key;
p = key;
- for (e = t->bucketPtr[h & t->mask]; e != NULL; e = e->next) {
+ chainlen = 0;
+#ifdef DEBUG_HASH_LOOKUP
+ if (DEBUG(HASH))
+ fprintf(debug_file, "%s: %p h=%x key=%s\n", __func__,
+ t, h, key);
+#endif
+ for (e = t->buckets[h & t->bucketsMask]; e != NULL; e = e->next) {
+ chainlen++;
if (e->namehash == h && strcmp(e->name, p) == 0) {
if (newPtr != NULL)
*newPtr = FALSE;
- return e;
+ break;
}
}
+ if (chainlen > t->maxchain)
+ t->maxchain = chainlen;
+ if (e)
+ return e;
/*
* The desired entry isn't there. Before allocating a new entry,
* expand the table if necessary (and this changes the resulting
* bucket chain).
*/
- if (t->numEntries >= rebuildLimit * t->size)
+ if (t->numEntries >= rebuildLimit * t->bucketsSize)
RebuildTable(t);
e = bmake_malloc(sizeof(*e) + keylen);
- hp = &t->bucketPtr[h & t->mask];
+ hp = &t->buckets[h & t->bucketsMask];
e->next = *hp;
*hp = e;
Hash_SetValue(e, NULL);
@@ -304,23 +277,7 @@ Hash_CreateEntry(Hash_Table *t, const char *key, Boolean *newPtr)
return e;
}
-/*
- *---------------------------------------------------------
- *
- * Hash_DeleteEntry --
- *
- * Delete the given hash table entry and free memory associated with
- * it.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * Hash chain that entry lives in is modified and memory is freed.
- *
- *---------------------------------------------------------
- */
-
+/* Delete the given hash table entry and free memory associated with it. */
void
Hash_DeleteEntry(Hash_Table *t, Hash_Entry *e)
{
@@ -328,7 +285,7 @@ Hash_DeleteEntry(Hash_Table *t, Hash_Entry *e)
if (e == NULL)
return;
- for (hp = &t->bucketPtr[e->namehash & t->mask];
+ for (hp = &t->buckets[e->namehash & t->bucketsMask];
(p = *hp) != NULL; hp = &p->next) {
if (p == e) {
*hp = p->next;
@@ -341,12 +298,7 @@ Hash_DeleteEntry(Hash_Table *t, Hash_Entry *e)
abort();
}
-/*
- *---------------------------------------------------------
- *
- * Hash_EnumFirst --
- * This procedure sets things up for a complete search
- * of all entries recorded in the hash table.
+/* Sets things up for enumerating all entries in the hash table.
*
* Input:
* t Table to be searched.
@@ -355,57 +307,34 @@ Hash_DeleteEntry(Hash_Table *t, Hash_Entry *e)
* Results:
* The return value is the address of the first entry in
* the hash table, or NULL if the table is empty.
- *
- * Side Effects:
- * The information in searchPtr is initialized so that successive
- * calls to Hash_Next will return successive HashEntry's
- * from the table.
- *
- *---------------------------------------------------------
*/
-
Hash_Entry *
Hash_EnumFirst(Hash_Table *t, Hash_Search *searchPtr)
{
- searchPtr->tablePtr = t;
- searchPtr->nextIndex = 0;
- searchPtr->hashEntryPtr = NULL;
+ searchPtr->table = t;
+ searchPtr->nextBucket = 0;
+ searchPtr->entry = NULL;
return Hash_EnumNext(searchPtr);
}
-/*
- *---------------------------------------------------------
- *
- * Hash_EnumNext --
- * This procedure returns successive entries in the hash table.
+/* Returns the next entry in the hash table, or NULL if the end of the table
+ * is reached.
*
* Input:
* searchPtr Area used to keep state about search.
- *
- * Results:
- * The return value is a pointer to the next HashEntry
- * in the table, or NULL when the end of the table is
- * reached.
- *
- * Side Effects:
- * The information in searchPtr is modified to advance to the
- * next entry.
- *
- *---------------------------------------------------------
*/
-
Hash_Entry *
Hash_EnumNext(Hash_Search *searchPtr)
{
Hash_Entry *e;
- Hash_Table *t = searchPtr->tablePtr;
+ Hash_Table *t = searchPtr->table;
/*
- * The hashEntryPtr field points to the most recently returned
- * entry, or is nil if we are starting up. If not nil, we have
+ * The entry field points to the most recently returned
+ * entry, or is NULL if we are starting up. If not NULL, we have
* to start at the next one in the chain.
*/
- e = searchPtr->hashEntryPtr;
+ e = searchPtr->entry;
if (e != NULL)
e = e->next;
/*
@@ -413,59 +342,49 @@ Hash_EnumNext(Hash_Search *searchPtr)
* find the next nonempty chain.
*/
while (e == NULL) {
- if (searchPtr->nextIndex >= t->size)
+ if (searchPtr->nextBucket >= t->bucketsSize)
return NULL;
- e = t->bucketPtr[searchPtr->nextIndex++];
+ e = t->buckets[searchPtr->nextBucket++];
}
- searchPtr->hashEntryPtr = e;
+ searchPtr->entry = e;
return e;
}
-/*
- *---------------------------------------------------------
- *
- * RebuildTable --
- * This local routine makes a new hash table that
- * is larger than the old one.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * The entire hash table is moved, so any bucket numbers
- * from the old table are invalid.
- *
- *---------------------------------------------------------
- */
-
+/* Makes a new hash table that is larger than the old one. The entire hash
+ * table is moved, so any bucket numbers from the old table become invalid. */
static void
RebuildTable(Hash_Table *t)
{
Hash_Entry *e, *next = NULL, **hp, **xp;
int i, mask;
- Hash_Entry **oldhp;
+ Hash_Entry **oldhp;
int oldsize;
- oldhp = t->bucketPtr;
- oldsize = i = t->size;
+ oldhp = t->buckets;
+ oldsize = i = t->bucketsSize;
i <<= 1;
- t->size = i;
- t->mask = mask = i - 1;
- t->bucketPtr = hp = bmake_malloc(sizeof(*hp) * i);
+ t->bucketsSize = i;
+ t->bucketsMask = mask = i - 1;
+ t->buckets = hp = bmake_malloc(sizeof(*hp) * i);
while (--i >= 0)
*hp++ = NULL;
for (hp = oldhp, i = oldsize; --i >= 0;) {
for (e = *hp++; e != NULL; e = next) {
next = e->next;
- xp = &t->bucketPtr[e->namehash & mask];
+ xp = &t->buckets[e->namehash & mask];
e->next = *xp;
*xp = e;
}
}
free(oldhp);
+ if (DEBUG(HASH))
+ fprintf(debug_file, "%s: %p size=%d entries=%d maxchain=%d\n",
+ __func__, t, t->bucketsSize, t->numEntries, t->maxchain);
+ t->maxchain = 0;
}
-void Hash_ForEach(Hash_Table *t, void (*action)(void *, void *), void *data)
+void
+Hash_ForEach(Hash_Table *t, void (*action)(void *, void *), void *data)
{
Hash_Search search;
Hash_Entry *e;
@@ -475,3 +394,11 @@ void Hash_ForEach(Hash_Table *t, void (*action)(void *, void *), void *data)
e = Hash_EnumNext(&search))
action(Hash_GetValue(e), data);
}
+
+void
+Hash_DebugStats(Hash_Table *t, const char *name)
+{
+ if (DEBUG(HASH))
+ fprintf(debug_file, "Hash_Table %s: size=%d numEntries=%d maxchain=%d\n",
+ name, t->bucketsSize, t->numEntries, t->maxchain);
+}
diff --git a/contrib/bmake/hash.h b/contrib/bmake/hash.h
index 2e4ea6a43560..d98a8144ed85 100644
--- a/contrib/bmake/hash.h
+++ b/contrib/bmake/hash.h
@@ -1,4 +1,4 @@
-/* $NetBSD: hash.h,v 1.13 2020/07/03 17:03:09 rillig Exp $ */
+/* $NetBSD: hash.h,v 1.21 2020/09/01 21:11:31 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -72,71 +72,51 @@
* from: @(#)hash.h 8.1 (Berkeley) 6/6/93
*/
-/* hash.h --
- *
- * This file contains definitions used by the hash module,
- * which maintains hash tables.
- */
-
-#ifndef _HASH_H
-#define _HASH_H
+/* Hash tables with strings as keys and arbitrary pointers as values. */
-/*
- * The following defines one entry in the hash table.
- */
+#ifndef MAKE_HASH_H
+#define MAKE_HASH_H
+/* A single key-value entry in the hash table. */
typedef struct Hash_Entry {
- struct Hash_Entry *next; /* Used to link together all the
- * entries associated with the same
- * bucket. */
- void *clientPtr; /* Arbitrary pointer */
- unsigned namehash; /* hash value of key */
- char name[1]; /* key string */
+ struct Hash_Entry *next; /* Used to link together all the entries
+ * associated with the same bucket. */
+ void *value;
+ unsigned namehash; /* hash value of key */
+ char name[1]; /* key string, variable length */
} Hash_Entry;
+/* The hash table containing the entries. */
typedef struct Hash_Table {
- struct Hash_Entry **bucketPtr;/* Pointers to Hash_Entry, one
- * for each bucket in the table. */
- int size; /* Actual size of array. */
+ Hash_Entry **buckets; /* Pointers to Hash_Entry, one
+ * for each bucket in the table. */
+ int bucketsSize;
int numEntries; /* Number of entries in the table. */
- int mask; /* Used to select bits for hashing. */
+ int bucketsMask; /* Used to select the bucket for a hash. */
+ int maxchain; /* max length of chain detected */
} Hash_Table;
/*
* The following structure is used by the searching routines
* to record where we are in the search.
*/
-
typedef struct Hash_Search {
- Hash_Table *tablePtr; /* Table being searched. */
- int nextIndex; /* Next bucket to check (after current). */
- Hash_Entry *hashEntryPtr; /* Next entry to check in current bucket. */
+ Hash_Table *table; /* Table being searched. */
+ int nextBucket; /* Next bucket to check (after current). */
+ Hash_Entry *entry; /* Next entry to check in current bucket. */
} Hash_Search;
-/*
- * Macros.
- */
-
-/*
- * void * Hash_GetValue(h)
- * Hash_Entry *h;
- */
-
-#define Hash_GetValue(h) ((h)->clientPtr)
-
-/*
- * Hash_SetValue(h, val);
- * Hash_Entry *h;
- * char *val;
- */
-
-#define Hash_SetValue(h, val) ((h)->clientPtr = (val))
-
-/*
- * Hash_Size(n) returns the number of words in an object of n bytes
- */
+static inline void * MAKE_ATTR_UNUSED
+Hash_GetValue(Hash_Entry *h)
+{
+ return h->value;
+}
-#define Hash_Size(n) (((n) + sizeof (int) - 1) / sizeof (int))
+static inline void MAKE_ATTR_UNUSED
+Hash_SetValue(Hash_Entry *h, void *datum)
+{
+ h->value = datum;
+}
void Hash_InitTable(Hash_Table *, int);
void Hash_DeleteTable(Hash_Table *);
@@ -146,5 +126,6 @@ void Hash_DeleteEntry(Hash_Table *, Hash_Entry *);
Hash_Entry *Hash_EnumFirst(Hash_Table *, Hash_Search *);
Hash_Entry *Hash_EnumNext(Hash_Search *);
void Hash_ForEach(Hash_Table *, void (*)(void *, void *), void *);
+void Hash_DebugStats(Hash_Table *, const char *);
-#endif /* _HASH_H */
+#endif /* MAKE_HASH_H */
diff --git a/contrib/bmake/job.c b/contrib/bmake/job.c
index a80347acc5cc..3f0b0498724f 100644
--- a/contrib/bmake/job.c
+++ b/contrib/bmake/job.c
@@ -1,4 +1,4 @@
-/* $NetBSD: job.c,v 1.201 2020/07/03 08:13:23 rillig Exp $ */
+/* $NetBSD: job.c,v 1.227 2020/08/30 19:56:02 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -70,14 +70,14 @@
*/
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: job.c,v 1.201 2020/07/03 08:13:23 rillig Exp $";
+static char rcsid[] = "$NetBSD: job.c,v 1.227 2020/08/30 19:56:02 rillig Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)job.c 8.2 (Berkeley) 3/19/94";
#else
-__RCSID("$NetBSD: job.c,v 1.201 2020/07/03 08:13:23 rillig Exp $");
+__RCSID("$NetBSD: job.c,v 1.227 2020/08/30 19:56:02 rillig Exp $");
#endif
#endif /* not lint */
#endif
@@ -112,7 +112,7 @@ __RCSID("$NetBSD: job.c,v 1.201 2020/07/03 08:13:23 rillig Exp $");
*
* Job_ParseShell Given the line following a .SHELL target, parse
* the line as a shell specification. Returns
- * FAILURE if the spec was incorrect.
+ * FALSE if the spec was incorrect.
*
* Job_Finish Perform any final processing which needs doing.
* This includes the execution of any commands
@@ -142,7 +142,6 @@ __RCSID("$NetBSD: job.c,v 1.201 2020/07/03 08:13:23 rillig Exp $");
#include <sys/time.h>
#include "wait.h"
-#include <assert.h>
#include <errno.h>
#if !defined(USE_SELECT) && defined(HAVE_POLL_H)
#include <poll.h>
@@ -155,15 +154,12 @@ __RCSID("$NetBSD: job.c,v 1.201 2020/07/03 08:13:23 rillig Exp $");
#endif
#endif
#include <signal.h>
-#include <stdio.h>
-#include <string.h>
#include <utime.h>
#if defined(HAVE_SYS_SOCKET_H)
# include <sys/socket.h>
#endif
#include "make.h"
-#include "hash.h"
#include "dir.h"
#include "job.h"
#include "pathnames.h"
@@ -200,7 +196,6 @@ static int aborting = 0; /* why is the make aborting? */
* 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
@@ -327,9 +322,9 @@ static Shell *commandShell = &shells[DEFSHELL_INDEX]; /* this is the shell to
* Job_ParseShell function */
const char *shellPath = NULL, /* full pathname of
* executable image */
- *shellName = NULL; /* last component of shell */
+ *shellName = NULL; /* last component of shell */
char *shellErrFlag = NULL;
-static const char *shellArgv = NULL; /* Custom shell args */
+static char *shellArgv = NULL; /* Custom shell args */
STATIC Job *job_table; /* The structures that describe them */
@@ -386,11 +381,6 @@ static void JobSigLock(sigset_t *);
static void JobSigUnlock(sigset_t *);
static void JobSigReset(void);
-#if !defined(MALLOC_OPTIONS)
-# define MALLOC_OPTIONS "A"
-#endif
-const char *malloc_options= MALLOC_OPTIONS;
-
static unsigned
nfds_per_job(void)
{
@@ -739,15 +729,14 @@ JobPrintCommand(void *cmdp, void *jobp)
char *escCmd = NULL; /* Command with quotes/backticks escaped */
char *cmd = (char *)cmdp;
Job *job = (Job *)jobp;
- 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));
+ LstNode dotsNode = Lst_FindDatum(job->node->commands, cmd);
+ job->tailCmds = dotsNode != NULL ? LstNode_Next(dotsNode) : NULL;
return 1;
}
return 0;
@@ -761,7 +750,7 @@ JobPrintCommand(void *cmdp, void *jobp)
numCommands += 1;
- cmdStart = cmd = Var_Subst(NULL, cmd, job->node, VARF_WANTRES);
+ cmdStart = cmd = Var_Subst(cmd, job->node, VARE_WANTRES);
cmdTemplate = "%s\n";
@@ -801,15 +790,17 @@ JobPrintCommand(void *cmdp, void *jobp)
*/
if (!commandShell->hasErrCtl) {
+ int i, j;
+
/* 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];
+ 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;
+ escCmd[j] = '\0';
}
if (shutUp) {
@@ -942,15 +933,15 @@ JobPrintCommand(void *cmdp, void *jobp)
* Always returns 0
*
* Side Effects:
- * The command is tacked onto the end of postCommands's commands list.
+ * The command is tacked onto the end of postCommands' commands list.
*
*-----------------------------------------------------------------------
*/
static int
JobSaveCommand(void *cmd, void *gn)
{
- cmd = Var_Subst(NULL, (char *)cmd, (GNode *)gn, VARF_WANTRES);
- (void)Lst_AtEnd(postCommands->commands, cmd);
+ cmd = Var_Subst((char *)cmd, (GNode *)gn, VARE_WANTRES);
+ Lst_Append(postCommands->commands, cmd);
return 0;
}
@@ -1140,7 +1131,7 @@ JobFinish (Job *job, WAIT_T status)
if (job->tailCmds != NULL) {
Lst_ForEachFrom(job->node->commands, job->tailCmds,
JobSaveCommand,
- job->node);
+ job->node);
}
job->node->made = MADE;
if (!(job->flags & JOB_SPECIAL))
@@ -1291,7 +1282,7 @@ Job_CheckCommands(GNode *gn, void (*abortProc)(const char *, ...))
*/
Make_HandleUse(DEFAULT, gn);
Var_Set(IMPSRC, Var_Value(TARGET, gn, &p1), gn);
- free(p1);
+ bmake_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
@@ -1318,7 +1309,7 @@ Job_CheckCommands(GNode *gn, void (*abortProc)(const char *, ...))
(void)fprintf(stdout, "%s%s %s (continuing)\n", progname,
msg, gn->name);
(void)fflush(stdout);
- return FALSE;
+ return FALSE;
} else {
(*abortProc)("%s%s %s. Stop", progname, msg, gn->name);
return FALSE;
@@ -1361,7 +1352,7 @@ JobExec(Job *job, char **argv)
for (i = 0; argv[i] != NULL; i++) {
(void)fprintf(debug_file, "%s ", argv[i]);
}
- (void)fprintf(debug_file, "\n");
+ (void)fprintf(debug_file, "\n");
}
/*
@@ -1736,7 +1727,7 @@ JobStart(GNode *gn, int flags)
* up the graph.
*/
job->cmdFILE = stdout;
- Job_Touch(gn, job->flags&JOB_SILENT);
+ Job_Touch(gn, job->flags&JOB_SILENT);
noExec = TRUE;
}
/* Just in case it isn't already... */
@@ -1765,8 +1756,8 @@ JobStart(GNode *gn, int flags)
if (cmdsOK && aborting == 0) {
if (job->tailCmds != NULL) {
Lst_ForEachFrom(job->node->commands, job->tailCmds,
- JobSaveCommand,
- job->node);
+ JobSaveCommand,
+ job->node);
}
job->node->made = MADE;
Make_Update(job->node);
@@ -2007,8 +1998,8 @@ JobRun(GNode *targ)
* 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);
+ Lst lst = Lst_Init();
+ Lst_Append(lst, targ);
(void)Make_Run(lst);
Lst_Destroy(lst, NULL);
JobStart(targ, JOB_SPECIAL);
@@ -2235,8 +2226,9 @@ Shell_Init(void)
shellName++;
} else
#endif
- shellPath = str_concat(_PATH_DEFSHELLDIR, shellName, STR_ADDSLASH);
+ shellPath = str_concat3(_PATH_DEFSHELLDIR, "/", shellName);
}
+ Var_Set_with_flags(".SHELL", shellPath, VAR_CMD, VAR_SET_READONLY);
if (commandShell->exit == NULL) {
commandShell->exit = "";
}
@@ -2270,38 +2262,23 @@ Shell_Init(void)
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);
}
- targPrefix = Var_Subst(NULL, "${" MAKE_JOB_PREFIX "}",
- VAR_GLOBAL, VARF_WANTRES);
+ targPrefix = Var_Subst("${" MAKE_JOB_PREFIX "}",
+ VAR_GLOBAL, VARE_WANTRES);
}
-/*-
- *-----------------------------------------------------------------------
- * Job_Init --
- * Initialize the process module
- *
- * Input:
- *
- * Results:
- * none
- *
- * Side Effects:
- * lists and counters are initialized
- *-----------------------------------------------------------------------
- */
+/* Initialize the process module. */
void
Job_Init(void)
{
@@ -2413,19 +2390,7 @@ static void JobSigReset(void)
(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.
- *
- *-----------------------------------------------------------------------
- */
+/* Find a shell in 'shells' given its name, or return NULL. */
static Shell *
JobMatchShell(const char *name)
{
@@ -2448,7 +2413,7 @@ JobMatchShell(const char *name)
* line The shell spec
*
* Results:
- * FAILURE if the specification was incorrect.
+ * FALSE if the specification was incorrect.
*
* Side Effects:
* commandShell points to a Shell structure (either predefined or
@@ -2485,12 +2450,13 @@ JobMatchShell(const char *name)
*
*-----------------------------------------------------------------------
*/
-ReturnStatus
+Boolean
Job_ParseShell(char *line)
{
+ Words wordsList;
char **words;
char **argv;
- int argc;
+ size_t argc;
char *path;
Shell newShell;
Boolean fullSpec = FALSE;
@@ -2500,17 +2466,20 @@ Job_ParseShell(char *line)
line++;
}
- free(UNCONST(shellArgv));
+ free(shellArgv);
memset(&newShell, 0, sizeof(newShell));
/*
* Parse the specification by keyword
*/
- words = brk_string(line, &argc, TRUE, &path);
+ wordsList = Str_Words(line, TRUE);
+ words = wordsList.words;
+ argc = wordsList.len;
+ path = wordsList.freeIt;
if (words == NULL) {
Error("Unterminated quoted string [%s]", line);
- return FAILURE;
+ return FALSE;
}
shellArgv = path;
@@ -2549,7 +2518,7 @@ Job_ParseShell(char *line)
Parse_Error(PARSE_FATAL, "Unknown keyword \"%s\"",
*argv);
free(words);
- return FAILURE;
+ return FALSE;
}
fullSpec = TRUE;
}
@@ -2565,13 +2534,13 @@ Job_ParseShell(char *line)
if (newShell.name == NULL) {
Parse_Error(PARSE_FATAL, "Neither path nor name specified");
free(words);
- return FAILURE;
+ return FALSE;
} else {
if ((sh = JobMatchShell(newShell.name)) == NULL) {
Parse_Error(PARSE_WARNING, "%s: No matching shell",
newShell.name);
free(words);
- return FAILURE;
+ return FALSE;
}
commandShell = sh;
shellName = newShell.name;
@@ -2607,7 +2576,7 @@ Job_ParseShell(char *line)
Parse_Error(PARSE_WARNING, "%s: No matching shell",
shellName);
free(words);
- return FAILURE;
+ return FALSE;
}
commandShell = sh;
} else {
@@ -2636,7 +2605,7 @@ Job_ParseShell(char *line)
* shell specification.
*/
free(words);
- return SUCCESS;
+ return TRUE;
}
/*-
@@ -2813,7 +2782,6 @@ Job_AbortAll(void)
continue;
}
-
/*-
*-----------------------------------------------------------------------
* JobRestartJobs --
@@ -3154,7 +3122,7 @@ emul_poll(struct pollfd *fd, int nfd, int timeout)
usecs = timeout * 1000;
tv.tv_sec = usecs / 1000000;
tv.tv_usec = usecs % 1000000;
- tvp = &tv;
+ tvp = &tv;
}
nselect = select(maxfd + 1, &rfds, &wfds, 0, tvp);
diff --git a/contrib/bmake/job.h b/contrib/bmake/job.h
index 603c09e861f1..45fc1b7f8df4 100644
--- a/contrib/bmake/job.h
+++ b/contrib/bmake/job.h
@@ -1,4 +1,4 @@
-/* $NetBSD: job.h,v 1.43 2020/07/03 08:13:23 rillig Exp $ */
+/* $NetBSD: job.h,v 1.47 2020/08/29 12:20:17 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -77,8 +77,8 @@
* job.h --
* Definitions pertaining to the running of jobs in parallel mode.
*/
-#ifndef _JOB_H_
-#define _JOB_H_
+#ifndef MAKE_JOB_H
+#define MAKE_JOB_H
#define TMPPAT "makeXXXXXX" /* relative to tmpdir */
@@ -110,7 +110,6 @@ emul_poll(struct pollfd *fd, int nfd, int timeout);
*/
#define POLL_MSEC 5000
-
/*-
* Job Table definitions.
*
@@ -164,7 +163,7 @@ typedef struct Job {
* commands */
#define JOB_TRACED 0x400 /* we've sent 'set -x' */
- int jobPipe[2]; /* Pipe for readind output from job */
+ int jobPipe[2]; /* Pipe for reading output from job */
struct pollfd *inPollfd; /* pollfd associated with inPipe */
char outBuf[JOB_BUFSIZE + 1];
/* Buffer for storing the output of the
@@ -179,7 +178,6 @@ typedef struct Job {
#define inPipe jobPipe[0]
#define outPipe jobPipe[1]
-
/*-
* Shell Specifications:
* Each shell type has associated with it the following information:
@@ -252,23 +250,20 @@ 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 *);
+Boolean 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);
Boolean Job_RunTarget(const char *, const char *);
-#endif /* _JOB_H_ */
+#endif /* MAKE_JOB_H */
diff --git a/contrib/bmake/lst.c b/contrib/bmake/lst.c
new file mode 100644
index 000000000000..26b2457cc013
--- /dev/null
+++ b/contrib/bmake/lst.c
@@ -0,0 +1,641 @@
+/* $NetBSD: lst.c,v 1.60 2020/08/31 05:56:02 rillig 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#elif defined(HAVE_STDINT_H)
+#include <stdint.h>
+#endif
+
+#include "make.h"
+
+#ifndef MAKE_NATIVE
+static char rcsid[] = "$NetBSD: lst.c,v 1.60 2020/08/31 05:56:02 rillig Exp $";
+#else
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: lst.c,v 1.60 2020/08/31 05:56:02 rillig Exp $");
+#endif /* not lint */
+#endif
+
+struct ListNode {
+ struct ListNode *prev; /* previous element in list */
+ struct ListNode *next; /* next in list */
+ uint8_t useCount; /* Count of functions using the node.
+ * node may not be deleted until count
+ * goes to 0 */
+ Boolean deleted; /* List node should be removed when done */
+ union {
+ void *datum; /* datum associated with this element */
+ const GNode *gnode; /* alias, just for debugging */
+ const char *str; /* alias, just for debugging */
+ };
+};
+
+typedef enum {
+ Head, Middle, Tail, Unknown
+} Where;
+
+struct List {
+ LstNode first; /* first node in list */
+ LstNode last; /* last node in list */
+
+ /* fields for sequential access */
+ Boolean isOpen; /* true if list has been Lst_Open'ed */
+ Where lastAccess; /* Where in the list the last access was */
+ LstNode curr; /* current node, if open. NULL if
+ * *just* opened */
+ LstNode prev; /* Previous node, if open. Used by Lst_Remove */
+};
+
+/* Allocate and initialize a list node.
+ *
+ * The fields 'prev' and 'next' must be initialized by the caller.
+ */
+static LstNode
+LstNodeNew(void *datum)
+{
+ LstNode node = bmake_malloc(sizeof *node);
+ node->useCount = 0;
+ node->deleted = FALSE;
+ node->datum = datum;
+ return node;
+}
+
+static Boolean
+LstIsEmpty(Lst list)
+{
+ return list->first == NULL;
+}
+
+/* Create and initialize a new, empty list. */
+Lst
+Lst_Init(void)
+{
+ Lst list = bmake_malloc(sizeof *list);
+
+ list->first = NULL;
+ list->last = NULL;
+ list->isOpen = FALSE;
+ list->lastAccess = Unknown;
+
+ return list;
+}
+
+/* Duplicate an entire list, usually by copying the datum pointers.
+ * If copyProc is given, that function is used to create the new datum from the
+ * old datum, usually by creating a copy of it. */
+Lst
+Lst_Copy(Lst list, LstCopyProc copyProc)
+{
+ Lst newList;
+ LstNode node;
+
+ assert(list != NULL);
+
+ newList = Lst_Init();
+
+ for (node = list->first; node != NULL; node = node->next) {
+ void *datum = copyProc != NULL ? copyProc(node->datum) : node->datum;
+ Lst_Append(newList, datum);
+ }
+
+ return newList;
+}
+
+/* Free a list and all its nodes. The list data itself are not freed though. */
+void
+Lst_Free(Lst list)
+{
+ LstNode node;
+ LstNode next;
+
+ assert(list != NULL);
+
+ for (node = list->first; node != NULL; node = next) {
+ next = node->next;
+ free(node);
+ }
+
+ free(list);
+}
+
+/* Destroy a list and free all its resources. The freeProc is called with the
+ * datum from each node in turn before the node is freed. */
+void
+Lst_Destroy(Lst list, LstFreeProc freeProc)
+{
+ LstNode node;
+ LstNode next;
+
+ assert(list != NULL);
+ assert(freeProc != NULL);
+
+ for (node = list->first; node != NULL; node = next) {
+ next = node->next;
+ freeProc(node->datum);
+ free(node);
+ }
+
+ free(list);
+}
+
+/*
+ * Functions to modify a list
+ */
+
+/* Insert a new node with the given piece of data before the given node in the
+ * given list. */
+void
+Lst_InsertBefore(Lst list, LstNode node, void *datum)
+{
+ LstNode newNode;
+
+ assert(list != NULL);
+ assert(!LstIsEmpty(list));
+ assert(node != NULL);
+ assert(datum != NULL);
+
+ newNode = LstNodeNew(datum);
+ newNode->prev = node->prev;
+ newNode->next = node;
+
+ if (node->prev != NULL) {
+ node->prev->next = newNode;
+ }
+ node->prev = newNode;
+
+ if (node == list->first) {
+ list->first = newNode;
+ }
+}
+
+/* Add a piece of data at the start of the given list. */
+void
+Lst_Prepend(Lst list, void *datum)
+{
+ LstNode node;
+
+ assert(list != NULL);
+ assert(datum != NULL);
+
+ node = LstNodeNew(datum);
+ node->prev = NULL;
+ node->next = list->first;
+
+ if (list->first == NULL) {
+ list->first = node;
+ list->last = node;
+ } else {
+ list->first->prev = node;
+ list->first = node;
+ }
+}
+
+/* Add a piece of data at the end of the given list. */
+void
+Lst_Append(Lst list, void *datum)
+{
+ LstNode node;
+
+ assert(list != NULL);
+ assert(datum != NULL);
+
+ node = LstNodeNew(datum);
+ node->prev = list->last;
+ node->next = NULL;
+
+ if (list->last == NULL) {
+ list->first = node;
+ list->last = node;
+ } else {
+ list->last->next = node;
+ list->last = node;
+ }
+}
+
+/* Remove the given node from the given list.
+ * The datum stored in the node must be freed by the caller, if necessary. */
+void
+Lst_Remove(Lst list, LstNode node)
+{
+ assert(list != NULL);
+ assert(node != NULL);
+
+ /*
+ * unlink it from the list
+ */
+ if (node->next != NULL) {
+ node->next->prev = node->prev;
+ }
+ if (node->prev != NULL) {
+ node->prev->next = node->next;
+ }
+
+ /*
+ * if either the first or last of the list point to this node,
+ * adjust them accordingly
+ */
+ if (list->first == node) {
+ list->first = node->next;
+ }
+ if (list->last == node) {
+ list->last = node->prev;
+ }
+
+ /*
+ * 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 (prev == NULL), we set the
+ * end to be Unknown, since it is.
+ */
+ if (list->isOpen && list->curr == node) {
+ list->curr = list->prev;
+ if (list->curr == NULL) {
+ list->lastAccess = Unknown;
+ }
+ }
+
+ /*
+ * note that the datum is unmolested. The caller must free it as
+ * necessary and as expected.
+ */
+ if (node->useCount == 0) {
+ free(node);
+ } else {
+ node->deleted = TRUE;
+ }
+}
+
+/* Replace the datum in the given node with the new datum. */
+void
+LstNode_Set(LstNode node, void *datum)
+{
+ assert(node != NULL);
+ assert(datum != NULL);
+
+ node->datum = datum;
+}
+
+/* Replace the datum in the given node to NULL. */
+void
+LstNode_SetNull(LstNode node)
+{
+ assert(node != NULL);
+
+ node->datum = NULL;
+}
+
+
+/*
+ * Node-specific functions
+ */
+
+/* Return the first node from the given list, or NULL if the list is empty. */
+LstNode
+Lst_First(Lst list)
+{
+ assert(list != NULL);
+
+ return list->first;
+}
+
+/* Return the last node from the given list, or NULL if the list is empty. */
+LstNode
+Lst_Last(Lst list)
+{
+ assert(list != NULL);
+
+ return list->last;
+}
+
+/* Return the successor to the given node on its list, or NULL. */
+LstNode
+LstNode_Next(LstNode node)
+{
+ assert(node != NULL);
+
+ return node->next;
+}
+
+/* Return the predecessor to the given node on its list, or NULL. */
+LstNode
+LstNode_Prev(LstNode node)
+{
+ assert(node != NULL);
+ return node->prev;
+}
+
+/* Return the datum stored in the given node. */
+void *
+LstNode_Datum(LstNode node)
+{
+ assert(node != NULL);
+ return node->datum;
+}
+
+
+/*
+ * Functions for entire lists
+ */
+
+/* Return TRUE if the given list is empty. */
+Boolean
+Lst_IsEmpty(Lst list)
+{
+ assert(list != NULL);
+
+ return LstIsEmpty(list);
+}
+
+/* Return the first node from the list for which the match function returns
+ * TRUE, or NULL if none of the nodes matched. */
+LstNode
+Lst_Find(Lst list, LstFindProc match, const void *matchArgs)
+{
+ return Lst_FindFrom(list, Lst_First(list), match, matchArgs);
+}
+
+/* Return the first node from the list, starting at the given node, for which
+ * the match function returns TRUE, or NULL if none of the nodes matches.
+ *
+ * The start node may be NULL, in which case nothing is found. This allows
+ * for passing Lst_First or LstNode_Next as the start node. */
+LstNode
+Lst_FindFrom(Lst list, LstNode node, LstFindProc match, const void *matchArgs)
+{
+ LstNode tln;
+
+ assert(list != NULL);
+ assert(match != NULL);
+
+ for (tln = node; tln != NULL; tln = tln->next) {
+ if (match(tln->datum, matchArgs))
+ return tln;
+ }
+
+ return NULL;
+}
+
+/* Return the first node that contains the given datum, or NULL. */
+LstNode
+Lst_FindDatum(Lst list, const void *datum)
+{
+ LstNode node;
+
+ assert(list != NULL);
+ assert(datum != NULL);
+
+ for (node = list->first; node != NULL; node = node->next) {
+ if (node->datum == datum) {
+ return node;
+ }
+ }
+
+ return NULL;
+}
+
+/* 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. */
+int
+Lst_ForEach(Lst list, LstActionProc proc, void *procData)
+{
+ if (LstIsEmpty(list))
+ return 0; /* XXX: Document what this value means. */
+ return Lst_ForEachFrom(list, Lst_First(list), proc, procData);
+}
+
+/* Apply the given function to each element of the given list, starting from
+ * the given node. The function should return 0 if traversal should continue,
+ * and non-zero if it should abort. */
+int
+Lst_ForEachFrom(Lst list, LstNode node,
+ LstActionProc proc, void *procData)
+{
+ LstNode tln = node;
+ LstNode next;
+ Boolean done;
+ int result;
+
+ assert(list != NULL);
+ assert(node != NULL);
+ assert(proc != NULL);
+
+ do {
+ /*
+ * Take care of having the current element deleted out from under
+ * us.
+ */
+
+ next = tln->next;
+
+ /*
+ * We're done with the traversal if
+ * - the next node to examine doesn't exist and
+ * - nothing's been added after the current node (check this
+ * after proc() has been called).
+ */
+ done = next == NULL;
+
+ tln->useCount++;
+ result = (*proc)(tln->datum, procData);
+ tln->useCount--;
+
+ /*
+ * Now check whether a node has been added.
+ * Note: this doesn't work if this node was deleted before
+ * the new node was added.
+ */
+ if (next != tln->next) {
+ next = tln->next;
+ done = 0;
+ }
+
+ if (tln->deleted) {
+ free((char *)tln);
+ }
+ tln = next;
+ } while (!result && !LstIsEmpty(list) && !done);
+
+ return result;
+}
+
+/* Move all nodes from list2 to the end of list1.
+ * List2 is destroyed and freed. */
+void
+Lst_MoveAll(Lst list1, Lst list2)
+{
+ assert(list1 != NULL);
+ assert(list2 != NULL);
+
+ if (list2->first != NULL) {
+ list2->first->prev = list1->last;
+ if (list1->last != NULL) {
+ list1->last->next = list2->first;
+ } else {
+ list1->first = list2->first;
+ }
+ list1->last = list2->last;
+ }
+ free(list2);
+}
+
+/* Copy the element data from src to the start of dst. */
+void
+Lst_PrependAll(Lst dst, Lst src)
+{
+ LstNode node;
+ for (node = src->last; node != NULL; node = node->prev)
+ Lst_Prepend(dst, node->datum);
+}
+
+/* Copy the element data from src to the end of dst. */
+void
+Lst_AppendAll(Lst dst, Lst src)
+{
+ LstNode node;
+ for (node = src->first; node != NULL; node = node->next)
+ Lst_Append(dst, node->datum);
+}
+
+/*
+ * 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().
+ *
+ * The sequential functions access the list in a slightly different way.
+ * CurPtr points to their idea of the current node in the list and they
+ * access the list based on it.
+ */
+
+/* Open a list for sequential access. A list can still be searched, etc.,
+ * without confusing these functions. */
+void
+Lst_Open(Lst list)
+{
+ assert(list != NULL);
+ assert(!list->isOpen);
+
+ list->isOpen = TRUE;
+ list->lastAccess = LstIsEmpty(list) ? Head : Unknown;
+ list->curr = NULL;
+}
+
+/* Return the next node for the given list, or NULL if the end has been
+ * reached. */
+LstNode
+Lst_Next(Lst list)
+{
+ LstNode node;
+
+ assert(list != NULL);
+ assert(list->isOpen);
+
+ list->prev = list->curr;
+
+ if (list->curr == NULL) {
+ if (list->lastAccess == Unknown) {
+ /*
+ * If we're just starting out, lastAccess will be Unknown.
+ * Then we want to start this thing off in the right
+ * direction -- at the start with lastAccess being Middle.
+ */
+ list->curr = node = list->first;
+ list->lastAccess = Middle;
+ } else {
+ node = NULL;
+ list->lastAccess = Tail;
+ }
+ } else {
+ node = list->curr->next;
+ list->curr = node;
+
+ if (node == list->first || node == NULL) {
+ /*
+ * If back at the front, then we've hit the end...
+ */
+ list->lastAccess = Tail;
+ } else {
+ /*
+ * Reset to Middle if gone past first.
+ */
+ list->lastAccess = Middle;
+ }
+ }
+
+ return node;
+}
+
+/* Close a list which was opened for sequential access. */
+void
+Lst_Close(Lst list)
+{
+ assert(list != NULL);
+ assert(list->isOpen);
+
+ list->isOpen = FALSE;
+ list->lastAccess = Unknown;
+}
+
+
+/*
+ * for using the list as a queue
+ */
+
+/* Add the datum to the tail of the given list. */
+void
+Lst_Enqueue(Lst list, void *datum)
+{
+ Lst_Append(list, datum);
+}
+
+/* Remove and return the datum at the head of the given list. */
+void *
+Lst_Dequeue(Lst list)
+{
+ void *datum;
+
+ assert(list != NULL);
+ assert(!LstIsEmpty(list));
+
+ datum = list->first->datum;
+ Lst_Remove(list, list->first);
+ assert(datum != NULL);
+ return datum;
+}
diff --git a/contrib/bmake/lst.h b/contrib/bmake/lst.h
index e207bc808bf9..d9aa9feb3b65 100644
--- a/contrib/bmake/lst.h
+++ b/contrib/bmake/lst.h
@@ -1,4 +1,4 @@
-/* $NetBSD: lst.h,v 1.20 2014/09/07 20:55:34 joerg Exp $ */
+/* $NetBSD: lst.h,v 1.60 2020/09/02 23:33:13 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -73,117 +73,107 @@
* from: @(#)lst.h 8.1 (Berkeley) 6/6/93
*/
-/*-
- * lst.h --
- * Header for using the list library
- */
-#ifndef _LST_H_
-#define _LST_H_
-
-#include <sys/param.h>
-#include <stdlib.h>
+/* Doubly-linked lists of arbitrary pointers. */
-#include "sprite.h"
+#ifndef MAKE_LST_H
+#define MAKE_LST_H
-/*
- * basic typedef. This is what the Lst_ functions handle
- */
+#include <sys/param.h>
+#include <stdlib.h>
+/* A doubly-linked list of pointers. */
typedef struct List *Lst;
+/* A single node in the doubly-linked list. */
typedef struct ListNode *LstNode;
-typedef void *DuplicateProc(void *);
-typedef void FreeProc(void *);
+/* Copy a node, usually by allocating a copy of the given object.
+ * For reference-counted objects, the original object may need to be
+ * modified, therefore the parameter is not const. */
+typedef void *LstCopyProc(void *);
+/* Free the datum of a node, called before freeing the node itself. */
+typedef void LstFreeProc(void *);
+/* Return TRUE if the datum matches the args, for Lst_Find. */
+typedef Boolean LstFindProc(const void *datum, const void *args);
+/* An action for Lst_ForEach. */
+typedef int LstActionProc(void *datum, void *args);
-#define LST_CONCNEW 0 /* create new LstNode's when using Lst_Concat */
-#define LST_CONCLINK 1 /* relink LstNode's when using Lst_Concat */
+/* Create or destroy a list */
-/*
- * Creation/destruction functions
- */
-/* Create a new list */
-Lst Lst_Init(Boolean);
-/* Duplicate an existing list */
-Lst Lst_Duplicate(Lst, DuplicateProc *);
-/* Destroy an old one */
-void Lst_Destroy(Lst, FreeProc *);
-/* True if list is empty */
-Boolean Lst_IsEmpty(Lst);
+/* Create a new list. */
+Lst Lst_Init(void);
+/* Duplicate an existing list. */
+Lst Lst_Copy(Lst, LstCopyProc);
+/* Free the list, leaving the node data unmodified. */
+void Lst_Free(Lst);
+/* Free the list, freeing the node data using the given function. */
+void Lst_Destroy(Lst, LstFreeProc);
-/*
- * Functions to modify a list
- */
-/* Insert an element before another */
-ReturnStatus Lst_InsertBefore(Lst, LstNode, void *);
-/* Insert an element after another */
-ReturnStatus Lst_InsertAfter(Lst, LstNode, void *);
-/* Place an element at the front of a lst. */
-ReturnStatus Lst_AtFront(Lst, void *);
-/* Place an element at the end of a lst. */
-ReturnStatus Lst_AtEnd(Lst, void *);
-/* Remove an element */
-ReturnStatus Lst_Remove(Lst, LstNode);
-/* Replace a node with a new value */
-ReturnStatus Lst_Replace(LstNode, void *);
-/* Concatenate two lists */
-ReturnStatus Lst_Concat(Lst, Lst, int);
+/* Get information about a list */
-/*
- * Node-specific functions
- */
-/* Return first element in list */
-LstNode Lst_First(Lst);
-/* Return last element in list */
-LstNode Lst_Last(Lst);
-/* Return successor to given element */
-LstNode Lst_Succ(LstNode);
-/* Return predecessor to given element */
-LstNode Lst_Prev(LstNode);
-/* Get datum from LstNode */
-void *Lst_Datum(LstNode);
+Boolean Lst_IsEmpty(Lst);
+/* Return the first node of the list, or NULL. */
+LstNode Lst_First(Lst);
+/* Return the last node of the list, or NULL. */
+LstNode Lst_Last(Lst);
+/* Find the first node for which the function returns TRUE, or NULL. */
+LstNode Lst_Find(Lst, LstFindProc, const void *);
+/* Find the first node for which the function returns TRUE, or NULL.
+ * The search starts at the given node, towards the end of the list. */
+LstNode Lst_FindFrom(Lst, LstNode, LstFindProc, const void *);
+/* Find the first node that contains the given datum, or NULL. */
+LstNode Lst_FindDatum(Lst, const void *);
-/*
- * Functions for entire lists
- */
-/* Find an element in a list */
-LstNode Lst_Find(Lst, const void *, int (*)(const void *, const void *));
-/* Find an element starting from somewhere */
-LstNode Lst_FindFrom(Lst, LstNode, const void *,
- int (*cProc)(const void *, const void *));
-/*
- * See if the given datum is on the list. Returns the LstNode containing
- * the datum
- */
-LstNode Lst_Member(Lst, void *);
-/* Apply a function to all elements of a lst */
-int Lst_ForEach(Lst, int (*)(void *, void *), void *);
-/*
- * Apply a function to all elements of a lst starting from a certain point.
- * If the list is circular, the application will wrap around to the
- * beginning of the list again.
- */
-int Lst_ForEachFrom(Lst, LstNode, int (*)(void *, void *),
- void *);
-/*
- * these functions are for dealing with a list as a table, of sorts.
- * An idea of the "current element" is kept and used by all the functions
- * between Lst_Open() and Lst_Close().
- */
-/* Open the list */
-ReturnStatus Lst_Open(Lst);
-/* Next element please */
-LstNode Lst_Next(Lst);
-/* Done yet? */
-Boolean Lst_IsAtEnd(Lst);
-/* Finish table access */
-void Lst_Close(Lst);
+/* Modify a list */
-/*
- * for using the list as a queue
- */
-/* Place an element at tail of queue */
-ReturnStatus Lst_EnQueue(Lst, void *);
-/* Remove an element from head of queue */
-void *Lst_DeQueue(Lst);
+/* Insert a datum before the given node. */
+void Lst_InsertBefore(Lst, LstNode, void *);
+/* Place a datum at the front of the list. */
+void Lst_Prepend(Lst, void *);
+/* Place a datum at the end of the list. */
+void Lst_Append(Lst, void *);
+/* Remove the node from the list. */
+void Lst_Remove(Lst, LstNode);
+void Lst_PrependAll(Lst, Lst);
+void Lst_AppendAll(Lst, Lst);
+void Lst_MoveAll(Lst, Lst);
+
+/* Node-specific functions */
+
+/* Return the successor of the node, or NULL. */
+LstNode LstNode_Next(LstNode);
+/* Return the predecessor of the node, or NULL. */
+LstNode LstNode_Prev(LstNode);
+/* Return the datum of the node. Usually not NULL. */
+void *LstNode_Datum(LstNode);
+/* Replace the value of the node. */
+void LstNode_Set(LstNode, void *);
+/* Set the value of the node to NULL. Having NULL in a list is unusual. */
+void LstNode_SetNull(LstNode);
+
+/* Iterating over a list, using a callback function */
+
+/* Apply a function to each datum of the list, until the callback function
+ * returns non-zero. */
+int Lst_ForEach(Lst, LstActionProc, void *);
+/* Apply a function to each datum of the list, starting at the node,
+ * until the callback function returns non-zero. */
+int Lst_ForEachFrom(Lst, LstNode, LstActionProc, void *);
+
+/* Iterating over a list while keeping track of the current node and possible
+ * concurrent modifications */
+
+/* Start iterating the list. */
+void Lst_Open(Lst);
+/* Return the next node, or NULL. */
+LstNode Lst_Next(Lst);
+/* Finish iterating the list. */
+void Lst_Close(Lst);
+
+/* Using the list as a queue */
+
+/* Add a datum at the tail of the queue. */
+void Lst_Enqueue(Lst, void *);
+/* Remove the head node of the queue and return its datum. */
+void *Lst_Dequeue(Lst);
-#endif /* _LST_H_ */
+#endif /* MAKE_LST_H */
diff --git a/contrib/bmake/lst.lib/Makefile b/contrib/bmake/lst.lib/Makefile
deleted file mode 100644
index e69de29bb2d1..000000000000
--- a/contrib/bmake/lst.lib/Makefile
+++ /dev/null
diff --git a/contrib/bmake/lst.lib/lstAppend.c b/contrib/bmake/lst.lib/lstAppend.c
deleted file mode 100644
index 97e60d959d7d..000000000000
--- a/contrib/bmake/lst.lib/lstAppend.c
+++ /dev/null
@@ -1,121 +0,0 @@
-/* $NetBSD: lstAppend.c,v 1.15 2020/07/03 08:37:56 rillig Exp $ */
-
-/*
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Adam de Boor.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: lstAppend.c,v 1.15 2020/07/03 08:37:56 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)lstAppend.c 8.1 (Berkeley) 6/6/93";
-#else
-__RCSID("$NetBSD: lstAppend.c,v 1.15 2020/07/03 08:37:56 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
-/*-
- * LstAppend.c --
- * Add a new node with a new datum after an existing node
- */
-
-#include "lstInt.h"
-
-/*-
- *-----------------------------------------------------------------------
- * Lst_InsertAfter --
- * Create a new node and add it to the given list after the given node.
- *
- * Input:
- * l affected list
- * ln node after which to append the datum
- * d said datum
- *
- * Results:
- * SUCCESS if all went well.
- *
- * Side Effects:
- * A new ListNode is created and linked in to the List. The lastPtr
- * field of the List will be altered if ln is the last node in the
- * list. lastPtr and firstPtr will alter if the list was empty and
- * ln was NULL.
- *
- *-----------------------------------------------------------------------
- */
-ReturnStatus
-Lst_InsertAfter(Lst l, LstNode ln, void *d)
-{
- List list;
- ListNode lNode;
- ListNode nLNode;
-
- if (LstValid (l) && (ln == NULL && LstIsEmpty (l))) {
- goto ok;
- }
-
- if (!LstValid (l) || LstIsEmpty (l) || ! LstNodeValid (ln, l)) {
- return FAILURE;
- }
- ok:
-
- list = l;
- lNode = ln;
-
- PAlloc (nLNode, ListNode);
- nLNode->datum = d;
- nLNode->useCount = nLNode->flags = 0;
-
- if (lNode == NULL) {
- if (list->isCirc) {
- nLNode->nextPtr = nLNode->prevPtr = nLNode;
- } else {
- nLNode->nextPtr = nLNode->prevPtr = NULL;
- }
- list->firstPtr = list->lastPtr = nLNode;
- } else {
- nLNode->prevPtr = lNode;
- nLNode->nextPtr = lNode->nextPtr;
-
- lNode->nextPtr = nLNode;
- if (nLNode->nextPtr != NULL) {
- nLNode->nextPtr->prevPtr = nLNode;
- }
-
- if (lNode == list->lastPtr) {
- list->lastPtr = nLNode;
- }
- }
-
- return SUCCESS;
-}
diff --git a/contrib/bmake/lst.lib/lstAtEnd.c b/contrib/bmake/lst.lib/lstAtEnd.c
deleted file mode 100644
index 4eadfdb0e7f3..000000000000
--- a/contrib/bmake/lst.lib/lstAtEnd.c
+++ /dev/null
@@ -1,79 +0,0 @@
-/* $NetBSD: lstAtEnd.c,v 1.14 2020/07/03 08:37:56 rillig Exp $ */
-
-/*
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Adam de Boor.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: lstAtEnd.c,v 1.14 2020/07/03 08:37:56 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)lstAtEnd.c 8.1 (Berkeley) 6/6/93";
-#else
-__RCSID("$NetBSD: lstAtEnd.c,v 1.14 2020/07/03 08:37:56 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
-/*-
- * LstAtEnd.c --
- * Add a node at the end of the list
- */
-
-#include "lstInt.h"
-
-/*-
- *-----------------------------------------------------------------------
- * Lst_AtEnd --
- * Add a node to the end of the given list
- *
- * Input:
- * l List to which to add the datum
- * d Datum to add
- *
- * Results:
- * SUCCESS if life is good.
- *
- * Side Effects:
- * A new ListNode is created and added to the list.
- *
- *-----------------------------------------------------------------------
- */
-ReturnStatus
-Lst_AtEnd(Lst l, void *d)
-{
- LstNode end;
-
- end = Lst_Last(l);
- return Lst_InsertAfter(l, end, d);
-}
diff --git a/contrib/bmake/lst.lib/lstAtFront.c b/contrib/bmake/lst.lib/lstAtFront.c
deleted file mode 100644
index 724713c092ed..000000000000
--- a/contrib/bmake/lst.lib/lstAtFront.c
+++ /dev/null
@@ -1,76 +0,0 @@
-/* $NetBSD: lstAtFront.c,v 1.14 2020/07/03 08:37:56 rillig Exp $ */
-
-/*
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Adam de Boor.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: lstAtFront.c,v 1.14 2020/07/03 08:37:56 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)lstAtFront.c 8.1 (Berkeley) 6/6/93";
-#else
-__RCSID("$NetBSD: lstAtFront.c,v 1.14 2020/07/03 08:37:56 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
-/*-
- * LstAtFront.c --
- * Add a node at the front of the list
- */
-
-#include "lstInt.h"
-
-/*-
- *-----------------------------------------------------------------------
- * Lst_AtFront --
- * Place a piece of data at the front of a list
- *
- * Results:
- * SUCCESS or FAILURE
- *
- * Side Effects:
- * A new ListNode is created and stuck at the front of the list.
- * hence, firstPtr (and possible lastPtr) in the list are altered.
- *
- *-----------------------------------------------------------------------
- */
-ReturnStatus
-Lst_AtFront(Lst l, void *d)
-{
- LstNode front;
-
- front = Lst_First(l);
- return Lst_InsertBefore(l, front, d);
-}
diff --git a/contrib/bmake/lst.lib/lstClose.c b/contrib/bmake/lst.lib/lstClose.c
deleted file mode 100644
index a1a3e9da80de..000000000000
--- a/contrib/bmake/lst.lib/lstClose.c
+++ /dev/null
@@ -1,85 +0,0 @@
-/* $NetBSD: lstClose.c,v 1.12 2020/07/03 08:37:56 rillig Exp $ */
-
-/*
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Adam de Boor.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: lstClose.c,v 1.12 2020/07/03 08:37:56 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)lstClose.c 8.1 (Berkeley) 6/6/93";
-#else
-__RCSID("$NetBSD: lstClose.c,v 1.12 2020/07/03 08:37:56 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
-/*-
- * LstClose.c --
- * Close a list for sequential access.
- * The sequential functions access the list in a slightly different way.
- * CurPtr points to their idea of the current node in the list and they
- * access the list based on it. Because the list is circular, Lst_Next
- * and Lst_Prev will go around the list forever. Lst_IsAtEnd must be
- * used to determine when to stop.
- */
-
-#include "lstInt.h"
-
-/*-
- *-----------------------------------------------------------------------
- * Lst_Close --
- * Close a list which was opened for sequential access.
- *
- * Input:
- * l The list to close
- *
- * Results:
- * None.
- *
- * Side Effects:
- * The list is closed.
- *
- *-----------------------------------------------------------------------
- */
-void
-Lst_Close(Lst l)
-{
- List list = l;
-
- if (LstValid(l) == TRUE) {
- list->isOpen = FALSE;
- list->atEnd = Unknown;
- }
-}
diff --git a/contrib/bmake/lst.lib/lstConcat.c b/contrib/bmake/lst.lib/lstConcat.c
deleted file mode 100644
index 2f667c5b0119..000000000000
--- a/contrib/bmake/lst.lib/lstConcat.c
+++ /dev/null
@@ -1,184 +0,0 @@
-/* $NetBSD: lstConcat.c,v 1.17 2020/07/03 08:37:56 rillig Exp $ */
-
-/*
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Adam de Boor.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: lstConcat.c,v 1.17 2020/07/03 08:37:56 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)lstConcat.c 8.1 (Berkeley) 6/6/93";
-#else
-__RCSID("$NetBSD: lstConcat.c,v 1.17 2020/07/03 08:37:56 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
-/*-
- * listConcat.c --
- * Function to concatentate two lists.
- */
-
-#include "lstInt.h"
-
-/*-
- *-----------------------------------------------------------------------
- * Lst_Concat --
- * Concatenate two lists. New elements are created to hold the data
- * elements, if specified, but the elements themselves are not copied.
- * If the elements should be duplicated to avoid confusion with another
- * list, the Lst_Duplicate function should be called first.
- * If LST_CONCLINK is specified, the second list is destroyed since
- * its pointers have been corrupted and the list is no longer useable.
- *
- * Input:
- * l1 The list to which l2 is to be appended
- * l2 The list to append to l1
- * flags LST_CONCNEW if LstNode's should be duplicated
- * LST_CONCLINK if should just be relinked
- *
- * Results:
- * SUCCESS if all went well. FAILURE otherwise.
- *
- * Side Effects:
- * New elements are created and appended the first list.
- *-----------------------------------------------------------------------
- */
-ReturnStatus
-Lst_Concat(Lst l1, Lst l2, int flags)
-{
- ListNode ln; /* original LstNode */
- ListNode nln; /* new LstNode */
- ListNode last; /* the last element in the list. Keeps
- * bookkeeping until the end */
- List list1 = l1;
- List list2 = l2;
-
- if (!LstValid (l1) || !LstValid (l2)) {
- return FAILURE;
- }
-
- if (flags == LST_CONCLINK) {
- if (list2->firstPtr != NULL) {
- /*
- * We set the nextPtr of the
- * last element of list two to be NIL to make the loop easier and
- * so we don't need an extra case should the first list turn
- * out to be non-circular -- the final element will already point
- * to NIL space and the first element will be untouched if it
- * existed before and will also point to NIL space if it didn't.
- */
- list2->lastPtr->nextPtr = NULL;
- /*
- * So long as the second list isn't empty, we just link the
- * first element of the second list to the last element of the
- * first list. If the first list isn't empty, we then link the
- * last element of the list to the first element of the second list
- * The last element of the second list, if it exists, then becomes
- * the last element of the first list.
- */
- list2->firstPtr->prevPtr = list1->lastPtr;
- if (list1->lastPtr != NULL) {
- list1->lastPtr->nextPtr = list2->firstPtr;
- } else {
- list1->firstPtr = list2->firstPtr;
- }
- list1->lastPtr = list2->lastPtr;
- }
- if (list1->isCirc && list1->firstPtr != NULL) {
- /*
- * If the first list is supposed to be circular and it is (now)
- * non-empty, we must make sure it's circular by linking the
- * first element to the last and vice versa
- */
- list1->firstPtr->prevPtr = list1->lastPtr;
- list1->lastPtr->nextPtr = list1->firstPtr;
- }
- free(l2);
- } else if (list2->firstPtr != NULL) {
- /*
- * We set the nextPtr of the last element of list 2 to be nil to make
- * the loop less difficult. The loop simply goes through the entire
- * second list creating new LstNodes and filling in the nextPtr, and
- * prevPtr to fit into l1 and its datum field from the
- * datum field of the corresponding element in l2. The 'last' node
- * follows the last of the new nodes along until the entire l2 has
- * been appended. Only then does the bookkeeping catch up with the
- * changes. During the first iteration of the loop, if 'last' is nil,
- * the first list must have been empty so the newly-created node is
- * made the first node of the list.
- */
- list2->lastPtr->nextPtr = NULL;
- for (last = list1->lastPtr, ln = list2->firstPtr;
- ln != NULL;
- ln = ln->nextPtr)
- {
- PAlloc (nln, ListNode);
- nln->datum = ln->datum;
- if (last != NULL) {
- last->nextPtr = nln;
- } else {
- list1->firstPtr = nln;
- }
- nln->prevPtr = last;
- nln->flags = nln->useCount = 0;
- last = nln;
- }
-
- /*
- * Finish bookkeeping. The last new element becomes the last element
- * of list one.
- */
- list1->lastPtr = last;
-
- /*
- * The circularity of both list one and list two must be corrected
- * for -- list one because of the new nodes added to it; list two
- * because of the alteration of list2->lastPtr's nextPtr to ease the
- * above for loop.
- */
- if (list1->isCirc) {
- list1->lastPtr->nextPtr = list1->firstPtr;
- list1->firstPtr->prevPtr = list1->lastPtr;
- } else {
- last->nextPtr = NULL;
- }
-
- if (list2->isCirc) {
- list2->lastPtr->nextPtr = list2->firstPtr;
- }
- }
-
- return SUCCESS;
-}
diff --git a/contrib/bmake/lst.lib/lstDatum.c b/contrib/bmake/lst.lib/lstDatum.c
deleted file mode 100644
index c8ccb558f3dc..000000000000
--- a/contrib/bmake/lst.lib/lstDatum.c
+++ /dev/null
@@ -1,76 +0,0 @@
-/* $NetBSD: lstDatum.c,v 1.14 2020/07/03 08:37:56 rillig Exp $ */
-
-/*
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Adam de Boor.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: lstDatum.c,v 1.14 2020/07/03 08:37:56 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)lstDatum.c 8.1 (Berkeley) 6/6/93";
-#else
-__RCSID("$NetBSD: lstDatum.c,v 1.14 2020/07/03 08:37:56 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
-/*-
- * LstDatum.c --
- * Return the datum associated with a list node.
- */
-
-#include "lstInt.h"
-
-/*-
- *-----------------------------------------------------------------------
- * Lst_Datum --
- * Return the datum stored in the given node.
- *
- * Results:
- * The datum or NULL if the node is invalid.
- *
- * Side Effects:
- * None.
- *
- *-----------------------------------------------------------------------
- */
-void *
-Lst_Datum(LstNode ln)
-{
- if (ln != NULL) {
- return ln->datum;
- } else {
- return NULL;
- }
-}
diff --git a/contrib/bmake/lst.lib/lstDeQueue.c b/contrib/bmake/lst.lib/lstDeQueue.c
deleted file mode 100644
index 0f1452e4ac7e..000000000000
--- a/contrib/bmake/lst.lib/lstDeQueue.c
+++ /dev/null
@@ -1,86 +0,0 @@
-/* $NetBSD: lstDeQueue.c,v 1.15 2020/07/03 08:37:56 rillig Exp $ */
-
-/*
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Adam de Boor.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: lstDeQueue.c,v 1.15 2020/07/03 08:37:56 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)lstDeQueue.c 8.1 (Berkeley) 6/6/93";
-#else
-__RCSID("$NetBSD: lstDeQueue.c,v 1.15 2020/07/03 08:37:56 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
-/*-
- * LstDeQueue.c --
- * Remove the node and return its datum from the head of the list
- */
-
-#include "lstInt.h"
-
-/*-
- *-----------------------------------------------------------------------
- * Lst_DeQueue --
- * Remove and return the datum at the head of the given list.
- *
- * Results:
- * The datum in the node at the head or NULL if the list
- * is empty.
- *
- * Side Effects:
- * The head node is removed from the list.
- *
- *-----------------------------------------------------------------------
- */
-void *
-Lst_DeQueue(Lst l)
-{
- void *rd;
- ListNode tln;
-
- tln = Lst_First(l);
- if (tln == NULL) {
- return NULL;
- }
-
- rd = tln->datum;
- if (Lst_Remove(l, tln) == FAILURE) {
- return NULL;
- } else {
- return rd;
- }
-}
diff --git a/contrib/bmake/lst.lib/lstDestroy.c b/contrib/bmake/lst.lib/lstDestroy.c
deleted file mode 100644
index 92c5b2b2050c..000000000000
--- a/contrib/bmake/lst.lib/lstDestroy.c
+++ /dev/null
@@ -1,101 +0,0 @@
-/* $NetBSD: lstDestroy.c,v 1.16 2008/12/13 15:19:29 dsl Exp $ */
-
-/*
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Adam de Boor.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: lstDestroy.c,v 1.16 2008/12/13 15:19:29 dsl Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)lstDestroy.c 8.1 (Berkeley) 6/6/93";
-#else
-__RCSID("$NetBSD: lstDestroy.c,v 1.16 2008/12/13 15:19:29 dsl Exp $");
-#endif
-#endif /* not lint */
-#endif
-
-/*-
- * LstDestroy.c --
- * Nuke a list and all its resources
- */
-
-#include "lstInt.h"
-
-/*-
- *-----------------------------------------------------------------------
- * Lst_Destroy --
- * Destroy a list and free all its resources. If the freeProc is
- * given, it is called with the datum from each node in turn before
- * the node is freed.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * The given list is freed in its entirety.
- *
- *-----------------------------------------------------------------------
- */
-void
-Lst_Destroy(Lst list, FreeProc *freeProc)
-{
- ListNode ln;
- ListNode tln = NULL;
-
- if (list == NULL)
- return;
-
- /* To ease scanning */
- if (list->lastPtr != NULL)
- list->lastPtr->nextPtr = NULL;
- else {
- free(list);
- return;
- }
-
- if (freeProc) {
- for (ln = list->firstPtr; ln != NULL; ln = tln) {
- tln = ln->nextPtr;
- freeProc(ln->datum);
- free(ln);
- }
- } else {
- for (ln = list->firstPtr; ln != NULL; ln = tln) {
- tln = ln->nextPtr;
- free(ln);
- }
- }
-
- free(list);
-}
diff --git a/contrib/bmake/lst.lib/lstDupl.c b/contrib/bmake/lst.lib/lstDupl.c
deleted file mode 100644
index 6318ee4e462a..000000000000
--- a/contrib/bmake/lst.lib/lstDupl.c
+++ /dev/null
@@ -1,107 +0,0 @@
-/* $NetBSD: lstDupl.c,v 1.17 2020/07/03 08:37:56 rillig Exp $ */
-
-/*
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Adam de Boor.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: lstDupl.c,v 1.17 2020/07/03 08:37:56 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)lstDupl.c 8.1 (Berkeley) 6/6/93";
-#else
-__RCSID("$NetBSD: lstDupl.c,v 1.17 2020/07/03 08:37:56 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
-/*-
- * listDupl.c --
- * Duplicate a list. This includes duplicating the individual
- * elements.
- */
-
-#include "lstInt.h"
-
-/*-
- *-----------------------------------------------------------------------
- * Lst_Duplicate --
- * Duplicate an entire list. If a function to copy a void *is
- * given, the individual client elements will be duplicated as well.
- *
- * Input:
- * l the list to duplicate
- * copyProc A function to duplicate each void *
- *
- * Results:
- * The new Lst structure or NULL if failure.
- *
- * Side Effects:
- * A new list is created.
- *-----------------------------------------------------------------------
- */
-Lst
-Lst_Duplicate(Lst l, DuplicateProc *copyProc)
-{
- Lst nl;
- ListNode ln;
- List list = l;
-
- if (!LstValid (l)) {
- return NULL;
- }
-
- nl = Lst_Init(list->isCirc);
- if (nl == NULL) {
- return NULL;
- }
-
- ln = list->firstPtr;
- while (ln != NULL) {
- if (copyProc != NULL) {
- if (Lst_AtEnd(nl, copyProc(ln->datum)) == FAILURE) {
- return NULL;
- }
- } else if (Lst_AtEnd(nl, ln->datum) == FAILURE) {
- return NULL;
- }
-
- if (list->isCirc && ln == list->lastPtr) {
- ln = NULL;
- } else {
- ln = ln->nextPtr;
- }
- }
-
- return nl;
-}
diff --git a/contrib/bmake/lst.lib/lstEnQueue.c b/contrib/bmake/lst.lib/lstEnQueue.c
deleted file mode 100644
index c6941a8eb9d3..000000000000
--- a/contrib/bmake/lst.lib/lstEnQueue.c
+++ /dev/null
@@ -1,77 +0,0 @@
-/* $NetBSD: lstEnQueue.c,v 1.14 2020/07/03 08:37:56 rillig Exp $ */
-
-/*
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Adam de Boor.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: lstEnQueue.c,v 1.14 2020/07/03 08:37:56 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)lstEnQueue.c 8.1 (Berkeley) 6/6/93";
-#else
-__RCSID("$NetBSD: lstEnQueue.c,v 1.14 2020/07/03 08:37:56 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
-/*-
- * LstEnQueue.c--
- * Treat the list as a queue and place a datum at its end
- */
-
-#include "lstInt.h"
-
-/*-
- *-----------------------------------------------------------------------
- * Lst_EnQueue --
- * Add the datum to the tail of the given list.
- *
- * Results:
- * SUCCESS or FAILURE as returned by Lst_InsertAfter.
- *
- * Side Effects:
- * the lastPtr field is altered all the time and the firstPtr field
- * will be altered if the list used to be empty.
- *
- *-----------------------------------------------------------------------
- */
-ReturnStatus
-Lst_EnQueue(Lst l, void *d)
-{
- if (LstValid (l) == FALSE) {
- return FAILURE;
- }
-
- return Lst_InsertAfter(l, Lst_Last(l), d);
-}
diff --git a/contrib/bmake/lst.lib/lstFind.c b/contrib/bmake/lst.lib/lstFind.c
deleted file mode 100644
index a1d27d3ad686..000000000000
--- a/contrib/bmake/lst.lib/lstFind.c
+++ /dev/null
@@ -1,73 +0,0 @@
-/* $NetBSD: lstFind.c,v 1.16 2020/07/03 08:37:56 rillig Exp $ */
-
-/*
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Adam de Boor.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: lstFind.c,v 1.16 2020/07/03 08:37:56 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)lstFind.c 8.1 (Berkeley) 6/6/93";
-#else
-__RCSID("$NetBSD: lstFind.c,v 1.16 2020/07/03 08:37:56 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
-/*-
- * LstFind.c --
- * Find a node on a list.
- */
-
-#include "lstInt.h"
-
-/*-
- *-----------------------------------------------------------------------
- * Lst_Find --
- * Find a node on the given list using the given comparison function
- * and the given datum.
- *
- * Results:
- * The found node or NULL if none matches.
- *
- * Side Effects:
- * None.
- *
- *-----------------------------------------------------------------------
- */
-LstNode
-Lst_Find(Lst l, const void *d, int (*cProc)(const void *, const void *))
-{
- return Lst_FindFrom(l, Lst_First(l), d, cProc);
-}
diff --git a/contrib/bmake/lst.lib/lstFindFrom.c b/contrib/bmake/lst.lib/lstFindFrom.c
deleted file mode 100644
index 676c07392039..000000000000
--- a/contrib/bmake/lst.lib/lstFindFrom.c
+++ /dev/null
@@ -1,89 +0,0 @@
-/* $NetBSD: lstFindFrom.c,v 1.16 2020/07/03 08:37:56 rillig Exp $ */
-
-/*
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Adam de Boor.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: lstFindFrom.c,v 1.16 2020/07/03 08:37:56 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)lstFindFrom.c 8.1 (Berkeley) 6/6/93";
-#else
-__RCSID("$NetBSD: lstFindFrom.c,v 1.16 2020/07/03 08:37:56 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
-/*-
- * LstFindFrom.c --
- * Find a node on a list from a given starting point. Used by Lst_Find.
- */
-
-#include "lstInt.h"
-
-/*-
- *-----------------------------------------------------------------------
- * Lst_FindFrom --
- * Search for a node starting and ending with the given one on the
- * given list using the passed datum and comparison function to
- * determine when it has been found.
- *
- * Results:
- * The found node or NULL
- *
- * Side Effects:
- * None.
- *
- *-----------------------------------------------------------------------
- */
-LstNode
-Lst_FindFrom(Lst l, LstNode ln, const void *d,
- int (*cProc)(const void *, const void *))
-{
- ListNode tln;
-
- if (!LstValid (l) || LstIsEmpty (l) || !LstNodeValid (ln, l)) {
- return NULL;
- }
-
- tln = ln;
-
- do {
- if ((*cProc)(tln->datum, d) == 0)
- return tln;
- tln = tln->nextPtr;
- } while (tln != ln && tln != NULL);
-
- return NULL;
-}
diff --git a/contrib/bmake/lst.lib/lstFirst.c b/contrib/bmake/lst.lib/lstFirst.c
deleted file mode 100644
index a79db57120dd..000000000000
--- a/contrib/bmake/lst.lib/lstFirst.c
+++ /dev/null
@@ -1,76 +0,0 @@
-/* $NetBSD: lstFirst.c,v 1.13 2020/07/03 08:37:56 rillig Exp $ */
-
-/*
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Adam de Boor.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: lstFirst.c,v 1.13 2020/07/03 08:37:56 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)lstFirst.c 8.1 (Berkeley) 6/6/93";
-#else
-__RCSID("$NetBSD: lstFirst.c,v 1.13 2020/07/03 08:37:56 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
-/*-
- * LstFirst.c --
- * Return the first node of a list
- */
-
-#include "lstInt.h"
-
-/*-
- *-----------------------------------------------------------------------
- * Lst_First --
- * Return the first node on the given list.
- *
- * Results:
- * The first node or NULL if the list is empty.
- *
- * Side Effects:
- * None.
- *
- *-----------------------------------------------------------------------
- */
-LstNode
-Lst_First(Lst l)
-{
- if (!LstValid (l) || LstIsEmpty (l)) {
- return NULL;
- } else {
- return l->firstPtr;
- }
-}
diff --git a/contrib/bmake/lst.lib/lstForEach.c b/contrib/bmake/lst.lib/lstForEach.c
deleted file mode 100644
index dc2fdd8bfc57..000000000000
--- a/contrib/bmake/lst.lib/lstForEach.c
+++ /dev/null
@@ -1,75 +0,0 @@
-/* $NetBSD: lstForEach.c,v 1.14 2020/07/03 08:37:57 rillig Exp $ */
-
-/*
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Adam de Boor.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: lstForEach.c,v 1.14 2020/07/03 08:37:57 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)lstForEach.c 8.1 (Berkeley) 6/6/93";
-#else
-__RCSID("$NetBSD: lstForEach.c,v 1.14 2020/07/03 08:37:57 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
-/*-
- * LstForeach.c --
- * Perform a given function on all elements of a list.
- */
-
-#include "lstInt.h"
-
-/*-
- *-----------------------------------------------------------------------
- * Lst_ForEach --
- * Apply the given function to each element of the given list. The
- * function should return 0 if Lst_ForEach should continue and non-
- * zero if it should abort.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * Only those created by the passed-in function.
- *
- *-----------------------------------------------------------------------
- */
-/*VARARGS2*/
-int
-Lst_ForEach(Lst l, int (*proc)(void *, void *), void *d)
-{
- return Lst_ForEachFrom(l, Lst_First(l), proc, d);
-}
diff --git a/contrib/bmake/lst.lib/lstForEachFrom.c b/contrib/bmake/lst.lib/lstForEachFrom.c
deleted file mode 100644
index a08ddf35935b..000000000000
--- a/contrib/bmake/lst.lib/lstForEachFrom.c
+++ /dev/null
@@ -1,124 +0,0 @@
-/* $NetBSD: lstForEachFrom.c,v 1.18 2020/07/03 08:37:57 rillig Exp $ */
-
-/*
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Adam de Boor.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: lstForEachFrom.c,v 1.18 2020/07/03 08:37:57 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)lstForEachFrom.c 8.1 (Berkeley) 6/6/93";
-#else
-__RCSID("$NetBSD: lstForEachFrom.c,v 1.18 2020/07/03 08:37:57 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
-/*-
- * lstForEachFrom.c --
- * Perform a given function on all elements of a list starting from
- * a given point.
- */
-
-#include "lstInt.h"
-
-/*-
- *-----------------------------------------------------------------------
- * Lst_ForEachFrom --
- * Apply the given function to each element of the given list. The
- * function should return 0 if traversal should continue and non-
- * zero if it should abort.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * Only those created by the passed-in function.
- *
- *-----------------------------------------------------------------------
- */
-/*VARARGS2*/
-int
-Lst_ForEachFrom(Lst l, LstNode ln, int (*proc)(void *, void *),
- void *d)
-{
- ListNode tln = ln;
- List list = l;
- ListNode next;
- Boolean done;
- int result;
-
- if (!LstValid (list) || LstIsEmpty (list)) {
- return 0;
- }
-
- do {
- /*
- * Take care of having the current element deleted out from under
- * us.
- */
-
- next = tln->nextPtr;
-
- /*
- * We're done with the traversal if
- * - the next node to examine is the first in the queue or
- * doesn't exist and
- * - nothing's been added after the current node (check this
- * after proc() has been called).
- */
- done = (next == NULL || next == list->firstPtr);
-
- (void) tln->useCount++;
- result = (*proc) (tln->datum, d);
- (void) tln->useCount--;
-
- /*
- * Now check whether a node has been added.
- * Note: this doesn't work if this node was deleted before
- * the new node was added.
- */
- if (next != tln->nextPtr) {
- next = tln->nextPtr;
- done = 0;
- }
-
- if (tln->flags & LN_DELETED) {
- free((char *)tln);
- }
- tln = next;
- } while (!result && !LstIsEmpty(list) && !done);
-
- return result;
-}
diff --git a/contrib/bmake/lst.lib/lstInit.c b/contrib/bmake/lst.lib/lstInit.c
deleted file mode 100644
index 3255da7e59a1..000000000000
--- a/contrib/bmake/lst.lib/lstInit.c
+++ /dev/null
@@ -1,85 +0,0 @@
-/* $NetBSD: lstInit.c,v 1.13 2020/07/03 08:37:57 rillig Exp $ */
-
-/*
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Adam de Boor.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: lstInit.c,v 1.13 2020/07/03 08:37:57 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)lstInit.c 8.1 (Berkeley) 6/6/93";
-#else
-__RCSID("$NetBSD: lstInit.c,v 1.13 2020/07/03 08:37:57 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
-/*-
- * init.c --
- * Initialize a new linked list.
- */
-
-#include "lstInt.h"
-
-/*-
- *-----------------------------------------------------------------------
- * Lst_Init --
- * Create and initialize a new list.
- *
- * Input:
- * circ TRUE if the list should be made circular
- *
- * Results:
- * The created list.
- *
- * Side Effects:
- * A list is created, what else?
- *
- *-----------------------------------------------------------------------
- */
-Lst
-Lst_Init(Boolean circ)
-{
- List nList;
-
- PAlloc (nList, List);
-
- nList->firstPtr = NULL;
- nList->lastPtr = NULL;
- nList->isOpen = FALSE;
- nList->isCirc = circ;
- nList->atEnd = Unknown;
-
- return nList;
-}
diff --git a/contrib/bmake/lst.lib/lstInsert.c b/contrib/bmake/lst.lib/lstInsert.c
deleted file mode 100644
index 845b8899e03b..000000000000
--- a/contrib/bmake/lst.lib/lstInsert.c
+++ /dev/null
@@ -1,121 +0,0 @@
-/* $NetBSD: lstInsert.c,v 1.15 2020/07/03 08:37:57 rillig Exp $ */
-
-/*
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Adam de Boor.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: lstInsert.c,v 1.15 2020/07/03 08:37:57 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)lstInsert.c 8.1 (Berkeley) 6/6/93";
-#else
-__RCSID("$NetBSD: lstInsert.c,v 1.15 2020/07/03 08:37:57 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
-/*-
- * LstInsert.c --
- * Insert a new datum before an old one
- */
-
-#include "lstInt.h"
-
-/*-
- *-----------------------------------------------------------------------
- * Lst_InsertBefore --
- * Insert a new node with the given piece of data before the given
- * node in the given list.
- *
- * Input:
- * l list to manipulate
- * ln node before which to insert d
- * d datum to be inserted
- *
- * Results:
- * SUCCESS or FAILURE.
- *
- * Side Effects:
- * the firstPtr field will be changed if ln is the first node in the
- * list.
- *
- *-----------------------------------------------------------------------
- */
-ReturnStatus
-Lst_InsertBefore(Lst l, LstNode ln, void *d)
-{
- ListNode nLNode; /* new lnode for d */
- ListNode lNode = ln;
- List list = l;
-
-
- /*
- * check validity of arguments
- */
- if (LstValid (l) && (LstIsEmpty (l) && ln == NULL))
- goto ok;
-
- if (!LstValid (l) || LstIsEmpty (l) || !LstNodeValid (ln, l)) {
- return FAILURE;
- }
-
- ok:
- PAlloc (nLNode, ListNode);
-
- nLNode->datum = d;
- nLNode->useCount = nLNode->flags = 0;
-
- if (ln == NULL) {
- if (list->isCirc) {
- nLNode->prevPtr = nLNode->nextPtr = nLNode;
- } else {
- nLNode->prevPtr = nLNode->nextPtr = NULL;
- }
- list->firstPtr = list->lastPtr = nLNode;
- } else {
- nLNode->prevPtr = lNode->prevPtr;
- nLNode->nextPtr = lNode;
-
- if (nLNode->prevPtr != NULL) {
- nLNode->prevPtr->nextPtr = nLNode;
- }
- lNode->prevPtr = nLNode;
-
- if (lNode == list->firstPtr) {
- list->firstPtr = nLNode;
- }
- }
-
- return SUCCESS;
-}
diff --git a/contrib/bmake/lst.lib/lstInt.h b/contrib/bmake/lst.lib/lstInt.h
deleted file mode 100644
index ac53dcb67d77..000000000000
--- a/contrib/bmake/lst.lib/lstInt.h
+++ /dev/null
@@ -1,105 +0,0 @@
-/* $NetBSD: lstInt.h,v 1.22 2014/09/07 20:55:34 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: @(#)lstInt.h 8.1 (Berkeley) 6/6/93
- */
-
-/*-
- * lstInt.h --
- * Internals for the list library
- */
-#ifndef _LSTINT_H_
-#define _LSTINT_H_
-
-#include "../lst.h"
-#include "../make_malloc.h"
-
-typedef struct ListNode {
- struct ListNode *prevPtr; /* previous element in list */
- struct ListNode *nextPtr; /* next in list */
- unsigned int useCount:8, /* Count of functions using the node.
- * node may not be deleted until count
- * goes to 0 */
- flags:8; /* Node status flags */
- void *datum; /* datum associated with this element */
-} *ListNode;
-/*
- * Flags required for synchronization
- */
-#define LN_DELETED 0x0001 /* List node should be removed when done */
-
-typedef enum {
- Head, Middle, Tail, Unknown
-} Where;
-
-typedef struct List {
- ListNode firstPtr; /* first node in list */
- ListNode lastPtr; /* last node in list */
- Boolean isCirc; /* true if the list should be considered
- * circular */
-/*
- * fields for sequential access
- */
- Where atEnd; /* Where in the list the last access was */
- Boolean isOpen; /* true if list has been Lst_Open'ed */
- ListNode curPtr; /* current node, if open. NULL if
- * *just* opened */
- ListNode prevPtr; /* Previous node, if open. Used by
- * Lst_Remove */
-} *List;
-
-/*
- * PAlloc (var, ptype) --
- * Allocate a pointer-typedef structure 'ptype' into the variable 'var'
- */
-#define PAlloc(var,ptype) var = (ptype) bmake_malloc(sizeof *(var))
-
-/*
- * LstValid (l) --
- * Return TRUE if the list l is valid
- */
-#define LstValid(l) ((Lst)(l) != NULL)
-
-/*
- * LstNodeValid (ln, l) --
- * Return TRUE if the LstNode ln is valid with respect to l
- */
-#define LstNodeValid(ln, l) ((ln) != NULL)
-
-/*
- * LstIsEmpty (l) --
- * TRUE if the list l is empty.
- */
-#define LstIsEmpty(l) (((List)(l))->firstPtr == NULL)
-
-#endif /* _LSTINT_H_ */
diff --git a/contrib/bmake/lst.lib/lstIsAtEnd.c b/contrib/bmake/lst.lib/lstIsAtEnd.c
deleted file mode 100644
index c5add4d9867f..000000000000
--- a/contrib/bmake/lst.lib/lstIsAtEnd.c
+++ /dev/null
@@ -1,86 +0,0 @@
-/* $NetBSD: lstIsAtEnd.c,v 1.14 2020/07/03 08:37:57 rillig Exp $ */
-
-/*
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Adam de Boor.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: lstIsAtEnd.c,v 1.14 2020/07/03 08:37:57 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)lstIsAtEnd.c 8.1 (Berkeley) 6/6/93";
-#else
-__RCSID("$NetBSD: lstIsAtEnd.c,v 1.14 2020/07/03 08:37:57 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
-/*-
- * LstIsAtEnd.c --
- * Tell if the current node is at the end of the list.
- * The sequential functions access the list in a slightly different way.
- * CurPtr points to their idea of the current node in the list and they
- * access the list based on it. Because the list is circular, Lst_Next
- * and Lst_Prev will go around the list forever. Lst_IsAtEnd must be
- * used to determine when to stop.
- */
-
-#include "lstInt.h"
-
-/*-
- *-----------------------------------------------------------------------
- * Lst_IsAtEnd --
- * Return true if have reached the end of the given list.
- *
- * Results:
- * TRUE if at the end of the list (this includes the list not being
- * open or being invalid) or FALSE if not. We return TRUE if the list
- * is invalid or unopend so as to cause the caller to exit its loop
- * asap, the assumption being that the loop is of the form
- * while (!Lst_IsAtEnd (l)) {
- * ...
- * }
- *
- * Side Effects:
- * None.
- *
- *-----------------------------------------------------------------------
- */
-Boolean
-Lst_IsAtEnd(Lst l)
-{
- List list = l;
-
- return !LstValid (l) || !list->isOpen ||
- list->atEnd == Head || list->atEnd == Tail;
-}
diff --git a/contrib/bmake/lst.lib/lstIsEmpty.c b/contrib/bmake/lst.lib/lstIsEmpty.c
deleted file mode 100644
index ccf4525a3506..000000000000
--- a/contrib/bmake/lst.lib/lstIsEmpty.c
+++ /dev/null
@@ -1,74 +0,0 @@
-/* $NetBSD: lstIsEmpty.c,v 1.12 2020/07/03 08:37:57 rillig Exp $ */
-
-/*
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Adam de Boor.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: lstIsEmpty.c,v 1.12 2020/07/03 08:37:57 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)lstIsEmpty.c 8.1 (Berkeley) 6/6/93";
-#else
-__RCSID("$NetBSD: lstIsEmpty.c,v 1.12 2020/07/03 08:37:57 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
-/*-
- * LstIsEmpty.c --
- * A single function to decide if a list is empty
- */
-
-#include "lstInt.h"
-
-/*-
- *-----------------------------------------------------------------------
- * Lst_IsEmpty --
- * Return TRUE if the given list is empty.
- *
- * Results:
- * TRUE if the list is empty, FALSE otherwise.
- *
- * Side Effects:
- * None.
- *
- * A list is considered empty if its firstPtr == NULL (or if
- * the list itself is NULL).
- *-----------------------------------------------------------------------
- */
-Boolean
-Lst_IsEmpty(Lst l)
-{
- return !LstValid(l) || LstIsEmpty(l);
-}
diff --git a/contrib/bmake/lst.lib/lstLast.c b/contrib/bmake/lst.lib/lstLast.c
deleted file mode 100644
index 1d65bf19473e..000000000000
--- a/contrib/bmake/lst.lib/lstLast.c
+++ /dev/null
@@ -1,76 +0,0 @@
-/* $NetBSD: lstLast.c,v 1.13 2020/07/03 08:37:57 rillig Exp $ */
-
-/*
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Adam de Boor.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: lstLast.c,v 1.13 2020/07/03 08:37:57 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)lstLast.c 8.1 (Berkeley) 6/6/93";
-#else
-__RCSID("$NetBSD: lstLast.c,v 1.13 2020/07/03 08:37:57 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
-/*-
- * LstLast.c --
- * Return the last element of a list
- */
-
-#include "lstInt.h"
-
-/*-
- *-----------------------------------------------------------------------
- * Lst_Last --
- * Return the last node on the list l.
- *
- * Results:
- * The requested node or NULL if the list is empty.
- *
- * Side Effects:
- * None.
- *
- *-----------------------------------------------------------------------
- */
-LstNode
-Lst_Last(Lst l)
-{
- if (!LstValid(l) || LstIsEmpty (l)) {
- return NULL;
- } else {
- return l->lastPtr;
- }
-}
diff --git a/contrib/bmake/lst.lib/lstMember.c b/contrib/bmake/lst.lib/lstMember.c
deleted file mode 100644
index e9046aca1436..000000000000
--- a/contrib/bmake/lst.lib/lstMember.c
+++ /dev/null
@@ -1,77 +0,0 @@
-/* $NetBSD: lstMember.c,v 1.14 2013/11/14 00:01:28 sjg Exp $ */
-
-/*
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Adam de Boor.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: lstMember.c,v 1.14 2013/11/14 00:01:28 sjg Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)lstMember.c 8.1 (Berkeley) 6/6/93";
-#else
-__RCSID("$NetBSD: lstMember.c,v 1.14 2013/11/14 00:01:28 sjg Exp $");
-#endif
-#endif /* not lint */
-#endif
-
-/*-
- * lstMember.c --
- * See if a given datum is on a given list.
- */
-
-#include "lstInt.h"
-
-LstNode
-Lst_Member(Lst l, void *d)
-{
- List list = l;
- ListNode lNode;
-
- if (list == NULL) {
- return NULL;
- }
- lNode = list->firstPtr;
- if (lNode == NULL) {
- return NULL;
- }
-
- do {
- if (lNode->datum == d) {
- return lNode;
- }
- lNode = lNode->nextPtr;
- } while (lNode != NULL && lNode != list->firstPtr);
-
- return NULL;
-}
diff --git a/contrib/bmake/lst.lib/lstNext.c b/contrib/bmake/lst.lib/lstNext.c
deleted file mode 100644
index 9c180d2cfad1..000000000000
--- a/contrib/bmake/lst.lib/lstNext.c
+++ /dev/null
@@ -1,119 +0,0 @@
-/* $NetBSD: lstNext.c,v 1.13 2020/07/03 08:37:57 rillig Exp $ */
-
-/*
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Adam de Boor.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: lstNext.c,v 1.13 2020/07/03 08:37:57 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)lstNext.c 8.1 (Berkeley) 6/6/93";
-#else
-__RCSID("$NetBSD: lstNext.c,v 1.13 2020/07/03 08:37:57 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
-/*-
- * LstNext.c --
- * Return the next node for a list.
- * The sequential functions access the list in a slightly different way.
- * CurPtr points to their idea of the current node in the list and they
- * access the list based on it. Because the list is circular, Lst_Next
- * and Lst_Prev will go around the list forever. Lst_IsAtEnd must be
- * used to determine when to stop.
- */
-
-#include "lstInt.h"
-
-/*-
- *-----------------------------------------------------------------------
- * Lst_Next --
- * Return the next node for the given list.
- *
- * Results:
- * The next node or NULL if the list has yet to be opened. Also
- * if the list is non-circular and the end has been reached, NULL
- * is returned.
- *
- * Side Effects:
- * the curPtr field is updated.
- *
- *-----------------------------------------------------------------------
- */
-LstNode
-Lst_Next(Lst l)
-{
- ListNode tln;
- List list = l;
-
- if ((LstValid (l) == FALSE) ||
- (list->isOpen == FALSE)) {
- return NULL;
- }
-
- list->prevPtr = list->curPtr;
-
- if (list->curPtr == NULL) {
- if (list->atEnd == Unknown) {
- /*
- * If we're just starting out, atEnd will be Unknown.
- * Then we want to start this thing off in the right
- * direction -- at the start with atEnd being Middle.
- */
- list->curPtr = tln = list->firstPtr;
- list->atEnd = Middle;
- } else {
- tln = NULL;
- list->atEnd = Tail;
- }
- } else {
- tln = list->curPtr->nextPtr;
- list->curPtr = tln;
-
- if (tln == list->firstPtr || tln == NULL) {
- /*
- * If back at the front, then we've hit the end...
- */
- list->atEnd = Tail;
- } else {
- /*
- * Reset to Middle if gone past first.
- */
- list->atEnd = Middle;
- }
- }
-
- return tln;
-}
diff --git a/contrib/bmake/lst.lib/lstOpen.c b/contrib/bmake/lst.lib/lstOpen.c
deleted file mode 100644
index 919dd6d5000c..000000000000
--- a/contrib/bmake/lst.lib/lstOpen.c
+++ /dev/null
@@ -1,86 +0,0 @@
-/* $NetBSD: lstOpen.c,v 1.13 2020/07/03 08:37:57 rillig Exp $ */
-
-/*
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Adam de Boor.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: lstOpen.c,v 1.13 2020/07/03 08:37:57 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)lstOpen.c 8.1 (Berkeley) 6/6/93";
-#else
-__RCSID("$NetBSD: lstOpen.c,v 1.13 2020/07/03 08:37:57 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
-/*-
- * LstOpen.c --
- * Open a list for sequential access. The sequential functions access the
- * list in a slightly different way. CurPtr points to their idea of the
- * current node in the list and they access the list based on it.
- * If the list is circular, Lst_Next and Lst_Prev will go around
- * the list forever. Lst_IsAtEnd must be used to determine when to stop.
- */
-
-#include "lstInt.h"
-
-/*-
- *-----------------------------------------------------------------------
- * Lst_Open --
- * Open a list for sequential access. A list can still be searched,
- * etc., without confusing these functions.
- *
- * Results:
- * SUCCESS or FAILURE.
- *
- * Side Effects:
- * isOpen is set TRUE and curPtr is set to NULL so the
- * other sequential functions no it was just opened and can choose
- * the first element accessed based on this.
- *
- *-----------------------------------------------------------------------
- */
-ReturnStatus
-Lst_Open(Lst l)
-{
- if (LstValid (l) == FALSE) {
- return FAILURE;
- }
- (l)->isOpen = TRUE;
- (l)->atEnd = LstIsEmpty (l) ? Head : Unknown;
- (l)->curPtr = NULL;
-
- return SUCCESS;
-}
diff --git a/contrib/bmake/lst.lib/lstPrev.c b/contrib/bmake/lst.lib/lstPrev.c
deleted file mode 100644
index b6c548d9a523..000000000000
--- a/contrib/bmake/lst.lib/lstPrev.c
+++ /dev/null
@@ -1,78 +0,0 @@
-/* $NetBSD: lstPrev.c,v 1.4 2020/07/03 08:37:57 rillig Exp $ */
-
-/*
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Adam de Boor.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: lstPrev.c,v 1.4 2020/07/03 08:37:57 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)lstSucc.c 8.1 (Berkeley) 6/6/93";
-#else
-__RCSID("$NetBSD: lstPrev.c,v 1.4 2020/07/03 08:37:57 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
-/*-
- * LstPrev.c --
- * return the predecessor to a given node
- */
-
-#include "lstInt.h"
-
-/*-
- *-----------------------------------------------------------------------
- * Lst_Prev --
- * Return the predecessor to the given node on its list.
- *
- * Results:
- * The predecessor of the node, if it exists (note that on a circular
- * list, if the node is the only one in the list, it is its own
- * predecessor).
- *
- * Side Effects:
- * None.
- *
- *-----------------------------------------------------------------------
- */
-LstNode
-Lst_Prev(LstNode ln)
-{
- if (ln == NULL) {
- return NULL;
- } else {
- return ln->prevPtr;
- }
-}
diff --git a/contrib/bmake/lst.lib/lstRemove.c b/contrib/bmake/lst.lib/lstRemove.c
deleted file mode 100644
index 59245499bdc4..000000000000
--- a/contrib/bmake/lst.lib/lstRemove.c
+++ /dev/null
@@ -1,134 +0,0 @@
-/* $NetBSD: lstRemove.c,v 1.17 2020/07/03 08:37:57 rillig Exp $ */
-
-/*
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Adam de Boor.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: lstRemove.c,v 1.17 2020/07/03 08:37:57 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)lstRemove.c 8.1 (Berkeley) 6/6/93";
-#else
-__RCSID("$NetBSD: lstRemove.c,v 1.17 2020/07/03 08:37:57 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
-/*-
- * LstRemove.c --
- * Remove an element from a list
- */
-
-#include "lstInt.h"
-
-/*-
- *-----------------------------------------------------------------------
- * Lst_Remove --
- * Remove the given node from the given list.
- *
- * Results:
- * SUCCESS or FAILURE.
- *
- * Side Effects:
- * The list's firstPtr will be set to NULL if ln is the last
- * node on the list. firsPtr and lastPtr will be altered if ln is
- * either the first or last node, respectively, on the list.
- *
- *-----------------------------------------------------------------------
- */
-ReturnStatus
-Lst_Remove(Lst l, LstNode ln)
-{
- List list = l;
- ListNode lNode = ln;
-
- if (!LstValid (l) || !LstNodeValid (ln, l)) {
- return FAILURE;
- }
-
- /*
- * unlink it from the list
- */
- if (lNode->nextPtr != NULL) {
- lNode->nextPtr->prevPtr = lNode->prevPtr;
- }
- if (lNode->prevPtr != NULL) {
- lNode->prevPtr->nextPtr = lNode->nextPtr;
- }
-
- /*
- * if either the firstPtr or lastPtr of the list point to this node,
- * adjust them accordingly
- */
- if (list->firstPtr == lNode) {
- list->firstPtr = lNode->nextPtr;
- }
- if (list->lastPtr == lNode) {
- list->lastPtr = lNode->prevPtr;
- }
-
- /*
- * Sequential access stuff. If the node we're removing is the current
- * node in the list, reset the current node to the previous one. If the
- * previous one was non-existent (prevPtr == NULL), we set the
- * end to be Unknown, since it is.
- */
- if (list->isOpen && (list->curPtr == lNode)) {
- list->curPtr = list->prevPtr;
- if (list->curPtr == NULL) {
- list->atEnd = Unknown;
- }
- }
-
- /*
- * the only way firstPtr can still point to ln is if ln is the last
- * node on the list (the list is circular, so lNode->nextptr == lNode in
- * this case). The list is, therefore, empty and is marked as such
- */
- if (list->firstPtr == lNode) {
- list->firstPtr = NULL;
- }
-
- /*
- * note that the datum is unmolested. The caller must free it as
- * necessary and as expected.
- */
- if (lNode->useCount == 0) {
- free(ln);
- } else {
- lNode->flags |= LN_DELETED;
- }
-
- return SUCCESS;
-}
diff --git a/contrib/bmake/lst.lib/lstReplace.c b/contrib/bmake/lst.lib/lstReplace.c
deleted file mode 100644
index f30cb00855e3..000000000000
--- a/contrib/bmake/lst.lib/lstReplace.c
+++ /dev/null
@@ -1,77 +0,0 @@
-/* $NetBSD: lstReplace.c,v 1.14 2020/07/03 08:37:57 rillig Exp $ */
-
-/*
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Adam de Boor.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: lstReplace.c,v 1.14 2020/07/03 08:37:57 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)lstReplace.c 8.1 (Berkeley) 6/6/93";
-#else
-__RCSID("$NetBSD: lstReplace.c,v 1.14 2020/07/03 08:37:57 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
-/*-
- * LstReplace.c --
- * Replace the datum in a node with a new datum
- */
-
-#include "lstInt.h"
-
-/*-
- *-----------------------------------------------------------------------
- * Lst_Replace --
- * Replace the datum in the given node with the new datum
- *
- * Results:
- * SUCCESS or FAILURE.
- *
- * Side Effects:
- * The datum field fo the node is altered.
- *
- *-----------------------------------------------------------------------
- */
-ReturnStatus
-Lst_Replace(LstNode ln, void *d)
-{
- if (ln == NULL) {
- return FAILURE;
- } else {
- (ln)->datum = d;
- return SUCCESS;
- }
-}
diff --git a/contrib/bmake/lst.lib/lstSucc.c b/contrib/bmake/lst.lib/lstSucc.c
deleted file mode 100644
index b3f73bb15fd2..000000000000
--- a/contrib/bmake/lst.lib/lstSucc.c
+++ /dev/null
@@ -1,78 +0,0 @@
-/* $NetBSD: lstSucc.c,v 1.14 2020/07/03 08:37:57 rillig Exp $ */
-
-/*
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Adam de Boor.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: lstSucc.c,v 1.14 2020/07/03 08:37:57 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)lstSucc.c 8.1 (Berkeley) 6/6/93";
-#else
-__RCSID("$NetBSD: lstSucc.c,v 1.14 2020/07/03 08:37:57 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
-/*-
- * LstSucc.c --
- * return the successor to a given node
- */
-
-#include "lstInt.h"
-
-/*-
- *-----------------------------------------------------------------------
- * Lst_Succ --
- * Return the successor to the given node on its list.
- *
- * Results:
- * The successor of the node, if it exists (note that on a circular
- * list, if the node is the only one in the list, it is its own
- * successor).
- *
- * Side Effects:
- * None.
- *
- *-----------------------------------------------------------------------
- */
-LstNode
-Lst_Succ(LstNode ln)
-{
- if (ln == NULL) {
- return NULL;
- } else {
- return ln->nextPtr;
- }
-}
diff --git a/contrib/bmake/main.c b/contrib/bmake/main.c
index 25141625e55c..f27320c57451 100644
--- a/contrib/bmake/main.c
+++ b/contrib/bmake/main.c
@@ -1,4 +1,4 @@
-/* $NetBSD: main.c,v 1.279 2020/07/03 08:13:23 rillig Exp $ */
+/* $NetBSD: main.c,v 1.331 2020/08/30 19:56:02 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -69,7 +69,7 @@
*/
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: main.c,v 1.279 2020/07/03 08:13:23 rillig Exp $";
+static char rcsid[] = "$NetBSD: main.c,v 1.331 2020/08/30 19:56:02 rillig Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
@@ -81,7 +81,7 @@ __COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993\
#if 0
static char sccsid[] = "@(#)main.c 8.3 (Berkeley) 3/19/94";
#else
-__RCSID("$NetBSD: main.c,v 1.279 2020/07/03 08:13:23 rillig Exp $");
+__RCSID("$NetBSD: main.c,v 1.331 2020/08/30 19:56:02 rillig Exp $");
#endif
#endif /* not lint */
#endif
@@ -124,13 +124,13 @@ __RCSID("$NetBSD: main.c,v 1.279 2020/07/03 08:13:23 rillig Exp $");
#include <sys/utsname.h>
#include "wait.h"
+#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
-#include <ctype.h>
#include "make.h"
#include "hash.h"
@@ -162,7 +162,8 @@ static Lst makefiles; /* ordered list of makefiles to read */
static int printVars; /* -[vV] argument */
#define COMPAT_VARS 1
#define EXPAND_VARS 2
-static Lst variables; /* list of variables to print */
+static Lst variables; /* list of variables to print
+ * (for -v and -V) */
int maxJobs; /* -j argument */
static int maxJobTokens; /* -j argument */
Boolean compatMake; /* -B argument */
@@ -180,14 +181,13 @@ 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;
static void MainParseArgs(int, char **);
-static int ReadMakefile(const void *, const void *);
+static int ReadMakefile(const char *);
static void usage(void) MAKE_ATTR_DEAD;
static void purge_cached_realpaths(void);
@@ -257,7 +257,7 @@ parse_debug_options(const char *argvalue)
for (modules = argvalue; *modules; ++modules) {
switch (*modules) {
case 'A':
- debug = ~0;
+ debug = ~(0|DEBUG_LINT);
break;
case 'a':
debug |= DEBUG_ARCH;
@@ -291,9 +291,15 @@ parse_debug_options(const char *argvalue)
++modules;
}
break;
+ case 'h':
+ debug |= DEBUG_HASH;
+ break;
case 'j':
debug |= DEBUG_JOB;
break;
+ case 'L':
+ debug |= DEBUG_LINT;
+ break;
case 'l':
debug |= DEBUG_LOUD;
break;
@@ -375,7 +381,7 @@ debug_setbuf:
/*
* does path contain any relative components
*/
-static int
+static Boolean
is_relpath(const char *path)
{
const char *cp;
@@ -383,10 +389,7 @@ is_relpath(const char *path)
if (path[0] != '/')
return TRUE;
cp = path;
- do {
- cp = strstr(cp, "/.");
- if (!cp)
- break;
+ while ((cp = strstr(cp, "/.")) != NULL) {
cp += 2;
if (cp[0] == '/' || cp[0] == '\0')
return TRUE;
@@ -394,7 +397,7 @@ is_relpath(const char *path)
if (cp[1] == '/' || cp[1] == '\0')
return TRUE;
}
- } while (cp);
+ }
return FALSE;
}
@@ -416,7 +419,7 @@ static void
MainParseArgs(int argc, char **argv)
{
char *p;
- int c = '?';
+ char c = '?';
int arginc;
char *argvalue;
const char *getopt_def;
@@ -531,7 +534,6 @@ rearg:
} else {
Var_Append(MAKEFLAGS, "-J", VAR_GLOBAL);
Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL);
- jobServer = TRUE;
}
break;
case 'N':
@@ -553,7 +555,7 @@ rearg:
case 'v':
if (argvalue == NULL) goto noarg;
printVars = c == 'v' ? EXPAND_VARS : COMPAT_VARS;
- (void)Lst_AtEnd(variables, argvalue);
+ Lst_Append(variables, argvalue);
Var_Append(MAKEFLAGS, "-V", VAR_GLOBAL);
Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL);
break;
@@ -581,7 +583,7 @@ rearg:
break;
case 'f':
if (argvalue == NULL) goto noarg;
- (void)Lst_AtEnd(makefiles, argvalue);
+ Lst_Append(makefiles, argvalue);
break;
case 'i':
ignoreErrors = TRUE;
@@ -674,7 +676,7 @@ rearg:
Punt("illegal (null) argument.");
if (*argv[1] == '-' && !dashDash)
goto rearg;
- (void)Lst_AtEnd(create, bmake_strdup(argv[1]));
+ Lst_Append(create, bmake_strdup(argv[1]));
}
return;
@@ -705,12 +707,10 @@ noarg:
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;
+ Words words;
+ char *p1;
+ const char *argv0 = Var_Value(".MAKE", VAR_GLOBAL, &p1);
+ char *buf;
if (line == NULL)
return;
@@ -733,21 +733,19 @@ Main_ParseArgLine(const char *line)
return;
}
#endif
- buf = bmake_malloc(len = strlen(line) + strlen(argv0) + 2);
- (void)snprintf(buf, len, "%s %s", argv0, line);
+ buf = str_concat3(argv0, " ", line);
free(p1);
- argv = brk_string(buf, &argc, TRUE, &args);
- if (argv == NULL) {
+ words = Str_Words(buf, TRUE);
+ if (words.words == NULL) {
Error("Unterminated quoted string [%s]", buf);
free(buf);
return;
}
free(buf);
- MainParseArgs(argc, argv);
+ MainParseArgs((int)words.len, words.words);
- free(args);
- free(argv);
+ Words_Free(words);
}
Boolean
@@ -775,7 +773,7 @@ Main_SetObjdir(const char *fmt, ...)
(void)fprintf(stderr, "make warning: %s: %s.\n",
path, strerror(errno));
} else {
- strncpy(objdir, path, MAXPATHLEN);
+ snprintf(objdir, sizeof objdir, "%s", path);
Var_Set(".OBJDIR", objdir, VAR_GLOBAL);
setenv("PWD", objdir, 1);
Dir_InitDot();
@@ -792,37 +790,44 @@ Main_SetObjdir(const char *fmt, ...)
static Boolean
Main_SetVarObjdir(const char *var, const char *suffix)
{
- char *p, *path, *xpath;
+ char *path_freeIt;
+ const char *path = Var_Value(var, VAR_CMD, &path_freeIt);
+ const char *xpath;
+ char *xpath_freeIt;
- if ((path = Var_Value(var, VAR_CMD, &p)) == NULL ||
- *path == '\0')
+ if (path == NULL || path[0] == '\0') {
+ bmake_free(path_freeIt);
return FALSE;
+ }
/* expand variable substitutions */
+ xpath = path;
+ xpath_freeIt = NULL;
if (strchr(path, '$') != 0)
- xpath = Var_Subst(NULL, path, VAR_GLOBAL, VARF_WANTRES);
- else
- xpath = path;
+ xpath = xpath_freeIt = Var_Subst(path, VAR_GLOBAL,
+ VARE_WANTRES);
(void)Main_SetObjdir("%s%s", xpath, suffix);
- if (xpath != path)
- free(xpath);
- free(p);
+ bmake_free(xpath_freeIt);
+ bmake_free(path_freeIt);
return TRUE;
}
-/*-
- * ReadAllMakefiles --
- * wrapper around ReadMakefile() to read all.
- *
- * Results:
- * TRUE if ok, FALSE on error
- */
-static int
-ReadAllMakefiles(const void *p, const void *q)
+/* Read and parse the makefile.
+ * Return TRUE if reading the makefile succeeded, for Lst_Find. */
+static Boolean
+ReadMakefileSucceeded(const void *fname, const void *unused)
+{
+ return ReadMakefile(fname) == 0;
+}
+
+/* Read and parse the makefile.
+ * Return TRUE if reading the makefile failed, for Lst_Find. */
+static Boolean
+ReadMakefileFailed(const void *fname, const void *unused)
{
- return ReadMakefile(p, q) == 0;
+ return ReadMakefile(fname) != 0;
}
int
@@ -835,7 +840,7 @@ str2Lst_Append(Lst lp, char *str, const char *sep)
sep = " \t";
for (n = 0, cp = strtok(str, sep); cp; cp = strtok(NULL, sep)) {
- (void)Lst_AtEnd(lp, cp);
+ Lst_Append(lp, cp);
n++;
}
return n;
@@ -863,13 +868,13 @@ siginfo(int signo MAKE_ATTR_UNUSED)
void
MakeMode(const char *mode)
{
- char *mp = NULL;
+ char *mode_freeIt = NULL;
- if (!mode)
- mode = mp = Var_Subst(NULL, "${" MAKE_MODE ":tl}",
- VAR_GLOBAL, VARF_WANTRES);
+ if (mode == NULL)
+ mode = mode_freeIt = Var_Subst("${" MAKE_MODE ":tl}",
+ VAR_GLOBAL, VARE_WANTRES);
- if (mode && *mode) {
+ if (mode[0] != '\0') {
if (strstr(mode, "compat")) {
compatMake = TRUE;
forceJobs = FALSE;
@@ -880,7 +885,7 @@ MakeMode(const char *mode)
#endif
}
- free(mp);
+ free(mode_freeIt);
}
static void
@@ -896,15 +901,13 @@ doPrintVars(void)
else
expandVars = getBoolean(".MAKE.EXPAND_VARIABLES", FALSE);
- for (ln = Lst_First(variables); ln != NULL;
- ln = Lst_Succ(ln)) {
- char *var = (char *)Lst_Datum(ln);
- char *value;
+ for (ln = Lst_First(variables); ln != NULL; ln = LstNode_Next(ln)) {
+ char *var = LstNode_Datum(ln);
+ const char *value;
char *p1;
if (strchr(var, '$')) {
- value = p1 = Var_Subst(NULL, var, VAR_GLOBAL,
- VARF_WANTRES);
+ value = p1 = Var_Subst(var, VAR_GLOBAL, VARE_WANTRES);
} else if (expandVars) {
char tmp[128];
int len = snprintf(tmp, sizeof(tmp), "${%s}", var);
@@ -912,13 +915,12 @@ doPrintVars(void)
if (len >= (int)sizeof(tmp))
Fatal("%s: variable name too big: %s",
progname, var);
- value = p1 = Var_Subst(NULL, tmp, VAR_GLOBAL,
- VARF_WANTRES);
+ value = p1 = Var_Subst(tmp, VAR_GLOBAL, VARE_WANTRES);
} else {
value = Var_Value(var, VAR_GLOBAL, &p1);
}
printf("%s\n", value ? value : "");
- free(p1);
+ bmake_free(p1);
}
}
@@ -962,7 +964,7 @@ runTargets(void)
Compat_Run(targs);
outOfDate = FALSE;
}
- Lst_Destroy(targs, NULL);
+ Lst_Free(targs);
return outOfDate;
}
@@ -1079,7 +1081,7 @@ main(int argc, char **argv)
#else
#ifndef MACHINE_ARCH
#ifdef MAKE_MACHINE_ARCH
- machine_arch = MAKE_MACHINE_ARCH;
+ machine_arch = MAKE_MACHINE_ARCH;
#else
machine_arch = "unknown";
#endif
@@ -1113,11 +1115,11 @@ main(int argc, char **argv)
VAR_GLOBAL);
Var_Set(MAKE_DEPENDFILE, ".depend", VAR_GLOBAL);
- create = Lst_Init(FALSE);
- makefiles = Lst_Init(FALSE);
+ create = Lst_Init();
+ makefiles = Lst_Init();
printVars = 0;
debugVflag = FALSE;
- variables = Lst_Init(FALSE);
+ variables = Lst_Init();
beSilent = FALSE; /* Print commands as executed */
ignoreErrors = FALSE; /* Pay attention to non-zero returns */
noExecute = FALSE; /* Execute all commands */
@@ -1199,7 +1201,7 @@ main(int argc, char **argv)
#ifdef USE_META
meta_init();
#endif
- Dir_Init(NULL); /* Dir_* safe to call from MainParseArgs */
+ Dir_Init();
/*
* First snag any flags out of the MAKE environment variable.
@@ -1265,8 +1267,8 @@ main(int argc, char **argv)
(void)strncpy(curdir, pwd, MAXPATHLEN);
}
}
- free(ptmp1);
- free(ptmp2);
+ bmake_free(ptmp1);
+ bmake_free(ptmp2);
}
#endif
Var_Set(".CURDIR", curdir, VAR_GLOBAL);
@@ -1280,7 +1282,7 @@ main(int argc, char **argv)
* and * finally _PATH_OBJDIRPREFIX`pwd`, in that order. If none
* of these paths exist, just use .CURDIR.
*/
- Dir_Init(curdir);
+ Dir_InitDir(curdir);
(void)Main_SetObjdir("%s", curdir);
if (!Main_SetVarObjdir("MAKEOBJDIRPREFIX", curdir) &&
@@ -1312,10 +1314,8 @@ main(int argc, char **argv)
if (!Lst_IsEmpty(create)) {
LstNode ln;
- for (ln = Lst_First(create); ln != NULL;
- ln = Lst_Succ(ln)) {
- char *name = (char *)Lst_Datum(ln);
-
+ for (ln = Lst_First(create); ln != NULL; ln = LstNode_Next(ln)) {
+ char *name = LstNode_Datum(ln);
Var_Append(".TARGETS", name, VAR_GLOBAL);
}
} else
@@ -1327,7 +1327,8 @@ main(int argc, char **argv)
* 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')
+ /* XXX: mismatch: the -m option sets sysIncPath, not syspath */
+ if (syspath == NULL || syspath[0] == '\0')
syspath = defsyspath;
else
syspath = bmake_strdup(syspath);
@@ -1353,48 +1354,46 @@ main(int argc, char **argv)
/*
* 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.
+ * makefiles, or the default makefile and Makefile, in that order,
+ * if no makefiles were given on the command line.
*/
if (!noBuiltins) {
LstNode ln;
- sysMkPath = Lst_Init(FALSE);
+ sysMkPath = Lst_Init();
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);
+ ln = Lst_Find(sysMkPath, ReadMakefileSucceeded, NULL);
if (ln == NULL)
Fatal("%s: cannot open %s.", progname,
- (char *)Lst_Datum(ln));
+ (char *)LstNode_Datum(Lst_First(sysMkPath)));
}
if (!Lst_IsEmpty(makefiles)) {
LstNode ln;
- ln = Lst_Find(makefiles, NULL, ReadAllMakefiles);
+ ln = Lst_Find(makefiles, ReadMakefileFailed, NULL);
if (ln != NULL)
Fatal("%s: cannot open %s.", progname,
- (char *)Lst_Datum(ln));
+ (char *)LstNode_Datum(ln));
} else {
- p1 = Var_Subst(NULL, "${" MAKEFILE_PREFERENCE "}",
- VAR_CMD, VARF_WANTRES);
- if (p1) {
+ p1 = Var_Subst("${" MAKEFILE_PREFERENCE "}",
+ VAR_CMD, VARE_WANTRES);
(void)str2Lst_Append(makefiles, p1, NULL);
- (void)Lst_Find(makefiles, NULL, ReadMakefile);
+ (void)Lst_Find(makefiles, ReadMakefileSucceeded, NULL);
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, VARF_WANTRES);
+ makeDependfile = Var_Subst("${.MAKE.DEPENDFILE:T}",
+ VAR_CMD, VARE_WANTRES);
doing_depend = TRUE;
- (void)ReadMakefile(makeDependfile, NULL);
+ (void)ReadMakefile(makeDependfile);
doing_depend = FALSE;
}
@@ -1404,14 +1403,14 @@ main(int argc, char **argv)
MakeMode(NULL);
Var_Append("MFLAGS", Var_Value(MAKEFLAGS, VAR_GLOBAL, &p1), VAR_GLOBAL);
- free(p1);
+ bmake_free(p1);
if (!forceJobs && !compatMake &&
Var_Exists(".MAKE.JOBS", VAR_GLOBAL)) {
char *value;
int n;
- value = Var_Subst(NULL, "${.MAKE.JOBS}", VAR_GLOBAL, VARF_WANTRES);
+ value = Var_Subst("${.MAKE.JOBS}", VAR_GLOBAL, VARE_WANTRES);
n = strtol(value, NULL, 0);
if (n < 1) {
(void)fprintf(stderr, "%s: illegal value for .MAKE.JOBS -- must be positive integer!\n",
@@ -1439,8 +1438,9 @@ main(int argc, char **argv)
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);
+ fprintf(debug_file,
+ "job_pipe %d %d, maxjobs %d, tokens %d, compat %d\n",
+ jp_0, jp_1, maxJobs, maxJobTokens, compatMake ? 1 : 0);
if (!printVars)
Main_ExportMAKEFLAGS(TRUE); /* initial export */
@@ -1461,7 +1461,7 @@ main(int argc, char **argv)
*/
static char VPATH[] = "${VPATH}";
- vpath = Var_Subst(NULL, VPATH, VAR_CMD, VARF_WANTRES);
+ vpath = Var_Subst(VPATH, VAR_CMD, VARE_WANTRES);
path = vpath;
do {
/* skip to end of directory */
@@ -1502,9 +1502,9 @@ main(int argc, char **argv)
}
#ifdef CLEANUP
- Lst_Destroy(variables, NULL);
- Lst_Destroy(makefiles, NULL);
- Lst_Destroy(create, (FreeProc *)free);
+ Lst_Free(variables);
+ Lst_Free(makefiles);
+ Lst_Destroy(create, free);
#endif
/* print the graph now it's been processed if the user requested it */
@@ -1522,7 +1522,7 @@ main(int argc, char **argv)
meta_finish();
#endif
Suff_End();
- Targ_End();
+ Targ_End();
Arch_End();
Var_End();
Parse_End();
@@ -1533,23 +1533,16 @@ main(int argc, char **argv)
return outOfDate ? 1 : 0;
}
-/*-
- * ReadMakefile --
- * Open and parse the given makefile.
+/* Open and parse the given makefile, with all its side effects.
*
* Results:
* 0 if ok. -1 if couldn't open file.
- *
- * Side Effects:
- * lots
*/
static int
-ReadMakefile(const void *p, const void *q MAKE_ATTR_UNUSED)
+ReadMakefile(const char *fname)
{
- const char *fname = p; /* makefile to read */
int fd;
- size_t len = MAXPATHLEN;
- char *name, *path = bmake_malloc(len);
+ char *name, *path = NULL;
if (!strcmp(fname, "-")) {
Parse_File(NULL /*stdin*/, -1);
@@ -1557,22 +1550,16 @@ ReadMakefile(const void *p, const void *q MAKE_ATTR_UNUSED)
} 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);
+ path = str_concat3(curdir, "/", fname);
fd = open(path, O_RDONLY);
if (fd != -1) {
fname = path;
goto found;
}
+ free(path);
/* 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);
+ path = str_concat3(objdir, "/", fname);
fd = open(path, O_RDONLY);
if (fd != -1) {
fname = path;
@@ -1613,31 +1600,32 @@ found:
/*-
* Cmd_Exec --
* Execute the command in cmd, and return the output of that command
- * in a string.
+ * in a string. In the output, newlines are replaced with spaces.
*
* 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
+ * A string containing the output of the command, or the empty string.
+ * *errfmt returns a format string describing the command failure,
+ * if any, using a single %s conversion specification.
*
* Side Effects:
* The string must be freed by the caller.
*/
char *
-Cmd_Exec(const char *cmd, const char **errnum)
+Cmd_Exec(const char *cmd, const char **errfmt)
{
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 */
+ ssize_t bytes_read;
+ char *res; /* result */
+ size_t res_len;
char *cp;
- int cc; /* bytes read, or -1 */
int savederr; /* saved errno */
-
- *errnum = NULL;
+ *errfmt = NULL;
if (!shellName)
Shell_Init();
@@ -1653,7 +1641,7 @@ Cmd_Exec(const char *cmd, const char **errnum)
* Open a pipe for fetching its output
*/
if (pipe(fds) == -1) {
- *errnum = "Couldn't create pipe for \"%s\"";
+ *errfmt = "Couldn't create pipe for \"%s\"";
goto bad;
}
@@ -1682,7 +1670,7 @@ Cmd_Exec(const char *cmd, const char **errnum)
/*NOTREACHED*/
case -1:
- *errnum = "Couldn't exec \"%s\"";
+ *errfmt = "Couldn't exec \"%s\"";
goto bad;
default:
@@ -1696,12 +1684,12 @@ Cmd_Exec(const char *cmd, const char **errnum)
do {
char result[BUFSIZ];
- cc = read(fds[0], result, sizeof(result));
- if (cc > 0)
- Buf_AddBytes(&buf, cc, result);
+ bytes_read = read(fds[0], result, sizeof(result));
+ if (bytes_read > 0)
+ Buf_AddBytes(&buf, result, (size_t)bytes_read);
}
- while (cc > 0 || (cc == -1 && errno == EINTR));
- if (cc == -1)
+ while (bytes_read > 0 || (bytes_read == -1 && errno == EINTR));
+ if (bytes_read == -1)
savederr = errno;
/*
@@ -1716,43 +1704,28 @@ Cmd_Exec(const char *cmd, const char **errnum)
JobReapChild(pid, status, FALSE);
continue;
}
- cc = Buf_Size(&buf);
+ res_len = Buf_Size(&buf);
res = Buf_Destroy(&buf, FALSE);
if (savederr != 0)
- *errnum = "Couldn't read shell's output for \"%s\"";
+ *errfmt = "Couldn't read shell's output for \"%s\"";
if (WIFSIGNALED(status))
- *errnum = "\"%s\" exited on a signal";
+ *errfmt = "\"%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];
+ *errfmt = "\"%s\" returned non-zero status";
- if (cc > 0 && *--cp == '\n') {
- /*
- * A final newline is just stripped
- */
- *cp-- = '\0';
- }
- while (cp >= res) {
- if (*cp == '\n') {
+ /* Convert newlines to spaces. A final newline is just stripped */
+ if (res_len > 0 && res[res_len - 1] == '\n')
+ res[res_len - 1] = '\0';
+ for (cp = res; *cp != '\0'; cp++)
+ if (*cp == '\n')
*cp = ' ';
- }
- cp--;
- }
break;
}
return res;
bad:
- res = bmake_malloc(1);
- *res = '\0';
- return res;
+ return bmake_strdup("");
}
/*-
@@ -1888,7 +1861,7 @@ DieHorribly(void)
*/
void
Finish(int errors)
- /* number of errors encountered in Make_Make */
+ /* number of errors encountered in Make_Make */
{
if (dieQuietly(NULL, -1))
exit(2);
@@ -1956,13 +1929,13 @@ usage(void)
{
char *p;
if ((p = strchr(progname, '[')) != NULL)
- *p = '\0';
+ *p = '\0';
(void)fprintf(stderr,
-"usage: %s [-BeikNnqrstWwX] \n\
- [-C directory] [-D variable] [-d flags] [-f makefile]\n\
- [-I directory] [-J private] [-j max_jobs] [-m directory] [-T file]\n\
- [-V variable] [-v variable] [variable=value] [target ...]\n",
+"usage: %s [-BeikNnqrstWwX] \n"
+" [-C directory] [-D variable] [-d flags] [-f makefile]\n"
+" [-I directory] [-J private] [-j max_jobs] [-m directory] [-T file]\n"
+" [-V variable] [-v variable] [variable=value] [target ...]\n",
progname);
exit(2);
}
@@ -2010,7 +1983,8 @@ char *
cached_realpath(const char *pathname, char *resolved)
{
GNode *cache;
- char *rp, *cp;
+ const char *rp;
+ char *cp;
if (!pathname || !pathname[0])
return NULL;
@@ -2024,7 +1998,7 @@ cached_realpath(const char *pathname, char *resolved)
Var_Set(pathname, rp, cache);
} /* else should we negative-cache? */
- free(cp);
+ bmake_free(cp);
return rp ? resolved : NULL;
}
@@ -2070,9 +2044,14 @@ void
PrintOnError(GNode *gn, const char *s)
{
static GNode *en = NULL;
- char tmp[64];
+ const char *expr;
char *cp;
+ if (DEBUG(HASH)) {
+ Targ_Stats();
+ Var_Stats();
+ }
+
/* we generally want to keep quiet if a sub-make died */
if (dieQuietly(gn, -1))
return;
@@ -2092,14 +2071,10 @@ PrintOnError(GNode *gn, const char *s)
Var_Delete(".ERROR_CMD", VAR_GLOBAL);
Lst_ForEach(gn->commands, addErrorCMD, gn);
}
- strncpy(tmp, "${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'\n@}",
- sizeof(tmp) - 1);
- cp = Var_Subst(NULL, tmp, VAR_GLOBAL, VARF_WANTRES);
- if (cp) {
- if (*cp)
- printf("%s", cp);
- free(cp);
- }
+ expr = "${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'\n@}";
+ cp = Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES);
+ printf("%s", cp);
+ free(cp);
fflush(stdout);
/*
@@ -2115,18 +2090,17 @@ PrintOnError(GNode *gn, const char *s)
void
Main_ExportMAKEFLAGS(Boolean first)
{
- static int once = 1;
- char tmp[64];
+ static Boolean once = TRUE;
+ const char *expr;
char *s;
if (once != first)
return;
- once = 0;
+ once = FALSE;
- strncpy(tmp, "${.MAKEFLAGS} ${.MAKEOVERRIDES:O:u:@v@$v=${$v:Q}@}",
- sizeof(tmp));
- s = Var_Subst(NULL, tmp, VAR_CMD, VARF_WANTRES);
- if (s && *s) {
+ expr = "${.MAKEFLAGS} ${.MAKEOVERRIDES:O:u:@v@$v=${$v:Q}@}";
+ s = Var_Subst(expr, VAR_CMD, VARE_WANTRES);
+ if (s[0] != '\0') {
#ifdef POSIX
setenv("MAKEFLAGS", s, 1);
#else
@@ -2147,8 +2121,8 @@ getTmpdir(void)
* Honor $TMPDIR but only if it is valid.
* Ensure it ends with /.
*/
- tmpdir = Var_Subst(NULL, "${TMPDIR:tA:U" _PATH_TMP "}/", VAR_GLOBAL,
- VARF_WANTRES);
+ tmpdir = Var_Subst("${TMPDIR:tA:U" _PATH_TMP "}/", VAR_GLOBAL,
+ VARE_WANTRES);
if (stat(tmpdir, &st) < 0 || !S_ISDIR(st.st_mode)) {
free(tmpdir);
tmpdir = bmake_strdup(_PATH_TMP);
@@ -2235,18 +2209,12 @@ s2Boolean(const char *s, Boolean bf)
* is FALSE, otherwise TRUE.
*/
Boolean
-getBoolean(const char *name, Boolean bf)
+getBoolean(const char *name, Boolean fallback)
{
- char tmp[64];
- char *cp;
-
- if (snprintf(tmp, sizeof(tmp), "${%s:U:tl}", name) < (int)(sizeof(tmp))) {
- cp = Var_Subst(NULL, tmp, VAR_GLOBAL, VARF_WANTRES);
-
- if (cp) {
- bf = s2Boolean(cp, bf);
- free(cp);
- }
- }
- return bf;
+ char *expr = str_concat3("${", name, ":U:tl}");
+ char *value = Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES);
+ Boolean res = s2Boolean(value, fallback);
+ free(value);
+ free(expr);
+ return res;
}
diff --git a/contrib/bmake/make-bootstrap.sh.in b/contrib/bmake/make-bootstrap.sh.in
index ec100138b6d1..1bf84bbc3dec 100755
--- a/contrib/bmake/make-bootstrap.sh.in
+++ b/contrib/bmake/make-bootstrap.sh.in
@@ -58,17 +58,10 @@ do_link() {
${CC} ${LDSTATIC} ${LDFLAGS} -o "$output" "$@" ${LIBS}
}
-BASE_OBJECTS="arch.o buf.o compat.o cond.o dir.o for.o getopt hash.o \
-make.o make_malloc.o metachar.o parse.o sigcompat.o str.o strlist.o \
+BASE_OBJECTS="arch.o buf.o compat.o cond.o dir.o enum.o for.o getopt hash.o \
+lst.o make.o make_malloc.o metachar.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}
diff --git a/contrib/bmake/make-conf.h b/contrib/bmake/make-conf.h
index a85b86d3efb5..5b13e295ae0c 100644
--- a/contrib/bmake/make-conf.h
+++ b/contrib/bmake/make-conf.h
@@ -1,4 +1,4 @@
-/* $NetBSD: config.h,v 1.21 2012/03/31 00:12:24 christos Exp $ */
+/* $NetBSD: config.h,v 1.22 2020/09/01 17:40:34 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -100,7 +100,10 @@
* LIBSUFF
* Is the suffix used to denote libraries and is used by the Suff module
* to find the search path on which to seek any -l<xx> targets.
- *
+ */
+#define LIBSUFF ".a"
+
+/*
* RECHECK
* If defined, Make_Update will check a target for its current
* modification time after it has been re-made, setting it to the
@@ -108,10 +111,9 @@
* Unfortunately, under NFS the modification time often doesn't
* get updated in time, so a target will appear to not have been
* re-made, causing later targets to appear up-to-date. On systems
- * that don't have this problem, you should defined this. Under
+ * that don't have this problem, you should define this. Under
* NFS you probably should not, unless you aren't exporting jobs.
*/
-#define LIBSUFF ".a"
#define RECHECK
/*
diff --git a/contrib/bmake/make.1 b/contrib/bmake/make.1
index 1cc20ba97377..d3a3d539d5c1 100644
--- a/contrib/bmake/make.1
+++ b/contrib/bmake/make.1
@@ -1,4 +1,4 @@
-.\" $NetBSD: make.1,v 1.282 2020/06/06 20:28:42 wiz Exp $
+.\" $NetBSD: make.1,v 1.289 2020/08/28 17:15:04 rillig Exp $
.\"
.\" Copyright (c) 1990, 1993
.\" The Regents of the University of California. All rights reserved.
@@ -29,7 +29,7 @@
.\"
.\" from: @(#)make.1 8.4 (Berkeley) 3/19/94
.\"
-.Dd June 5, 2020
+.Dd August 28, 2020
.Dt MAKE 1
.Os
.Sh NAME
@@ -166,8 +166,15 @@ 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 h
+Print debugging information about hash table operations.
.It Ar j
Print debugging information about running multiple shells.
+.It Ar L
+Turn on lint checks.
+This will throw errors for variable assignments that do not parse
+correctly, at the time of assignment so the file and line number
+are available.
.It Ar l
Print commands in Makefiles regardless of whether or not they are prefixed by
.Ql @
@@ -311,7 +318,8 @@ 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).
+source (see below) or the command is prefixed with
+.Ql Ic + .
.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
@@ -412,37 +420,44 @@ 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:
+and are customarily created from them.
+A target is considered out-of-date if it does not exist, or if its
+modification time is less than that of any of its sources.
+An out-of-date target will be re-created, but not until all sources
+have been examined and themselves re-created as needed.
+Three operators may be used:
.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
+Many dependency lines may name this target but only one may have
+attached shell commands.
+All sources named in all dependency lines are considered together,
+and if needed the attached shell commands are run to create or
+re-create the target.
+If
.Nm
-is interrupted.
+is interrupted, the target is removed.
.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.
+The same, but the target is always re-created whether or not it is out
+of date.
.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
+Any dependency line may have attached shell commands, but each one
+is handled independently: its sources are considered and the attached
+shell commands are run if the target is out of date with respect to
+(only) those sources.
+Thus, different groups of the attached shell commands may be run
+depending on the circumstances.
+Furthermore, unlike
+.Ic \&:,
+for dependency lines with no sources, the attached shell
+commands are always run.
+Also unlike
+.Ic \&:,
+the target will not be removed if
.Nm
is interrupted.
.El
+All dependency lines mentioning a particular target must use the same
+operator.
.Pp
Targets and sources may contain the shell wildcard values
.Ql \&? ,
@@ -608,7 +623,7 @@ 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!
+braces, parentheses, or whitespace are really best avoided!
.Pp
If the result of expanding a variable contains a dollar sign
.Pq Ql \&$
@@ -1137,6 +1152,9 @@ is set to the value of
for all programs which
.Nm
executes.
+.It Ev .SHELL
+The pathname of the shell used to run target scripts.
+It is read-only.
.It Ev .TARGETS
The list of targets explicitly specified on the command line, if any.
.It Ev VPATH
@@ -1182,7 +1200,7 @@ 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
+Selects only those words that match
.Ar pattern .
The standard shell wildcard characters
.Pf ( Ql * ,
@@ -1206,11 +1224,11 @@ This is identical to
but selects all words which do not match
.Ar pattern .
.It Cm \&:O
-Order every word in variable alphabetically.
+Orders every word in variable alphabetically.
.It Cm \&:Or
-Order every word in variable in reverse alphabetical order.
+Orders every word in variable in reverse alphabetical order.
.It Cm \&:Ox
-Randomize words in variable.
+Shuffles the 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 \&:=
@@ -1260,7 +1278,7 @@ If a
.Va utc
value is not provided or is 0, the current time is used.
.It Cm \&:hash
-Compute a 32-bit hash of the value and encode it as hex digits.
+Computes a 32-bit hash of the value and encode it as hex digits.
.It Cm \&:localtime[=utc]
The value is a format string for
.Xr strftime 3 ,
@@ -1270,7 +1288,7 @@ If a
.Va utc
value is not provided or is 0, the current time is used.
.It Cm \&:tA
-Attempt to convert variable to an absolute path using
+Attempts to convert variable to an absolute path using
.Xr realpath 3 ,
if that fails, the value is unchanged.
.It Cm \&:tl
@@ -1282,7 +1300,7 @@ This modifier sets the separator to the character
If
.Ar c
is omitted, then no separator is used.
-The common escapes (including octal numeric codes), work as expected.
+The common escapes (including octal numeric codes) work as expected.
.It Cm \&:tu
Converts variable to upper-case letters.
.It Cm \&:tW
@@ -1298,21 +1316,21 @@ See also
.Sm off
.It Cm \&:S No \&/ Ar old_string No \&/ Ar new_string No \&/ Op Cm 1gW
.Sm on
-Modify the first occurrence of
+Modifies the first occurrence of
.Ar old_string
-in the variable's value, replacing it with
+in each word of 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
+is appended to the last delimiter 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 appended to the last delimiter of the pattern, only the first occurrence
is affected.
If a
.Ql W
-is appended to the last slash of the pattern,
+is appended to the last delimiter of the pattern,
then the value is treated as a single word
(possibly containing embedded white space).
If
@@ -1381,13 +1399,6 @@ 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.
.Pp
As for the
.Cm \&:S
@@ -1398,9 +1409,9 @@ and
are subjected to variable expansion before being parsed as
regular expressions.
.It Cm \&:T
-Replaces each word in the variable with its last component.
+Replaces each word in the variable with its last path component.
.It Cm \&:u
-Remove adjacent duplicate words (like
+Removes adjacent duplicate words (like
.Xr uniq 1 ) .
.Sm off
.It Cm \&:\&? Ar true_string Cm \&: Ar false_string
@@ -1416,7 +1427,7 @@ 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:
+to determine if any words match "42" you need to use something like:
.Dl ${"${NUMBERS:M42}" != \&"\&":?match:no} .
.It Ar :old_string=new_string
This is the
@@ -1460,7 +1471,7 @@ in either the
or
.Ar old_string ,
only the first instance is treated specially (as the pattern character);
-all subsequent instances are treated as regular characters
+all subsequent instances are treated as regular characters.
.Pp
Variable expansion occurs in the normal fashion inside both
.Ar old_string
@@ -1477,11 +1488,10 @@ 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
+loops, expansion occurs at the time of reference.
+Assigns
.Ar temp
-to each word in the variable and evaluate
+to each word in the variable and evaluates
.Ar string .
The ODE convention is that
.Ar temp
@@ -1492,7 +1502,7 @@ For example.
However a single character variable is often more readable:
.Dl ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@}
.It Cm \&:_[=var]
-Save the current variable value in
+Saves the current variable value in
.Ql $_
or the named
.Va var
@@ -1513,7 +1523,7 @@ is used to save the result of the
modifier which is later referenced using the index values from
.Ql :range .
.It Cm \&:U Ns Ar newval
-If the variable is undefined
+If the variable is undefined,
.Ar newval
is the value.
If the variable is defined, the existing value is returned.
@@ -1523,7 +1533,7 @@ It is handy for setting per-target CFLAGS for instance:
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
+If the variable is defined,
.Ar newval
is the value.
.It Cm \&:L
@@ -1652,7 +1662,7 @@ 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
+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
@@ -1698,7 +1708,7 @@ The possible conditionals are as follows:
The message is printed along with the name of the makefile and line number,
then
.Nm
-will exit.
+will exit immediately.
.It Ic .export Ar variable ...
Export the specified global variable.
If no variable list is provided, all globals are exported
@@ -1887,7 +1897,7 @@ 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
+variable is being compared against 0, or an empty string in the case
of a string comparison.
.Pp
When
@@ -1928,7 +1938,7 @@ 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 Aq make-lines
.It Ic \&.endfor
.El
.Pp
@@ -1940,7 +1950,7 @@ On each iteration of the loop, one word is taken and assigned to each
in order, and these
.Ic variables
are substituted into the
-.Ic make-rules
+.Ic make-lines
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
diff --git a/contrib/bmake/make.c b/contrib/bmake/make.c
index 9472a045c26a..da2b8adf6efd 100644
--- a/contrib/bmake/make.c
+++ b/contrib/bmake/make.c
@@ -1,4 +1,4 @@
-/* $NetBSD: make.c,v 1.99 2020/07/03 08:13:23 rillig Exp $ */
+/* $NetBSD: make.c,v 1.133 2020/08/30 14:11:42 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -69,14 +69,14 @@
*/
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: make.c,v 1.99 2020/07/03 08:13:23 rillig Exp $";
+static char rcsid[] = "$NetBSD: make.c,v 1.133 2020/08/30 14:11:42 rillig Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)make.c 8.1 (Berkeley) 6/6/93";
#else
-__RCSID("$NetBSD: make.c,v 1.99 2020/07/03 08:13:23 rillig Exp $");
+__RCSID("$NetBSD: make.c,v 1.133 2020/08/30 14:11:42 rillig Exp $");
#endif
#endif /* not lint */
#endif
@@ -116,7 +116,7 @@ __RCSID("$NetBSD: make.c,v 1.99 2020/07/03 08:13:23 rillig Exp $");
*/
#include "make.h"
-#include "hash.h"
+#include "enum.h"
#include "dir.h"
#include "job.h"
@@ -151,6 +151,43 @@ make_abort(GNode *gn, int line)
abort();
}
+ENUM_VALUE_RTTI_8(GNodeMade,
+ UNMADE, DEFERRED, REQUESTED, BEINGMADE,
+ MADE, UPTODATE, ERROR, ABORTED);
+
+ENUM_FLAGS_RTTI_31(GNodeType,
+ OP_DEPENDS, OP_FORCE, OP_DOUBLEDEP,
+ /* OP_OPMASK is omitted since it combines other flags */
+ OP_OPTIONAL, OP_USE, OP_EXEC, OP_IGNORE,
+ OP_PRECIOUS, OP_SILENT, OP_MAKE, OP_JOIN,
+ OP_MADE, OP_SPECIAL, OP_USEBEFORE, OP_INVISIBLE,
+ OP_NOTMAIN, OP_PHONY, OP_NOPATH, OP_WAIT,
+ OP_NOMETA, OP_META, OP_NOMETA_CMP, OP_SUBMAKE,
+ OP_TRANSFORM, OP_MEMBER, OP_LIB, OP_ARCHV,
+ OP_HAS_COMMANDS, OP_SAVE_CMDS, OP_DEPS_FOUND, OP_MARK);
+
+ENUM_FLAGS_RTTI_10(GNodeFlags,
+ REMAKE, CHILDMADE, FORCE, DONE_WAIT,
+ DONE_ORDER, FROM_DEPEND, DONE_ALLSRC, CYCLE,
+ DONECYCLE, INTERNAL);
+
+void
+GNode_FprintDetails(FILE *f, const char *prefix, const GNode *gn,
+ const char *suffix)
+{
+ char type_buf[GNodeType_ToStringSize];
+ char flags_buf[GNodeFlags_ToStringSize];
+
+ fprintf(f, "%smade %s, type %s, flags %s%s",
+ prefix,
+ Enum_ValueToString(gn->made, GNodeMade_ToStringSpecs),
+ Enum_FlagsToString(type_buf, sizeof type_buf,
+ gn->type, GNodeType_ToStringSpecs),
+ Enum_FlagsToString(flags_buf, sizeof flags_buf,
+ gn->flags, GNodeFlags_ToStringSpecs),
+ suffix);
+}
+
/*-
*-----------------------------------------------------------------------
* Make_TimeStamp --
@@ -189,7 +226,7 @@ MakeTimeStamp(void *pgn, void *cgn)
{
return Make_TimeStamp((GNode *)pgn, (GNode *)cgn);
}
-
+
/*-
*-----------------------------------------------------------------------
* Make_OODate --
@@ -350,7 +387,7 @@ Make_OODate(GNode *gn)
return oodate;
}
-
+
/*-
*-----------------------------------------------------------------------
* MakeAddChild --
@@ -378,11 +415,11 @@ MakeAddChild(void *gnp, void *lp)
if (DEBUG(MAKE))
fprintf(debug_file, "MakeAddChild: need to examine %s%s\n",
gn->name, gn->cohort_num);
- (void)Lst_EnQueue(l, gn);
+ Lst_Enqueue(l, gn);
}
return 0;
}
-
+
/*-
*-----------------------------------------------------------------------
* MakeFindChild --
@@ -412,31 +449,18 @@ MakeFindChild(void *gnp, void *pgnp)
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.
+
+/* Called by Make_Run and SuffApplyTransform on the downward pass to handle
+ * .USE and transformation nodes, by copying the child 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.
+ * 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)
@@ -452,52 +476,42 @@ Make_HandleUse(GNode *cgn, GNode *pgn)
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);
+ /* .USEBEFORE */
+ Lst_PrependAll(pgn->commands, cgn->commands);
} else {
- /*
- * .USE or target has no commands --
- * append the child's commands to the parent.
- */
- (void)Lst_Concat(pgn->commands, cgn->commands, LST_CONCNEW);
+ /* .USE, or target has no commands */
+ Lst_AppendAll(pgn->commands, cgn->commands);
}
}
- if (Lst_Open(cgn->children) == SUCCESS) {
- while ((ln = Lst_Next(cgn->children)) != NULL) {
- GNode *tgn, *gn = (GNode *)Lst_Datum(ln);
+ Lst_Open(cgn->children);
+ while ((ln = Lst_Next(cgn->children)) != NULL) {
+ GNode *gn = LstNode_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 {
- free(gn->name);
- }
- gn->name = Var_Subst(NULL, gn->uname, pgn, VARF_WANTRES);
- 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;
+ /*
+ * 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 {
+ free(gn->name);
}
- Lst_Close(cgn->children);
+ gn->name = Var_Subst(gn->uname, pgn, VARE_WANTRES);
+ if (gn->uname && strcmp(gn->name, gn->uname) != 0) {
+ /* See if we have a target for this node. */
+ GNode *tgn = Targ_FindNode(gn->name, TARG_NOCREATE);
+ if (tgn != NULL)
+ gn = tgn;
+ }
+
+ Lst_Append(pgn->children, gn);
+ Lst_Append(gn->parents, pgn);
+ pgn->unmade += 1;
}
+ Lst_Close(cgn->children);
pgn->type |= cgn->type & ~(OP_OPMASK|OP_USE|OP_USEBEFORE|OP_TRANSFORM);
}
@@ -547,7 +561,7 @@ MakeHandleUse(void *cgnp, void *pgnp)
* 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) {
+ if ((ln = Lst_FindDatum(pgn->children, cgn)) != NULL) {
Lst_Remove(pgn->children, ln);
pgn->unmade--;
}
@@ -680,8 +694,8 @@ void
Make_Update(GNode *cgn)
{
GNode *pgn; /* the parent node */
- char *cname; /* the child's name */
- LstNode ln; /* Element in parents and iParents lists */
+ const char *cname; /* the child's name */
+ LstNode ln; /* Element in parents and implicitParents lists */
time_t mtime = -1;
char *p1;
Lst parents;
@@ -691,7 +705,7 @@ Make_Update(GNode *cgn)
checked++;
cname = Var_Value(TARGET, cgn, &p1);
- free(p1);
+ bmake_free(p1);
if (DEBUG(MAKE))
fprintf(debug_file, "Make_Update: %s%s\n", cgn->name, cgn->cohort_num);
@@ -724,123 +738,123 @@ Make_Update(GNode *cgn)
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;
+ Lst_Open(parents);
+ while ((ln = Lst_Next(parents)) != NULL) {
+ pgn = LstNode_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 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 (!(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 ( ! (cgn->type & (OP_EXEC|OP_USE|OP_USEBEFORE))) {
- if (cgn->made == MADE)
- pgn->flags |= CHILDMADE;
- (void)Make_TimeStamp(pgn, cgn);
- }
+ /*
+ * 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;
+ }
- /*
- * 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;
- }
+ if ( ! (cgn->type & (OP_EXEC|OP_USE|OP_USEBEFORE))) {
+ if (cgn->made == MADE)
+ pgn->flags |= CHILDMADE;
+ (void)Make_TimeStamp(pgn, cgn);
+ }
- /* 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);
- }
+ /*
+ * 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;
+ }
- /* 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;
- }
+ /* One more child of this parent is now made */
+ pgn->unmade -= 1;
+ if (pgn->unmade < 0) {
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);
+ fprintf(debug_file, "Graph cycles through %s%s\n",
+ pgn->name, pgn->cohort_num);
+ Targ_PrintGraph(2);
}
- /* Ok, we can schedule the parent again */
- pgn->made = REQUESTED;
- (void)Lst_EnQueue(toBeMade, pgn);
+ 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;
}
- Lst_Close(parents);
+ assert(pgn->order_pred != NULL);
+ if (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;
+ 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);
+ Lst_Open(cgn->implicitParents);
+ {
+ const char *cpref = Var_Value(PREFIX, cgn, &p1);
- while ((ln = Lst_Next(cgn->iParents)) != NULL) {
- pgn = (GNode *)Lst_Datum(ln);
+ while ((ln = Lst_Next(cgn->implicitParents)) != NULL) {
+ pgn = LstNode_Datum(ln);
if (pgn->flags & REMAKE) {
Var_Set(IMPSRC, cname, pgn);
if (cpref != NULL)
Var_Set(PREFIX, cpref, pgn);
}
}
- free(p1);
- Lst_Close(cgn->iParents);
+ bmake_free(p1);
+ Lst_Close(cgn->implicitParents);
}
}
-
+
/*-
*-----------------------------------------------------------------------
* MakeAddAllSrc --
@@ -890,7 +904,7 @@ MakeAddAllSrc(void *cgnp, void *pgnp)
cgn->type |= OP_MARK;
if ((cgn->type & (OP_EXEC|OP_USE|OP_USEBEFORE|OP_INVISIBLE)) == 0) {
- char *child, *allsrc;
+ const char *child, *allsrc;
char *p1 = NULL, *p2 = NULL;
if (cgn->type & OP_ARCHV)
@@ -904,7 +918,7 @@ MakeAddAllSrc(void *cgnp, void *pgnp)
}
if (allsrc != NULL)
Var_Append(ALLSRC, allsrc, pgn);
- free(p2);
+ bmake_free(p2);
if (pgn->type & OP_JOIN) {
if (cgn->made == MADE) {
Var_Append(OODATE, child, pgn);
@@ -930,11 +944,11 @@ MakeAddAllSrc(void *cgnp, void *pgnp)
*/
Var_Append(OODATE, child, pgn);
}
- free(p1);
+ bmake_free(p1);
}
return 0;
}
-
+
/*-
*-----------------------------------------------------------------------
* Make_DoAllVar --
@@ -976,11 +990,11 @@ Make_DoAllVar(GNode *gn)
if (gn->type & OP_JOIN) {
char *p1;
Var_Set(TARGET, Var_Value(ALLSRC, gn, &p1), gn);
- free(p1);
+ bmake_free(p1);
}
gn->flags |= DONE_ALLSRC;
}
-
+
/*-
*-----------------------------------------------------------------------
* MakeStartJobs --
@@ -1023,7 +1037,8 @@ MakeBuildChild(void *v_cn, void *toBeMade_next)
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)) {
+ assert(cn->order_pred);
+ if (Lst_ForEach(cn->order_pred, MakeCheckOrder, 0)) {
/* Can't build this (or anything else in this child list) yet */
cn->made = DEFERRED;
return 0; /* but keep looking */
@@ -1035,7 +1050,7 @@ MakeBuildChild(void *v_cn, void *toBeMade_next)
cn->made = REQUESTED;
if (toBeMade_next == NULL)
- Lst_AtEnd(toBeMade, cn);
+ Lst_Append(toBeMade, cn);
else
Lst_InsertBefore(toBeMade, toBeMade_next, cn);
@@ -1072,13 +1087,13 @@ MakeStartJobs(void)
GNode *gn;
int have_token = 0;
- while (!Lst_IsEmpty (toBeMade)) {
+ 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);
+ gn = Lst_Dequeue(toBeMade);
if (DEBUG(MAKE))
fprintf(debug_file, "Examining %s%s...\n",
gn->name, gn->cohort_num);
@@ -1147,7 +1162,7 @@ MakeStartJobs(void)
return FALSE;
}
-
+
/*-
*-----------------------------------------------------------------------
* MakePrintStatus --
@@ -1179,15 +1194,15 @@ MakePrintStatusOrder(void *ognp, void *gnp)
/* 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);
+ printf(" `%s%s' has .ORDER dependency against %s%s ",
+ gn->name, gn->cohort_num, ogn->name, ogn->cohort_num);
+ GNode_FprintDetails(stdout, "(", ogn, ")\n");
+
+ if (DEBUG(MAKE) && debug_file != stdout) {
+ fprintf(debug_file, " `%s%s' has .ORDER dependency against %s%s ",
+ gn->name, gn->cohort_num, ogn->name, ogn->cohort_num);
+ GNode_FprintDetails(debug_file, "(", ogn, ")\n");
+ }
return 0;
}
@@ -1214,12 +1229,13 @@ MakePrintStatus(void *gnp, void *v_errors)
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);
+ printf("`%s%s' was not built", gn->name, gn->cohort_num);
+ GNode_FprintDetails(stdout, " (", gn, ")!\n");
+ if (DEBUG(MAKE) && debug_file != stdout) {
+ fprintf(debug_file, "`%s%s' was not built",
+ gn->name, gn->cohort_num);
+ GNode_FprintDetails(debug_file, " (", gn, ")!\n");
+ }
/* Most likely problem is actually caused by .ORDER */
Lst_ForEach(gn->order_pred, MakePrintStatusOrder, gn);
break;
@@ -1262,7 +1278,7 @@ MakePrintStatus(void *gnp, void *v_errors)
Lst_ForEach(gn->children, MakePrintStatus, errors);
return 0;
}
-
+
/*-
*-----------------------------------------------------------------------
@@ -1281,7 +1297,7 @@ Make_ExpandUse(Lst targs)
GNode *gn; /* a temporary pointer */
Lst examine; /* List of targets to examine */
- examine = Lst_Duplicate(targs, NULL);
+ examine = Lst_Copy(targs, NULL);
/*
* Make an initial downward pass over the graph, marking nodes to be made
@@ -1291,8 +1307,8 @@ Make_ExpandUse(Lst targs)
* 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);
+ while (!Lst_IsEmpty(examine)) {
+ gn = Lst_Dequeue(examine);
if (gn->flags & REMAKE)
/* We've looked at this one already */
@@ -1302,13 +1318,8 @@ Make_ExpandUse(Lst targs)
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;
- }
+ if (gn->type & OP_DOUBLEDEP)
+ Lst_PrependAll(examine, gn->cohorts);
/*
* Apply any .USE rules before looking for implicit dependencies
@@ -1349,7 +1360,7 @@ Make_ExpandUse(Lst targs)
Lst_ForEach(gn->children, MakeAddChild, examine);
}
- Lst_Destroy(examine, NULL);
+ Lst_Free(examine);
}
/*-
@@ -1369,8 +1380,8 @@ link_parent(void *cnp, void *pnp)
GNode *cn = cnp;
GNode *pn = pnp;
- Lst_AtEnd(pn->children, cn);
- Lst_AtEnd(cn->parents, pn);
+ Lst_Append(pn->children, cn);
+ Lst_Append(cn->parents, pn);
pn->unmade++;
return 0;
}
@@ -1392,9 +1403,9 @@ add_wait_dep(void *v_cn, void *v_wn)
fprintf(debug_file, ".WAIT: add dependency %s%s -> %s\n",
cn->name, cn->cohort_num, wn->name);
- Lst_AtEnd(wn->children, cn);
+ Lst_Append(wn->children, cn);
wn->unmade++;
- Lst_AtEnd(cn->parents, wn);
+ Lst_Append(cn->parents, wn);
return 0;
}
@@ -1417,18 +1428,18 @@ Make_ProcessWait(Lst targs)
pgn->flags = REMAKE;
pgn->type = OP_PHONY | OP_DEPENDS;
/* Get it displayed in the diag dumps */
- Lst_AtFront(Targ_List(), pgn);
+ Lst_Prepend(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);
+ examine = Lst_Init();
+ Lst_Append(examine, pgn);
- while (!Lst_IsEmpty (examine)) {
- pgn = Lst_DeQueue(examine);
+ while (!Lst_IsEmpty(examine)) {
+ pgn = Lst_Dequeue(examine);
/* We only want to process each child-list once */
if (pgn->flags & DONE_WAIT)
@@ -1437,30 +1448,25 @@ Make_ProcessWait(Lst targs)
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;
- }
+ if (pgn->type & OP_DOUBLEDEP)
+ Lst_PrependAll(examine, pgn->cohorts);
owln = Lst_First(pgn->children);
Lst_Open(pgn->children);
for (; (ln = Lst_Next(pgn->children)) != NULL; ) {
- cgn = Lst_Datum(ln);
+ cgn = LstNode_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_Append(examine, cgn);
}
}
Lst_Close(pgn->children);
}
- Lst_Destroy(examine, NULL);
+ Lst_Free(examine);
}
/*-
@@ -1493,7 +1499,7 @@ Make_Run(Lst targs)
int errors; /* Number of errors the Job module reports */
/* Start trying to make the current targets... */
- toBeMade = Lst_Init(FALSE);
+ toBeMade = Lst_Init();
Make_ExpandUse(targs);
Make_ProcessWait(targs);
diff --git a/contrib/bmake/make.h b/contrib/bmake/make.h
index eafa761ea36a..520a6602518f 100644
--- a/contrib/bmake/make.h
+++ b/contrib/bmake/make.h
@@ -1,4 +1,4 @@
-/* $NetBSD: make.h,v 1.109 2020/07/02 15:14:38 rillig Exp $ */
+/* $NetBSD: make.h,v 1.137 2020/09/02 23:42:58 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -77,8 +77,8 @@
* The global definitions for pmake
*/
-#ifndef _MAKE_H_
-#define _MAKE_H_
+#ifndef MAKE_MAKE_H
+#define MAKE_MAKE_H
#ifdef HAVE_CONFIG_H
# include "config.h"
@@ -86,7 +86,9 @@
#include <sys/types.h>
#include <sys/param.h>
+#include <sys/stat.h>
+#include <assert.h>
#include <ctype.h>
#include <fcntl.h>
#include <stdio.h>
@@ -132,8 +134,34 @@
#define MAKE_ATTR_PRINTFLIKE(fmtarg, firstvararg) /* delete */
#endif
-#include "sprite.h"
+/*
+ * A boolean type is defined as an integer, not an enum, for historic reasons.
+ * The only allowed values are the constants TRUE and FALSE (1 and 0).
+ */
+
+#ifdef USE_DOUBLE_BOOLEAN
+/* During development, to find type mismatches in function declarations. */
+typedef double Boolean;
+#elif defined(USE_UCHAR_BOOLEAN)
+/* During development, to find code that depends on the exact value of TRUE or
+ * that stores other values in Boolean variables. */
+typedef unsigned char Boolean;
+#define TRUE ((unsigned char)0xFF)
+#define FALSE ((unsigned char)0x00)
+#elif defined(USE_ENUM_BOOLEAN)
+typedef enum { FALSE, TRUE} Boolean;
+#else
+typedef int Boolean;
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif /* TRUE */
+#ifndef FALSE
+#define FALSE 0
+#endif /* FALSE */
+
#include "lst.h"
+#include "enum.h"
#include "hash.h"
#include "make-conf.h"
#include "buf.h"
@@ -150,164 +178,191 @@
#define POSIX_SIGNALS
#endif
-/*-
- * The structure for an individual graph node. Each node has several
- * pieces of data associated with it.
- * 1) the name of the target it describes
- * 2) the location of the target file in the file system.
- * 3) the type of operator used to define its sources (qv. parse.c)
- * 4) whether it is involved in this invocation of make
- * 5) whether the target has been remade
- * 6) whether any of its children has been remade
- * 7) the number of its children that are, as yet, unmade
- * 8) its modification time
- * 9) the modification time of its youngest child (qv. make.c)
- * 10) a list of nodes for which this is a source (parents)
- * 11) a list of nodes on which this depends (children)
- * 12) a list of nodes that depend on this, as gleaned from the
- * transformation rules (iParents)
- * 13) a list of ancestor nodes, which includes parents, iParents,
- * and recursive parents of parents
- * 14) a list of nodes of the same name created by the :: operator
- * 15) a list of nodes that must be made (if they're made) before
- * this node can be, but that do not enter into the datedness of
- * this node.
- * 16) a list of nodes that must be made (if they're made) before
- * this node or any child of this node can be, but that do not
- * enter into the datedness of this node.
- * 17) a list of nodes that must be made (if they're made) after
- * this node is, but that do not depend on this node, in the
- * normal sense.
- * 18) a Lst of ``local'' variables that are specific to this target
- * and this target only (qv. var.c [$@ $< $?, etc.])
- * 19) a Lst of strings that are commands to be given to a shell
- * to create this target.
- */
+typedef enum {
+ UNMADE, /* Not examined yet */
+ DEFERRED, /* Examined once (building child) */
+ REQUESTED, /* on toBeMade list */
+ BEINGMADE, /* Target is already being made.
+ * Indicates a cycle in the graph. */
+ MADE, /* Was out-of-date and has been made */
+ UPTODATE, /* Was already up-to-date */
+ ERROR, /* An error occurred while it was being
+ * made (used only in compat mode) */
+ ABORTED /* The target was aborted due to an error
+ * making an inferior (compat). */
+} GNodeMade;
+
+/* The OP_ constants are used when parsing a dependency line as a way of
+ * communicating to other parts of the program the way in which a target
+ * should be made.
+ *
+ * These constants are bitwise-OR'ed together and placed in the 'type' field
+ * of each node. Any node that has a 'type' field which satisfies the OP_NOP
+ * function was never never on the left-hand side of an operator, though it
+ * may have been on the right-hand side... */
+typedef enum {
+ /* Execution of commands depends on children (:) */
+ OP_DEPENDS = 1 << 0,
+ /* Always execute commands (!) */
+ OP_FORCE = 1 << 1,
+ /* Execution of commands depends on children per line (::) */
+ OP_DOUBLEDEP = 1 << 2,
+
+ OP_OPMASK = OP_DEPENDS|OP_FORCE|OP_DOUBLEDEP,
+
+ /* Don't care if the target doesn't exist and can't be created */
+ OP_OPTIONAL = 1 << 3,
+ /* Use associated commands for parents */
+ OP_USE = 1 << 4,
+ /* Target is never out of date, but always execute commands anyway.
+ * Its time doesn't matter, so it has none...sort of */
+ OP_EXEC = 1 << 5,
+ /* Ignore errors when creating the node */
+ OP_IGNORE = 1 << 6,
+ /* Don't remove the target when interrupted */
+ OP_PRECIOUS = 1 << 7,
+ /* Don't echo commands when executed */
+ OP_SILENT = 1 << 8,
+ /* 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 */
+ OP_MAKE = 1 << 9,
+ /* Target is out-of-date only if any of its children was out-of-date */
+ OP_JOIN = 1 << 10,
+ /* Assume the children of the node have been already made */
+ OP_MADE = 1 << 11,
+ /* Special .BEGIN, .END, .INTERRUPT */
+ OP_SPECIAL = 1 << 12,
+ /* Like .USE, only prepend commands */
+ OP_USEBEFORE = 1 << 13,
+ /* The node is invisible to its parents. I.e. it doesn't show up in the
+ * parents' local variables. */
+ OP_INVISIBLE = 1 << 14,
+ /* The node is exempt from normal 'main target' processing in parse.c */
+ OP_NOTMAIN = 1 << 15,
+ /* Not a file target; run always */
+ OP_PHONY = 1 << 16,
+ /* Don't search for file in the path */
+ OP_NOPATH = 1 << 17,
+ /* .WAIT phony node */
+ OP_WAIT = 1 << 18,
+ /* .NOMETA do not create a .meta file */
+ OP_NOMETA = 1 << 19,
+ /* .META we _do_ want a .meta file */
+ OP_META = 1 << 20,
+ /* Do not compare commands in .meta file */
+ OP_NOMETA_CMP = 1 << 21,
+ /* Possibly a submake node */
+ OP_SUBMAKE = 1 << 22,
+
+ /* Attributes applied by PMake */
+
+ /* The node is a transformation rule */
+ OP_TRANSFORM = 1 << 31,
+ /* Target is a member of an archive */
+ OP_MEMBER = 1 << 30,
+ /* Target is a library */
+ OP_LIB = 1 << 29,
+ /* Target is an archive construct */
+ OP_ARCHV = 1 << 28,
+ /* Target has all the commands it should. Used when parsing to catch
+ * multiple commands for a target. */
+ OP_HAS_COMMANDS = 1 << 27,
+ /* Saving commands on .END (Compat) */
+ OP_SAVE_CMDS = 1 << 26,
+ /* Already processed by Suff_FindDeps */
+ OP_DEPS_FOUND = 1 << 25,
+ /* Node found while expanding .ALLSRC */
+ OP_MARK = 1 << 24
+} GNodeType;
+
+typedef enum {
+ REMAKE = 0x0001, /* this target needs to be (re)made */
+ CHILDMADE = 0x0002, /* children of this target were made */
+ FORCE = 0x0004, /* children don't exist, and we pretend made */
+ DONE_WAIT = 0x0008, /* Set by Make_ProcessWait() */
+ DONE_ORDER = 0x0010, /* Build requested by .ORDER processing */
+ FROM_DEPEND = 0x0020, /* Node created from .depend */
+ DONE_ALLSRC = 0x0040, /* We do it once only */
+ CYCLE = 0x1000, /* Used by MakePrintStatus */
+ DONECYCLE = 0x2000, /* Used by MakePrintStatus */
+ INTERNAL = 0x4000 /* Internal use only */
+} GNodeFlags;
+
+/* A graph node represents a target that can possibly be made, including its
+ * relation to other targets and a lot of other details. */
typedef struct GNode {
- char *name; /* The target's name */
- char *uname; /* The unexpanded name of a .USE node */
- char *path; /* The full pathname of the file */
- int type; /* Its type (see the OP flags, below) */
-
- int flags;
-#define REMAKE 0x1 /* this target needs to be (re)made */
-#define CHILDMADE 0x2 /* children of this target were made */
-#define FORCE 0x4 /* children don't exist, and we pretend made */
-#define DONE_WAIT 0x8 /* Set by Make_ProcessWait() */
-#define DONE_ORDER 0x10 /* Build requested by .ORDER processing */
-#define FROM_DEPEND 0x20 /* Node created from .depend */
-#define DONE_ALLSRC 0x40 /* We do it once only */
-#define CYCLE 0x1000 /* Used by MakePrintStatus */
-#define DONECYCLE 0x2000 /* Used by MakePrintStatus */
-#define INTERNAL 0x4000 /* Internal use only */
- enum enum_made {
- UNMADE, DEFERRED, REQUESTED, BEINGMADE,
- MADE, UPTODATE, ERROR, ABORTED
- } made; /* Set to reflect the state of processing
- * on this node:
- * UNMADE - Not examined yet
- * DEFERRED - Examined once (building child)
- * REQUESTED - on toBeMade list
- * BEINGMADE - Target is already being made.
- * Indicates a cycle in the graph.
- * MADE - Was out-of-date and has been made
- * UPTODATE - Was already up-to-date
- * ERROR - An error occurred while it was being
- * made (used only in compat mode)
- * ABORTED - The target was aborted due to
- * an error making an inferior (compat).
- */
- int unmade; /* The number of unmade children */
-
- time_t mtime; /* Its modification time */
- struct GNode *cmgn; /* The youngest child */
-
- Lst iParents; /* Links to parents for which this is an
- * implied source, if any */
- Lst cohorts; /* Other nodes for the :: operator */
- Lst parents; /* Nodes that depend on this one */
- Lst children; /* Nodes on which this one depends */
- Lst order_pred; /* .ORDER nodes we need made */
- Lst order_succ; /* .ORDER nodes who need us */
-
- char cohort_num[8]; /* #n for this cohort */
- int unmade_cohorts;/* # of unmade instances on the
- cohorts list */
- struct GNode *centurion; /* Pointer to the first instance of a ::
- node; only set when on a cohorts list */
- unsigned int checked; /* Last time we tried to makle this node */
-
- Hash_Table context; /* The local variables */
- Lst commands; /* Creation commands */
-
- struct _Suff *suffix; /* Suffix for the node (determined by
- * Suff_FindDeps and opaque to everyone
- * but the Suff module) */
- const char *fname; /* filename where the GNode got defined */
- int lineno; /* line number where the GNode got defined */
+ /* The target's name, such as "clean" or "make.c" */
+ char *name;
+ /* The unexpanded name of a .USE node */
+ char *uname;
+ /* The full pathname of the file belonging to the target.
+ * XXX: What about .PHONY targets? These don't have an associated path. */
+ char *path;
+
+ /* The type of operator used to define the sources (see the OP flags below).
+ * XXX: This looks like a wild mixture of type and flags. */
+ GNodeType type;
+ /* whether it is involved in this invocation of make */
+ GNodeFlags flags;
+
+ /* The state of processing on this node */
+ GNodeMade made;
+ int unmade; /* The number of unmade children */
+
+ time_t mtime; /* Its modification time */
+ struct GNode *cmgn; /* The youngest child */
+
+ /* The GNodes for which this node is an implied source. May be empty.
+ * For example, when there is an inference rule for .c.o, the node for
+ * file.c has the node for file.o in this list. */
+ Lst implicitParents;
+
+ /* Other nodes of the same name for the :: operator. */
+ Lst cohorts;
+
+ /* The nodes that depend on this one, or in other words, the nodes for
+ * which this is a source. */
+ Lst parents;
+ /* The nodes on which this one depends. */
+ Lst children;
+
+ /* .ORDER nodes we need made. The nodes that must be made (if they're
+ * made) before this node can be made, but that do not enter into the
+ * datedness of this node. */
+ Lst order_pred;
+ /* .ORDER nodes who need us. The nodes that must be made (if they're made
+ * at all) after this node is made, but that do not depend on this node,
+ * in the normal sense. */
+ Lst order_succ;
+
+ /* #n for this cohort */
+ char cohort_num[8];
+ /* The number of unmade instances on the cohorts list */
+ int unmade_cohorts;
+ /* Pointer to the first instance of a '::' node; only set when on a
+ * cohorts list */
+ struct GNode *centurion;
+
+ /* Last time (sequence number) we tried to make this node */
+ unsigned int checked;
+
+ /* The "local" variables that are specific to this target and this target
+ * only, such as $@, $<, $?. */
+ Hash_Table context;
+
+ /* The commands to be given to a shell to create this target. */
+ Lst commands;
+
+ /* Suffix for the node (determined by Suff_FindDeps and opaque to everyone
+ * but the Suff module) */
+ struct Suff *suffix;
+
+ /* filename where the GNode got defined */
+ const char *fname;
+ /* line number where the GNode got defined */
+ int lineno;
} GNode;
-/*
- * The OP_ constants are used when parsing a dependency line as a way of
- * communicating to other parts of the program the way in which a target
- * should be made. These constants are bitwise-OR'ed together and
- * placed in the 'type' field of each node. Any node that has
- * a 'type' field which satisfies the OP_NOP function was never never on
- * the lefthand side of an operator, though it may have been on the
- * righthand side...
- */
-#define OP_DEPENDS 0x00000001 /* Execution of commands depends on
- * kids (:) */
-#define OP_FORCE 0x00000002 /* Always execute commands (!) */
-#define OP_DOUBLEDEP 0x00000004 /* Execution of commands depends on kids
- * per line (::) */
-#define OP_OPMASK (OP_DEPENDS|OP_FORCE|OP_DOUBLEDEP)
-
-#define OP_OPTIONAL 0x00000008 /* Don't care if the target doesn't
- * exist and can't be created */
-#define OP_USE 0x00000010 /* Use associated commands for parents */
-#define OP_EXEC 0x00000020 /* Target is never out of date, but always
- * execute commands anyway. Its time
- * doesn't matter, so it has none...sort
- * of */
-#define OP_IGNORE 0x00000040 /* Ignore errors when creating the node */
-#define OP_PRECIOUS 0x00000080 /* Don't remove the target when
- * interrupted */
-#define OP_SILENT 0x00000100 /* Don't echo commands when executed */
-#define OP_MAKE 0x00000200 /* Target is a recursive make so its
- * commands should always be executed when
- * it is out of date, regardless of the
- * state of the -n or -t flags */
-#define OP_JOIN 0x00000400 /* Target is out-of-date only if any of its
- * children was out-of-date */
-#define OP_MADE 0x00000800 /* Assume the children of the node have
- * been already made */
-#define OP_SPECIAL 0x00001000 /* Special .BEGIN, .END, .INTERRUPT */
-#define OP_USEBEFORE 0x00002000 /* Like .USE, only prepend commands */
-#define OP_INVISIBLE 0x00004000 /* The node is invisible to its parents.
- * I.e. it doesn't show up in the parents's
- * local variables. */
-#define OP_NOTMAIN 0x00008000 /* The node is exempt from normal 'main
- * target' processing in parse.c */
-#define OP_PHONY 0x00010000 /* Not a file target; run always */
-#define OP_NOPATH 0x00020000 /* Don't search for file in the path */
-#define OP_WAIT 0x00040000 /* .WAIT phony node */
-#define OP_NOMETA 0x00080000 /* .NOMETA do not create a .meta file */
-#define OP_META 0x00100000 /* .META we _do_ want a .meta file */
-#define OP_NOMETA_CMP 0x00200000 /* Do not compare commands in .meta file */
-#define OP_SUBMAKE 0x00400000 /* Possibly a submake node */
-/* Attributes applied by PMake */
-#define OP_TRANSFORM 0x80000000 /* The node is a transformation rule */
-#define OP_MEMBER 0x40000000 /* Target is a member of an archive */
-#define OP_LIB 0x20000000 /* Target is a library */
-#define OP_ARCHV 0x10000000 /* Target is an archive construct */
-#define OP_HAS_COMMANDS 0x08000000 /* Target has all the commands it should.
- * Used when parsing to catch multiple
- * commands for a target */
-#define OP_SAVE_CMDS 0x04000000 /* Saving commands on .END (Compat) */
-#define OP_DEPS_FOUND 0x02000000 /* Already processed by Suff_FindDeps */
-#define OP_MARK 0x01000000 /* Node found while expanding .ALLSRC */
-
#define NoExecute(gn) ((gn->type & OP_MAKE) ? noRecursiveExecute : noExecute)
/*
* OP_NOP will return TRUE if the node with the given type was not the
@@ -330,18 +385,6 @@ typedef struct GNode {
#define TARG_NOHASH 0x02 /* don't look in/add to hash table */
/*
- * These constants are all used by the Str_Concat function to decide how the
- * final string should look. If STR_ADDSPACE is given, a space will be
- * placed between the two strings. If STR_ADDSLASH is given, a '/' will
- * be used instead of a space. If neither is given, no intervening characters
- * will be placed between the two strings in the final output. If the
- * STR_DOFREE bit is set, the two input strings will be freed before
- * Str_Concat returns.
- */
-#define STR_ADDSPACE 0x01 /* add a space when Str_Concat'ing */
-#define STR_ADDSLASH 0x02 /* add a slash when Str_Concat'ing */
-
-/*
* Error levels for parsing. PARSE_FATAL means the process cannot continue
* once the makefile has been parsed. PARSE_WARNING means it can. Passed
* as the first argument to Parse_Error.
@@ -353,9 +396,11 @@ typedef struct GNode {
/*
* Values returned by Cond_Eval.
*/
-#define COND_PARSE 0 /* Parse the next lines */
-#define COND_SKIP 1 /* Skip the next lines */
-#define COND_INVALID 2 /* Not a conditional statement */
+typedef enum {
+ COND_PARSE, /* Parse the next lines */
+ COND_SKIP, /* Skip the next lines */
+ COND_INVALID /* Not a conditional statement */
+} CondEvalResult;
/*
* Definitions for the "local" variables. Used only for clarity.
@@ -403,7 +448,6 @@ 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 */
@@ -420,7 +464,6 @@ extern GNode *VAR_INTERNAL; /* Variables defined internally by make
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
@@ -439,6 +482,8 @@ extern char *progname; /* The program name */
extern char *makeDependfile; /* .depend */
extern char **savedEnv; /* if we replaced environ this will be non-NULL */
+extern int makelevel;
+
/*
* We cannot vfork() in a child of vfork().
* Most systems do not enforce this but some do.
@@ -464,7 +509,7 @@ extern pid_t myPid;
* There is one bit per module. It is up to the module what debug
* information to print.
*/
-extern FILE *debug_file; /* Output written here - default stdout */
+extern FILE *debug_file; /* Output is written here - default stderr */
extern int debug;
#define DEBUG_ARCH 0x00001
#define DEBUG_COND 0x00002
@@ -481,12 +526,15 @@ extern int debug;
#define DEBUG_ERROR 0x01000
#define DEBUG_LOUD 0x02000
#define DEBUG_META 0x04000
+#define DEBUG_HASH 0x08000
#define DEBUG_GRAPH3 0x10000
#define DEBUG_SCRIPT 0x20000
#define DEBUG_PARSE 0x40000
#define DEBUG_CWD 0x80000
+#define DEBUG_LINT 0x100000
+
#define CONCAT(a,b) a##b
#define DEBUG(module) (debug & CONCAT(DEBUG_,module))
@@ -501,16 +549,13 @@ 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 **);
int dieQuietly(GNode *, int);
void PrintOnError(GNode *, const char *);
void Main_ExportMAKEFLAGS(Boolean);
Boolean Main_SetObjdir(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2);
int mkTempFile(const char *, char **);
int str2Lst_Append(Lst, char *, const char *);
-int cached_lstat(const char *, void *);
-int cached_stat(const char *, void *);
+void GNode_FprintDetails(FILE *, const char *, const GNode *, const char *);
#ifdef __GNUC__
#define UNCONST(ptr) ({ \
@@ -524,10 +569,10 @@ int cached_stat(const char *, void *);
#endif
#ifndef MIN
-#define MIN(a, b) ((a < b) ? a : b)
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
#ifndef MAX
-#define MAX(a, b) ((a > b) ? a : b)
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#endif
/* At least GNU/Hurd systems lack hardcoded MAXPATHLEN/PATH_MAX */
@@ -547,4 +592,4 @@ int cached_stat(const char *, void *);
#define KILLPG(pid, sig) killpg((pid), (sig))
#endif
-#endif /* _MAKE_H_ */
+#endif /* MAKE_MAKE_H */
diff --git a/contrib/bmake/make_malloc.c b/contrib/bmake/make_malloc.c
index 7e2f75ff85e0..ba9632b2b254 100644
--- a/contrib/bmake/make_malloc.c
+++ b/contrib/bmake/make_malloc.c
@@ -1,4 +1,4 @@
-/* $NetBSD: make_malloc.c,v 1.12 2020/07/03 08:02:55 rillig Exp $ */
+/* $NetBSD: make_malloc.c,v 1.18 2020/09/02 06:10:44 rillig Exp $ */
/*-
* Copyright (c) 2009 The NetBSD Foundation, Inc.
@@ -28,7 +28,7 @@
#ifdef MAKE_NATIVE
#include <sys/cdefs.h>
-__RCSID("$NetBSD: make_malloc.c,v 1.12 2020/07/03 08:02:55 rillig Exp $");
+__RCSID("$NetBSD: make_malloc.c,v 1.18 2020/09/02 06:10:44 rillig Exp $");
#endif
#include <stdio.h>
@@ -41,10 +41,7 @@ __RCSID("$NetBSD: make_malloc.c,v 1.12 2020/07/03 08:02:55 rillig Exp $");
#ifndef USE_EMALLOC
static MAKE_ATTR_DEAD void enomem(void);
-/*
- * enomem --
- * die when out of memory.
- */
+/* die when out of memory. */
static MAKE_ATTR_DEAD void
enomem(void)
{
@@ -52,10 +49,7 @@ enomem(void)
exit(2);
}
-/*
- * bmake_malloc --
- * malloc, but die on error.
- */
+/* malloc, but die on error. */
void *
bmake_malloc(size_t len)
{
@@ -66,10 +60,7 @@ bmake_malloc(size_t len)
return p;
}
-/*
- * bmake_strdup --
- * strdup, but die on error.
- */
+/* strdup, but die on error. */
char *
bmake_strdup(const char *str)
{
@@ -82,33 +73,17 @@ bmake_strdup(const char *str)
return memcpy(p, str, len);
}
-/*
- * bmake_strndup --
- * strndup, but die on error.
- */
+/* Allocate a string starting from str with exactly len characters. */
char *
-bmake_strndup(const char *str, size_t max_len)
+bmake_strldup(const char *str, size_t 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);
+ char *p = bmake_malloc(len + 1);
memcpy(p, str, len);
p[len] = '\0';
-
return p;
}
-/*
- * bmake_realloc --
- * realloc, but die on error.
- */
+/* realloc, but die on error. */
void *
bmake_realloc(void *ptr, size_t size)
{
@@ -117,3 +92,10 @@ bmake_realloc(void *ptr, size_t size)
return ptr;
}
#endif
+
+/* Allocate a string from start up to but excluding end. */
+char *
+bmake_strsedup(const char *start, const char *end)
+{
+ return bmake_strldup(start, (size_t)(end - start));
+}
diff --git a/contrib/bmake/make_malloc.h b/contrib/bmake/make_malloc.h
index 36d3eff3c027..ac804d79c711 100644
--- a/contrib/bmake/make_malloc.h
+++ b/contrib/bmake/make_malloc.h
@@ -1,4 +1,4 @@
-/* $NetBSD: make_malloc.h,v 1.4 2009/01/24 14:43:29 dsl Exp $ */
+/* $NetBSD: make_malloc.h,v 1.10 2020/08/29 16:47:45 rillig Exp $ */
/*-
* Copyright (c) 2009 The NetBSD Foundation, Inc.
@@ -30,12 +30,25 @@
void *bmake_malloc(size_t);
void *bmake_realloc(void *, size_t);
char *bmake_strdup(const char *);
-char *bmake_strndup(const char *, size_t);
+char *bmake_strldup(const char *, size_t);
#else
#include <util.h>
#define bmake_malloc(x) emalloc(x)
#define bmake_realloc(x,y) erealloc(x,y)
#define bmake_strdup(x) estrdup(x)
-#define bmake_strndup(x,y) estrndup(x,y)
+#define bmake_strldup(x,y) estrndup(x,y)
#endif
+char *bmake_strsedup(const char *, const char *);
+/* Thin wrapper around free(3) to avoid the extra function call in case
+ * p is NULL, which on x86_64 costs about 12 machine instructions.
+ * Other platforms are similarly affected.
+ *
+ * The case of a NULL pointer happens especially often after Var_Value,
+ * since only environment variables need to be freed, but not others. */
+static inline void MAKE_ATTR_UNUSED
+bmake_free(void *p)
+{
+ if (p != NULL)
+ free(p);
+}
diff --git a/contrib/bmake/meta.c b/contrib/bmake/meta.c
index 27f16a0da908..5d87febb0496 100644
--- a/contrib/bmake/meta.c
+++ b/contrib/bmake/meta.c
@@ -1,4 +1,4 @@
-/* $NetBSD: meta.c,v 1.86 2020/07/11 00:39:53 sjg Exp $ */
+/* $NetBSD: meta.c,v 1.113 2020/09/02 04:08:54 rillig Exp $ */
/*
* Implement 'meta' mode.
@@ -47,6 +47,7 @@ char * dirname(char *);
#endif
#include "make.h"
+#include "dir.h"
#include "job.h"
#ifdef USE_FILEMON
@@ -318,7 +319,7 @@ meta_name(char *mname, size_t mnamelen,
static int
is_submake(void *cmdp, void *gnp)
{
- static char *p_make = NULL;
+ static const char *p_make = NULL;
static int p_len;
char *cmd = cmdp;
GNode *gn = gnp;
@@ -333,7 +334,7 @@ is_submake(void *cmdp, void *gnp)
}
cp = strchr(cmd, '$');
if ((cp)) {
- mp = Var_Subst(NULL, cmd, gn, VARF_WANTRES);
+ mp = Var_Subst(cmd, gn, VARE_WANTRES);
cmd = mp;
}
cp2 = strstr(cmd, p_make);
@@ -372,13 +373,13 @@ printCMD(void *cmdp, void *mfpp)
{
meta_file_t *mfp = mfpp;
char *cmd = cmdp;
- char *cp = NULL;
+ char *cmd_freeIt = NULL;
if (strchr(cmd, '$')) {
- cmd = cp = Var_Subst(NULL, cmd, mfp->gn, VARF_WANTRES);
+ cmd = cmd_freeIt = Var_Subst(cmd, mfp->gn, VARE_WANTRES);
}
fprintf(mfp->fp, "CMD %s\n", cmd);
- free(cp);
+ free(cmd_freeIt);
return 0;
}
@@ -403,7 +404,7 @@ static Boolean
meta_needed(GNode *gn, const char *dname,
char *objdir, int verbose)
{
- struct stat fs;
+ struct make_stat mst;
if (verbose)
verbose = DEBUG(META);
@@ -437,7 +438,7 @@ meta_needed(GNode *gn, const char *dname,
}
/* The object directory may not exist. Check it.. */
- if (cached_stat(dname, &fs) != 0) {
+ if (cached_stat(dname, &mst) != 0) {
if (verbose)
fprintf(debug_file, "Skipping meta for %s: no .OBJDIR\n",
gn->name);
@@ -488,7 +489,7 @@ meta_create(BuildMon *pbm, GNode *gn)
char *mp;
/* Describe the target we are building */
- mp = Var_Subst(NULL, "${" MAKE_META_PREFIX "}", gn, VARF_WANTRES);
+ mp = Var_Subst("${" MAKE_META_PREFIX "}", gn, VARE_WANTRES);
if (*mp)
fprintf(stdout, "%s\n", mp);
free(mp);
@@ -546,7 +547,7 @@ meta_create(BuildMon *pbm, GNode *gn)
}
out:
for (i--; i >= 0; i--) {
- free(p[i]);
+ bmake_free(p[i]);
}
return mf.fp;
@@ -629,24 +630,19 @@ meta_mode_init(const char *make_mode)
/*
* We consider ourselves master of all within ${.MAKE.META.BAILIWICK}
*/
- metaBailiwick = Lst_Init(FALSE);
- metaBailiwickStr = Var_Subst(NULL, "${.MAKE.META.BAILIWICK:O:u:tA}",
- VAR_GLOBAL, VARF_WANTRES);
- if (metaBailiwickStr) {
- str2Lst_Append(metaBailiwick, metaBailiwickStr, NULL);
- }
+ metaBailiwick = Lst_Init();
+ metaBailiwickStr = Var_Subst("${.MAKE.META.BAILIWICK:O:u:tA}",
+ VAR_GLOBAL, VARE_WANTRES);
+ str2Lst_Append(metaBailiwick, metaBailiwickStr, NULL);
/*
* We ignore any paths that start with ${.MAKE.META.IGNORE_PATHS}
*/
- metaIgnorePaths = Lst_Init(FALSE);
+ metaIgnorePaths = Lst_Init();
Var_Append(MAKE_META_IGNORE_PATHS,
"/dev /etc /proc /tmp /var/run /var/tmp ${TMPDIR}", VAR_GLOBAL);
- metaIgnorePathsStr = Var_Subst(NULL,
- "${" MAKE_META_IGNORE_PATHS ":O:u:tA}", VAR_GLOBAL,
- VARF_WANTRES);
- if (metaIgnorePathsStr) {
- str2Lst_Append(metaIgnorePaths, metaIgnorePathsStr, NULL);
- }
+ metaIgnorePathsStr = Var_Subst("${" MAKE_META_IGNORE_PATHS ":O:u:tA}",
+ VAR_GLOBAL, VARE_WANTRES);
+ str2Lst_Append(metaIgnorePaths, metaIgnorePathsStr, NULL);
/*
* We ignore any paths that match ${.MAKE.META.IGNORE_PATTERNS}
@@ -654,12 +650,12 @@ meta_mode_init(const char *make_mode)
cp = NULL;
if (Var_Value(MAKE_META_IGNORE_PATTERNS, VAR_GLOBAL, &cp)) {
metaIgnorePatterns = TRUE;
- free(cp);
+ bmake_free(cp);
}
cp = NULL;
if (Var_Value(MAKE_META_IGNORE_FILTER, VAR_GLOBAL, &cp)) {
metaIgnoreFilter = TRUE;
- free(cp);
+ bmake_free(cp);
}
}
@@ -822,8 +818,8 @@ meta_job_output(Job *job, char *cp, const char *nl)
if (!meta_prefix) {
char *cp2;
- meta_prefix = Var_Subst(NULL, "${" MAKE_META_PREFIX "}",
- VAR_GLOBAL, VARF_WANTRES);
+ meta_prefix = Var_Subst("${" MAKE_META_PREFIX "}",
+ VAR_GLOBAL, VARE_WANTRES);
if ((cp2 = strchr(meta_prefix, '$')))
meta_prefix_len = cp2 - meta_prefix;
else
@@ -894,9 +890,11 @@ meta_job_finish(Job *job)
void
meta_finish(void)
{
- Lst_Destroy(metaBailiwick, NULL);
+ if (metaBailiwick != NULL)
+ Lst_Free(metaBailiwick);
free(metaBailiwickStr);
- Lst_Destroy(metaIgnorePaths, NULL);
+ if (metaIgnorePaths != NULL)
+ Lst_Free(metaIgnorePaths);
free(metaIgnorePathsStr);
}
@@ -958,39 +956,23 @@ prefix_match(void *p, void *q)
return strncmp(path, prefix, n) == 0;
}
-/*
- * looking for exact or prefix/ match to
- * Lst_Find wants 0 to stop search
- */
-static int
+/* See if the path equals prefix or starts with "prefix/". */
+static Boolean
path_match(const void *p, const void *q)
{
- const char *prefix = q;
const char *path = p;
+ const char *prefix = q;
size_t n = strlen(prefix);
- int rc;
- if ((rc = strncmp(path, prefix, n)) == 0) {
- switch (path[n]) {
- case '\0':
- case '/':
- break;
- default:
- rc = 1;
- break;
- }
- }
- return rc;
+ if (strncmp(path, prefix, n) != 0)
+ return FALSE;
+ return path[n] == '\0' || path[n] == '/';
}
-/* Lst_Find wants 0 to stop search */
-static int
+static Boolean
string_match(const void *p, const void *q)
{
- const char *p1 = p;
- const char *p2 = q;
-
- return strcmp(p1, p2);
+ return strcmp(p, q) == 0;
}
@@ -1015,12 +997,12 @@ meta_ignore(GNode *gn, const char *p)
}
if (metaIgnorePatterns) {
+ const char *expr;
char *pm;
Var_Set(".p.", p, gn);
- pm = Var_Subst(NULL,
- "${" MAKE_META_IGNORE_PATTERNS ":@m@${.p.:M$m}@}",
- gn, VARF_WANTRES);
+ expr = "${" MAKE_META_IGNORE_PATTERNS ":@m@${.p.:M$m}@}";
+ pm = Var_Subst(expr, gn, VARE_WANTRES);
if (*pm) {
#ifdef DEBUG_META_MODE
if (DEBUG(META))
@@ -1040,7 +1022,7 @@ meta_ignore(GNode *gn, const char *p)
snprintf(fname, sizeof(fname),
"${%s:L:${%s:ts:}}",
p, MAKE_META_IGNORE_FILTER);
- fm = Var_Subst(NULL, fname, gn, VARF_WANTRES);
+ fm = Var_Subst(fname, gn, VARE_WANTRES);
if (*fm == '\0') {
#ifdef DEBUG_META_MODE
if (DEBUG(META))
@@ -1122,7 +1104,7 @@ meta_oodate(GNode *gn, Boolean oodate)
goto oodate_out;
dname = fname3;
- missingFiles = Lst_Init(FALSE);
+ missingFiles = Lst_Init();
/*
* We need to check if the target is out-of-date. This includes
@@ -1147,7 +1129,7 @@ meta_oodate(GNode *gn, Boolean oodate)
int pid;
int x;
LstNode ln;
- struct stat fs;
+ struct make_stat mst;
if (!buf) {
bufsz = 8 * BUFSIZ;
@@ -1240,7 +1222,7 @@ meta_oodate(GNode *gn, Boolean oodate)
CHECK_VALID_META(p);
pid = atoi(p);
if (pid > 0 && pid != lastpid) {
- char *ldir;
+ const char *ldir;
char *tp;
if (lastpid > 0) {
@@ -1254,12 +1236,12 @@ meta_oodate(GNode *gn, Boolean oodate)
ldir = Var_Value(ldir_vname, VAR_GLOBAL, &tp);
if (ldir) {
strlcpy(latestdir, ldir, sizeof(latestdir));
- free(tp);
+ bmake_free(tp);
}
ldir = Var_Value(lcwd_vname, VAR_GLOBAL, &tp);
if (ldir) {
strlcpy(lcwd, ldir, sizeof(lcwd));
- free(tp);
+ bmake_free(tp);
}
}
/* Skip past the pid. */
@@ -1337,15 +1319,16 @@ meta_oodate(GNode *gn, Boolean oodate)
case 'D': /* unlink */
if (*p == '/' && !Lst_IsEmpty(missingFiles)) {
/* remove any missingFiles entries that match p */
- if ((ln = Lst_Find(missingFiles, p,
- path_match)) != NULL) {
+ ln = Lst_Find(missingFiles, path_match, p);
+ if (ln != NULL) {
LstNode nln;
char *tp;
do {
- nln = Lst_FindFrom(missingFiles, Lst_Succ(ln),
- p, path_match);
- tp = Lst_Datum(ln);
+ nln = Lst_FindFrom(missingFiles,
+ LstNode_Next(ln),
+ path_match, p);
+ tp = LstNode_Datum(ln);
Lst_Remove(missingFiles, ln);
free(tp);
} while ((ln = nln) != NULL);
@@ -1411,11 +1394,11 @@ meta_oodate(GNode *gn, Boolean oodate)
if ((strstr("tmp", p)))
break;
- if ((link_src != NULL && cached_lstat(p, &fs) < 0) ||
- (link_src == NULL && cached_stat(p, &fs) < 0)) {
+ if ((link_src != NULL && cached_lstat(p, &mst) < 0) ||
+ (link_src == NULL && cached_stat(p, &mst) < 0)) {
if (!meta_ignore(gn, p)) {
- if (Lst_Find(missingFiles, p, string_match) == NULL)
- Lst_AtEnd(missingFiles, bmake_strdup(p));
+ if (Lst_Find(missingFiles, string_match, p) == NULL)
+ Lst_Append(missingFiles, bmake_strdup(p));
}
}
break;
@@ -1475,7 +1458,7 @@ meta_oodate(GNode *gn, Boolean oodate)
if (DEBUG(META))
fprintf(debug_file, "%s: %d: looking for: %s\n", fname, lineno, *sdp);
#endif
- if (cached_stat(*sdp, &fs) == 0) {
+ if (cached_stat(*sdp, &mst) == 0) {
found = 1;
p = *sdp;
}
@@ -1485,12 +1468,12 @@ meta_oodate(GNode *gn, Boolean oodate)
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 (!S_ISDIR(mst.mst_mode) &&
+ mst.mst_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)) {
+ } else if (S_ISDIR(mst.mst_mode)) {
/* Update the latest directory. */
cached_realpath(p, latestdir);
}
@@ -1500,8 +1483,8 @@ meta_oodate(GNode *gn, Boolean oodate)
* A referenced file outside of CWD is missing.
* We cannot catch every eventuality here...
*/
- if (Lst_Find(missingFiles, p, string_match) == NULL)
- Lst_AtEnd(missingFiles, bmake_strdup(p));
+ if (Lst_Find(missingFiles, string_match, p) == NULL)
+ Lst_Append(missingFiles, bmake_strdup(p));
}
}
if (buf[0] == 'E') {
@@ -1524,7 +1507,7 @@ meta_oodate(GNode *gn, Boolean oodate)
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);
+ char *cmd = LstNode_Datum(ln);
Boolean hasOODATE = FALSE;
if (strstr(cmd, "$?"))
@@ -1539,7 +1522,7 @@ meta_oodate(GNode *gn, Boolean oodate)
if (DEBUG(META))
fprintf(debug_file, "%s: %d: cannot compare command using .OODATE\n", fname, lineno);
}
- cmd = Var_Subst(NULL, cmd, gn, VARF_WANTRES|VARF_UNDEFERR);
+ cmd = Var_Subst(cmd, gn, VARE_WANTRES|VARE_UNDEFERR);
if ((cp = strchr(cmd, '\n'))) {
int n;
@@ -1576,7 +1559,7 @@ meta_oodate(GNode *gn, Boolean oodate)
oodate = TRUE;
}
free(cmd);
- ln = Lst_Succ(ln);
+ ln = LstNode_Next(ln);
}
} else if (strcmp(buf, "CWD") == 0) {
/*
@@ -1601,7 +1584,7 @@ meta_oodate(GNode *gn, Boolean oodate)
if (!Lst_IsEmpty(missingFiles)) {
if (DEBUG(META))
fprintf(debug_file, "%s: missing files: %s...\n",
- fname, (char *)Lst_Datum(Lst_First(missingFiles)));
+ fname, (char *)LstNode_Datum(Lst_First(missingFiles)));
oodate = TRUE;
}
if (!oodate && !have_filemon && filemonMissing) {
@@ -1628,7 +1611,7 @@ meta_oodate(GNode *gn, Boolean oodate)
}
}
- Lst_Destroy(missingFiles, (FreeProc *)free);
+ Lst_Destroy(missingFiles, free);
if (oodate && needOODATE) {
/*
@@ -1638,12 +1621,12 @@ meta_oodate(GNode *gn, Boolean oodate)
*/
Var_Delete(OODATE, gn);
Var_Set(OODATE, Var_Value(ALLSRC, gn, &cp), gn);
- free(cp);
+ bmake_free(cp);
}
oodate_out:
for (i--; i >= 0; i--) {
- free(pa[i]);
+ bmake_free(pa[i]);
}
return oodate;
}
diff --git a/contrib/bmake/metachar.c b/contrib/bmake/metachar.c
index 49603383e660..971962272a4a 100644
--- a/contrib/bmake/metachar.c
+++ b/contrib/bmake/metachar.c
@@ -1,4 +1,4 @@
-/* $NetBSD: metachar.c,v 1.5 2015/06/19 08:03:35 mlelstv Exp $ */
+/* $NetBSD: metachar.c,v 1.6 2020/08/03 20:43:41 rillig Exp $ */
/*-
* Copyright (c) 2015 The NetBSD Foundation, Inc.
@@ -38,7 +38,7 @@
#endif
#if defined(__RCSID) && !defined(lint)
-__RCSID("$NetBSD: metachar.c,v 1.5 2015/06/19 08:03:35 mlelstv Exp $");
+__RCSID("$NetBSD: metachar.c,v 1.6 2020/08/03 20:43:41 rillig Exp $");
#endif
#include "metachar.h"
@@ -52,37 +52,37 @@ __RCSID("$NetBSD: metachar.c,v 1.5 2015/06/19 08:03:35 mlelstv Exp $");
*/
unsigned char _metachar[128] = {
-// nul soh stx etx eot enq ack bel
+/* nul soh stx etx eot enq ack bel */
1, 0, 0, 0, 0, 0, 0, 0,
-// bs ht nl vt np cr so si
+/* bs ht nl vt np cr so si */
0, 0, 1, 0, 0, 0, 0, 0,
-// dle dc1 dc2 dc3 dc4 nak syn etb
+/* dle dc1 dc2 dc3 dc4 nak syn etb */
0, 0, 0, 0, 0, 0, 0, 0,
-// can em sub esc fs gs rs us
+/* can em sub esc fs gs rs us */
0, 0, 0, 0, 0, 0, 0, 0,
-// sp ! " # $ % & '
+/* sp ! " # $ % & ' */
0, 1, 1, 1, 1, 0, 1, 1,
-// ( ) * + , - . /
+/* ( ) * + , - . / */
1, 1, 1, 0, 0, 0, 0, 0,
-// 0 1 2 3 4 5 6 7
+/* 0 1 2 3 4 5 6 7 */
0, 0, 0, 0, 0, 0, 0, 0,
-// 8 9 : ; < = > ?
+/* 8 9 : ; < = > ? */
0, 0, 0, 1, 1, 0, 1, 1,
-// @ A B C D E F G
+/* @ A B C D E F G */
0, 0, 0, 0, 0, 0, 0, 0,
-// H I J K L M N O
+/* H I J K L M N O */
0, 0, 0, 0, 0, 0, 0, 0,
-// P Q R S T U V W
+/* P Q R S T U V W */
0, 0, 0, 0, 0, 0, 0, 0,
-// X Y Z [ \ ] ^ _
+/* X Y Z [ \ ] ^ _ */
0, 0, 0, 1, 1, 1, 1, 0,
-// ` a b c d e f g
+/* ` a b c d e f g */
1, 0, 0, 0, 0, 0, 0, 0,
-// h i j k l m n o
+/* h i j k l m n o */
0, 0, 0, 0, 0, 0, 0, 0,
-// p q r s t u v w
+/* p q r s t u v w */
0, 0, 0, 0, 0, 0, 0, 0,
-// x y z { | } ~ del
+/* x y z { | } ~ del */
0, 0, 0, 1, 1, 1, 1, 0,
};
diff --git a/contrib/bmake/metachar.h b/contrib/bmake/metachar.h
index db88d671067e..98408ab3fb58 100644
--- a/contrib/bmake/metachar.h
+++ b/contrib/bmake/metachar.h
@@ -1,4 +1,4 @@
-/* $NetBSD: metachar.h,v 1.4 2015/06/21 20:26:02 christos Exp $ */
+/* $NetBSD: metachar.h,v 1.7 2020/08/25 17:37:09 rillig Exp $ */
/*-
* Copyright (c) 2015 The NetBSD Foundation, Inc.
@@ -28,25 +28,16 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef _METACHAR_H
-#define _METACHAR_H
+#ifndef MAKE_METACHAR_H
+#define MAKE_METACHAR_H
-#include <ctype.h>
+#include "make.h"
extern unsigned char _metachar[];
#define ismeta(c) _metachar[(c) & 0x7f]
-static inline int
-hasmeta(const char *cmd)
-{
- while (!ismeta(*cmd))
- cmd++;
-
- return *cmd != '\0';
-}
-
-static inline int
+static inline int MAKE_ATTR_UNUSED
needshell(const char *cmd, int white)
{
while (!ismeta(*cmd) && *cmd != ':' && *cmd != '=') {
@@ -58,4 +49,4 @@ needshell(const char *cmd, int white)
return *cmd != '\0';
}
-#endif /* _METACHAR_H */
+#endif /* MAKE_METACHAR_H */
diff --git a/contrib/bmake/mk/ChangeLog b/contrib/bmake/mk/ChangeLog
index ca42bc2839c5..9ea9d42a7776 100644
--- a/contrib/bmake/mk/ChangeLog
+++ b/contrib/bmake/mk/ChangeLog
@@ -1,3 +1,106 @@
+2020-08-26 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * dirdeps.mk: ensure we cannot confuse a static cache for dynamic
+ (even more rare that use of static cache is playing clever tricks
+ with it)
+
+2020-08-16 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * dirdeps-cache-update.mk: allow
+ MK_STATIC_DIRDEPS_CACHE_UPDATE_IMMEDIATE to control when we
+ actually update STATIC_DIRDEPS_CACHE.
+
+ * stage-install.sh: create dest directory if needed
+ before running install(1)
+
+2020-08-10 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * dirdeps-targets.mk: include Makefile.dirdeps.options
+
+ * dirdeps.mk: use _TARGETS if defined for DIRDEPS_CACHE
+
+2020-08-09 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * dirdeps.mk: default BUILD_DIRDEPS_MAKEFILE to empty
+
+ * dirdeps-cache-update.mk: building parallel cache update
+ under the context of dirdeps-cached would be ideal, but
+ is problematic, so it runs as a sibling.
+ Use cache-built target to ensure we wait for it to complete if
+ necessary.
+
+2020-08-06 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20200806
+
+ * dirdeps-options: allow TARGET_SPEC to affect option values.
+ Use DIRDEPS_OPTIONS_QUALIFIER_LIST before using bare MK_*
+
+ * dirdeps-targets.mk: check for MK_STATIC_DIRDEPS_CACHE defined
+ before looking for STATIC_DIRDEPS_CACHE
+
+2020-08-05 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * host-target.mk: Darwin use MACHINE for HOST_ARCH too
+
+ * dirdeps-options.mk: improve debug output
+
+2020-07-22 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * dirdeps.mk: set and export DYNAMIC_DIRDEPS_CACHE
+ for use by dirdeps-cache-update.mk
+
+ * dirdeps-targets.mk: set and export STATIC_DIRDEPS_CACHE
+ for use by dirdeps-cache-update.mk even if we don't use it.
+
+ * dirdeps-cache-update.mk: we only need worry about the background
+ update case, with the above, the update from DIRDEPS_CACHE is
+ simple.
+
+ * meta2deps.py: R 1234 . is not interesting
+
+2020-07-20 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * sys.mk: default MK_STATIC_DIRDEPS_CACHE from MK_DIRDEPS_CACHE
+
+ * dirdeps-options.mk: do not :tu DIRDEPS_OPTIONS
+ allows use of lower case for pseudo options.
+
+ * dirdeps-cache-update.mk: magic to deal with STATIC_DIRDEPS_CACHE
+
+2020-07-18 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * dirdeps-targets.mk: Look for Makefile.dirdeps.cache
+ which allows us to have a static cache for expensive targets.
+ Use -DWITHOUT_STATIC_DIRDEPS_CACHE -DWITH_DIRDEPS_CACHE
+ to regenerate the dirdeps.cache it is a copy of.
+
+2020-07-17 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * Get rid of BUILD_AT_LEVEL0, MK_DIRDEPS_BUILD makes more sense.
+
+2020-07-16 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * dirdeps.mk (DIRDEP_LOADAVG_REPORT): make it easy to record
+ load averages at intervals during build.
+
+2020-07-15 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20200715
+
+ * dirdeps.mk: tweak Checking line to make matching Finished
+ lines for post-build analysis easier.
+
+ * meta.autodep.mk: use !defined(WITHOUT_META_STATS)
+
+ * progs.mk: avoid prog.mk outputting multiple Finished lines
+
+2020-07-11 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * dirdeps.mk: further optimize dirdeps.cache
+ generate a DIRDEPS.${.TARGET} list for other purposes
+ and improve the layout.
+
2020-07-10 Simon J Gerraty <sjg@beast.crufty.net>
* dirdeps.mk: optimize content of dirdeps.cache
@@ -124,7 +227,7 @@
* install-mk (MK_VERSION): 20180919
* dirdeps-options.mk: .undef cannot handle var that expands to
- more than one var.
+ more than one var.
2018-07-08 Simon J Gerraty <sjg@beast.crufty.net>
@@ -309,8 +412,8 @@
2016-12-12 Simon J. Gerraty <sjg@bad.crufty.net>
* install-mk (MK_VERSION): 20161212
-
- * meta2deps.py: set pid_cwd[pid] when we process 'C'hdir,
+
+ * meta2deps.py: set pid_cwd[pid] when we process 'C'hdir,
rather than when we detect pid change.
2016-12-07 Simon J. Gerraty <sjg@bad.crufty.net>
@@ -322,7 +425,7 @@
to use foo.tgz to reference the latest staged version - so we
make foo.tgz a symlink to it.
Using a target to do both operations ensures we stay in sync.
-
+
2016-11-26 Simon J. Gerraty <sjg@bad.crufty.net>
* install-mk (MK_VERSION): 20161126
@@ -374,18 +477,18 @@
2016-08-13 Simon J. Gerraty <sjg@bad.crufty.net>
- * meta.sys.mk (.MAKE.META.IGNORE_PATHS):
+ * meta.sys.mk (.MAKE.META.IGNORE_PATHS):
in meta mode we can ignore the mtime of makefiles
2016-08-02 Simon J. Gerraty <sjg@bad.crufty.net>
* install-mk (MK_VERSION): 20160802
-
+
* lib.mk (libinstall): depends on beforinstall
* prog.mk (proginstall): depends on beforinstall
patch from Lauri Tirkkonen
-
+
* dirdeps.mk (bootstrap): When bootstrapping; creat
.MAKE.DEPENDFILE_DEFAULT and allow additional filtering via
.MAKE.DEPENDFILE_BOOTSTRAP_SED
@@ -408,7 +511,7 @@
* install-mk (MK_VERSION): 20160530
* meta.stage.mk: we assume ${CLEANFILES} gets .NOPATH
make it so.
-
+
2016-05-12 Simon J. Gerraty <sjg@bad.crufty.net>
* install-mk (MK_VERSION): 20160512
@@ -418,7 +521,7 @@
skip INCLUDES_* for staged libs unless SRC_* defined.
* own.mk: add INCLUDEDIR
-
+
2016-04-18 Simon J. Gerraty <sjg@bad.crufty.net>
* dirdeps.mk: when doing -f dirdeps.mk if target suppies no
@@ -428,9 +531,9 @@
2016-04-07 Simon J. Gerraty <sjg@bad.crufty.net>
* meta.autodep.mk: issue a warning if UPDATE_DEPENDFILE=NO due to
- NO_FILEMON_COOKIE
+ NO_FILEMON_COOKIE
- * dirdeps.mk: move the logic that allows for
+ * dirdeps.mk: move the logic that allows for
make -f dirdeps.mk some/dir.${TARGET_SPEC}
inside the check for !target(_DIRDEP_USE)
@@ -438,15 +541,15 @@
* Use <> when including local*.mk and others which may exist
elsewhere so that user can better control what they get.
-
- * meta.autodep.mk (NO_FILEMON_COOKIE):
+
+ * meta.autodep.mk (NO_FILEMON_COOKIE):
create a cookie if we ever build dir with nofilemon
so that UPDATE_DEPENDFILE will be forced to NO until cleaned.
2016-04-01 Simon J. Gerraty <sjg@bad.crufty.net>
* install-mk (MK_VERSION): 20160401
-
+
* meta2deps.py: fix old print statement when debugging.
* gendirdeps.mk: META2DEPS_CMD append M2D_EXCLUDES with -X
@@ -455,12 +558,12 @@
2016-03-22 Simon J. Gerraty <sjg@bad.crufty.net>
* install-mk (MK_VERSION): 20160317 (St. Pats)
-
+
* warnings.mk: g++ does not like -Wimplicit
-
+
* sys.mk sys/*.mk lib.mk prog.mk: use CXX_SUFFIXES to handle the
pelthora of common suffixes for C++
-
+
* lib.mk: use .So for shared objects
2016-03-15 Simon J. Gerraty <sjg@bad.crufty.net>
@@ -469,9 +572,9 @@
* meta.stage.mk (LN_CP_SCRIPT): do not ln(1) if we have to chmod(1)
normally only applies to scripts.
-
+
* dirdeps.mk: NO_DIRDEPS_BELOW to supress DIRDEPS below RELDIR as
- well as outside it.
+ well as outside it.
2016-03-10 Simon J. Gerraty <sjg@bad.crufty.net>
@@ -509,17 +612,17 @@
we cannot use the '$$' trick, but .export-literal does the job
we need.
* auto.dep.mk: make use .dinclude if we can.
-
+
2016-02-05 Simon J. Gerraty <sjg@bad.crufty.net>
- * dirdeps.mk:
+ * dirdeps.mk:
Add _build_all_dirs such that local.dirdeps.mk can
add fully qualified dirs to it.
- These will be built normally but the current
+ These will be built normally but the current
DEP_RELDIR will not depend on then (to avoid cycles).
This makes it easy to hook things like unit-tests into build.
-
+
2016-01-21 Simon J. Gerraty <sjg@bad.crufty.net>
@@ -541,7 +644,7 @@
to set MK_AUTO_OBJ and MK_DIRDEPS_BUILD
include local.sys.env.mk early
include local.sys.mk later
-
+
* own.mk (OPTIONS_DEFAULT_NO): AUTO_OBJ etc moved to sys.mk
2015-11-13 Simon J. Gerraty <sjg@bad.crufty.net>
@@ -560,8 +663,8 @@
2015-10-20 Simon J. Gerraty <sjg@bad.crufty.net>
* install-mk (MK_VERSION): 20151020
-
- * dirdeps.mk: Add logic for
+
+ * dirdeps.mk: Add logic for
make -f dirdeps.mk some/dir.${TARGET_SPEC}
2015-10-14 Simon J. Gerraty <sjg@bad.crufty.net>
@@ -587,16 +690,16 @@
2015-06-15 Simon J. Gerraty <sjg@bad.crufty.net>
* install-mk (MK_VERSION): 20150615
-
+
* auto.obj.mk: allow use of MAKEOBJDIRPREFIX too.
Follow make's normal precedence rules.
-
+
* gendirdeps.mk: allow customization of the header.
- eg. for FreeBSD:
+ eg. for FreeBSD:
GENDIRDEPS_HEADER= echo '\# ${FreeBSD:L:@v@$$$v$$ @:M*F*}';
* meta.autodep.mk: ignore dirdeps.cache*
-
+
* meta.stage.mk: when bootstrapping options it can be handy to
throw warnings rather than errors for staging conflicts.
@@ -605,11 +708,11 @@
2015-06-06 Simon J. Gerraty <sjg@bad.crufty.net>
* install-mk (MK_VERSION): 20150606
-
+
* dirdeps.mk: don't rely on manually maintained Makefile.depend
to set DEP_RELDIR and reset DIRDEPS.
By setting DEP_RELDIR ourselves we can skip :tA
-
+
* gendirdeps.mk: skip setting DEP_RELDIR.
2015-05-24 Simon J. Gerraty <sjg@bad.crufty.net>
@@ -626,19 +729,19 @@
* meta.stage.mk: for STAGE_AS_* basename of file may not be unique
so first use absolute path as key.
- Also skip staging at level 0.
+ Also skip staging at level 0.
2015-04-30 Simon J. Gerraty <sjg@bad.crufty.net>
* install-mk (MK_VERSION): 20150430
-
+
* dirdeps.mk: fix _count_dirdeps for non-cache case.
2015-04-16 Simon J. Gerraty <sjg@bad.crufty.net>
* install-mk (MK_VERSION): 20150411
bump version
-
+
* own.mk: put AUTO_OBJ in OPTIONS_DEFAULT_NO rather than YES.
it is here mainly for documentation purposes, since
if using auto.obj.mk it is better done via sys.mk
@@ -646,21 +749,21 @@
2015-04-01 Simon J. Gerraty <sjg@bad.crufty.net>
* install-mk (MK_VERSION): 20150401
-
+
* meta2deps.sh: support @list
-
+
* meta2deps.py: updates from Juniper
- o add EXCLUDES
+ o add EXCLUDES
o skip bogus input files.
o treat 'M' and 'L' as both an 'R' and a 'W'
2015-03-03 Simon J. Gerraty <sjg@bad.crufty.net>
* install-mk (MK_VERSION): 20150303
-
+
* dirdeps.mk: if MK_DIRDEPS_CACHE is yes, use dirdeps-cache
which is built via sub-make so we have a .meta file to tell if
- it is out-of-date.
+ it is out-of-date.
The dirdeps-cache contains the same dependency rules that we
normaly construct on the fly.
This adds a few seconds overhead when the cache is out of date,
@@ -669,15 +772,15 @@
2014-11-18 Simon J. Gerraty <sjg@bad.crufty.net>
* install-mk (MK_VERSION): 20141118
-
+
* meta.stage.mk: add stale_staged
-
+
* dirdeps.mk (_DIRDEP_USE_LEVEL): allow this to be tweaked
only useful under very rare conditions such as
FreeBSD's make universe.
* auto.obj.mk: Allow MK_AUTO_OBJ to set MKOBJDIRS=auto
-
+
2014-11-11 Simon J. Gerraty <sjg@bad.crufty.net>
* install-mk (MK_VERSION): 20141111
@@ -695,24 +798,24 @@
for GENDIRDEPS_FILTER to avoid surprises.
2014-10-10 Simon J. Gerraty <sjg@bad.crufty.net>
-
+
* dirdeps.mk (NSkipHostDir): this needs SRCTOP prepended since by
the time it is applied to __depdirs they have.
-
+
* dirdeps.mk fix filtering of _machines since M_dep_qual_fixes
expects patterns like *.${MACHINE}
-
+
* cython.mk (pyprefix?): use pyprefix to find python bits
since prefix might be something else (where we install our
stuff)
-
+
2014-09-11 Simon J. Gerraty <sjg@bad.crufty.net>
* install-mk (MK_VERSION): 20140911
-
+
* dirdeps.mk: add bootstrap target to simplify adding support for
new MACHINE.
-
+
2014-09-01 Simon J. Gerraty <sjg@bad.crufty.net>
* gendirdeps.mk: Add handling of GENDIRDEPS_FILTER_DIR_VARS and
@@ -722,7 +825,7 @@
2014-08-28 Simon J. Gerraty <sjg@bad.crufty.net>
* install-mk (MK_VERSION): 20140828
-
+
* cython.mk: capture logic for building python extension modules
with Cython.
@@ -735,8 +838,8 @@
* install-mk (MK_VERSION): 20140801
* dep.mk: use explicit MKDEP_MK rather than overload MKDEP to
- identify the autodep.mk variant.
-
+ identify the autodep.mk variant.
+
* sys.dependfile.mk: delete .MAKE.DEPENDFILE if its
initial value does not match .MAKE.DEPENDFILE_PREFIX
@@ -774,7 +877,7 @@
build).
* dirdeps.mk (__depdirs): ensure // don't sneak in
-
+
* gendirdeps.mk (DIRDEPS): ensure // don't sneak in
@@ -794,7 +897,7 @@
2014-02-09 Simon J. Gerraty <sjg@bad.crufty.net>
- * options.mk: cleanup and simplify semanitcs
+ * options.mk: cleanup and simplify semanitcs
NO_* dominates all, if both WITH_* and WITHOUT_*
are defined then result is DOMINATE_* which defaults to "no".
Ie. WITHOUT_ normally wins.
@@ -815,7 +918,7 @@
as _build_dirs.
Also fix the filtering of Makefile.depend files - for reporting
what we are looking for (M_dep_qual_fixes can get confused by
- Makefile.depend)
+ Makefile.depend)
Add some more debug info.
2013-09-04 Simon J. Gerraty <sjg@bad.crufty.net>
@@ -828,7 +931,7 @@
* install-mk (MK_VERSION): 20130801
* libs.mk: update to match progs.mk
-
+
2013-07-26 Simon J. Gerraty <sjg@bad.crufty.net>
* install-mk (MK_VERSION): 20130726
@@ -837,13 +940,13 @@
errors
also allow @file to provide huge list of .meta files.
* meta2deps.py: add try_parse() to cleanup the above.
-
+
2013-07-16 Simon J. Gerraty <sjg@bad.crufty.net>
* install-mk (MK_VERSION): 20130716
* own.mk: add GPROG as an option
* prog.mk: honor MK_GPROF==yes
-
+
2013-05-10 Simon J. Gerraty <sjg@bad.crufty.net>
* install-mk (MK_VERSION): 20130505
@@ -878,7 +981,7 @@
* meta.stage.mk (LN_CP_SCRIPT): Add LnCp to do the ln||cp dance
consistently.
* dirdeps.mk: better describe the dance in sys.mk for TARGET_SPEC.
-
+
2013-03-18 Simon J. Gerraty <sjg@bad.crufty.net>
* gendirdeps.mk: revert the dance around .MAKE.DEPENDFILE_DEFAULT
@@ -893,7 +996,7 @@
* gendirdeps.mk: ensure _objroot has trailing / if it needs it.
* meta2deps.py: if machine is "host", then also trim
self.host_target from any OBJROOTS.
-
+
2013-03-11 Simon J. Gerraty <sjg@bad.crufty.net>
@@ -909,7 +1012,7 @@
2013-03-07 Simon J. Gerraty <sjg@bad.crufty.net>
- * sys.dependfile.mk (.MAKE.DEPENDFILE_DEFAULT):
+ * sys.dependfile.mk (.MAKE.DEPENDFILE_DEFAULT):
use a separate variable for the default .MAKE.DEPENDFILE value
so that it can be controlled independently of
.MAKE.DEPENDFILE_PREFERENCE
@@ -926,12 +1029,12 @@
2013-02-10 Simon J. Gerraty <sjg@bad.crufty.net>
* install-mk (MK_VERSION): bump version to 20130210
- * import latest dirdeps.mk, gendirdeps.mk and meta2deps.py
- from Juniper.
+ * import latest dirdeps.mk, gendirdeps.mk and meta2deps.py
+ from Juniper.
o dirdeps.mk now fully supports TARGET_SPEC consisting of more
than just MACHINE.
o no longer use DEP_MACHINE from Makefile.depend* so remove it.
-
+
2013-01-23 Simon J. Gerraty <sjg@bad.crufty.net>
* install-mk (MK_VERSION): bump version to 20130123
@@ -976,13 +1079,13 @@
* progs.mk: add MAN and CXXFLAGS to PROG_VARS
also add PROGS_TARGETS and pass on PROG_CXX if it seems
appropriate.
-
+
2012-11-04 Simon J. Gerraty <sjg@bad.crufty.net>
* meta.stage.mk: update CLEANFILES
remove redundant cp of .dirdep from STAGE_AS_SCRIPT.
* progs.mk: Add LDADD to PROG_VARS
-
+
2012-10-12 Simon J. Gerraty <sjg@bad.crufty.net>
* meta.stage.mk (STAGE_DIR_FILTER): track dirs we stage to in
@@ -1004,11 +1107,11 @@
* install-mk (MK_VERSION): bump version to 20120711
* dep.mk: add explicit dependencies on SRCS after applying
- SRCS_DEP_FILTER
+ SRCS_DEP_FILTER
* meta.autodep.mk: add explicit dependencies on SRCS after
applying SRCS_DEP_FILTER
* meta.autodep.mk: ensure GENDIRDEPS_FILTER is exported if needed.
-
+
2012-06-26 Simon J. Gerraty <sjg@bad.crufty.net>
* install-mk (MK_VERSION): bump version to 20120626
@@ -1020,14 +1123,14 @@
* gendirdeps.mk: only produce unqualified deps if no
.MAKE.DEPENDFILE_PREFERENCE ends in .${MACHINE}
* meta.subdir.mk: apply SUBDIRDEPS_FILTER
-
+
2012-04-20 Simon J. Gerraty <sjg@bad.crufty.net>
* install-mk (MK_VERSION): bump version to 20120420
* add sys.dependfile.mk so we can experiment with
- .MAKE.DEPENDFILE_PREFERENCE
+ .MAKE.DEPENDFILE_PREFERENCE
* meta.autodep.mk: _DEPENDFILE is precious!
-
+
2012-03-15 Simon J. Gerraty <sjg@bad.crufty.net>
* install-mk (MK_VERSION): bump version to 20120315
@@ -1068,37 +1171,37 @@
o meta2deps.py add a clear 'ERROR:' token if an exception is raised.
o gendirdeps.mk if ERROR: from meta2deps.py do not update
anything.
-
+
2011-10-30 Simon J. Gerraty <sjg@bad.crufty.net>
* install-new.mk separate the cmp and copy logic to its own function.
-
+
2011-10-28 Simon J. Gerraty <sjg@bad.crufty.net>
* install-mk (MK_VERSION): bump version to 20111028
* sys.mk: include auto.obj.mk if MKOBJDIRS is set to auto
* subdir.mk: ensure _SUBDIRUSE is provided
- * meta.autodep.mk: remove dependency of gendirdeps.mk on auto.obj.mk
+ * meta.autodep.mk: remove dependency of gendirdeps.mk on auto.obj.mk
* meta.subdir.mk: always allow for Makefile.depend
-
+
2011-10-10 Simon J. Gerraty <sjg@bad.crufty.net>
* install-mk (MK_VERSION): bump version to 20111010
o minor tweak to *dirdeps.mk from Juniper sjg@
-
+
2011-10-01 Simon J. Gerraty <sjg@bad.crufty.net>
* install-mk (MK_VERSION): bump version to 20111001
o add meta2deps.py from Juniper sjg@
o tweak gendirdeps.mk to work with meta2deps.py when not
- cross-building
- * autoconf.mk: add autoconf-input as a hook for regenerating
+ cross-building
+ * autoconf.mk: add autoconf-input as a hook for regenerating
AUTOCONF_INPUTS (configure).
2011-08-24 Simon J. Gerraty <sjg@bad.crufty.net>
* meta.autodep.mk: if we do not have OBJS, .depend isn't a useful
- trigger for updating Makefile.depend*
+ trigger for updating Makefile.depend*
2011-08-08 Simon J. Gerraty <sjg@bad.crufty.net>
@@ -1155,7 +1258,7 @@
2011-04-03 Simon J. Gerraty <sjg@bad.crufty.net>
* rst2htm.mk: convert rst to s5 (slides) or plain html depending
- on target name.
+ on target name.
2011-03-30 Simon J. Gerraty <sjg@bad.crufty.net>
@@ -1167,16 +1270,16 @@
can be used to debug level 0 only and DEBUG_MAKE_FLAGS for the rest.
* sys.mk: re-define M_whence in terms of M_type.
M_type is useful for checking if something is a builtin.
-
+
2011-03-16 Simon J. Gerraty <sjg@bad.crufty.net>
* meta.stage.mk: add stage_symlinks and leverage StageLinks for
- stage_libs
+ stage_libs
2011-03-10 Simon J. Gerraty <sjg@bad.crufty.net>
* dirdeps.mk: correct value for _depdir_files depends on
- .MAKE.DEPENDFILE
+ .MAKE.DEPENDFILE
Add our copyright - just to make it clear we have frobbed this
quite a bit.
DEP_MACHINE needs to be set to MACHINE each time, if using only
@@ -1216,7 +1319,7 @@
2010-09-24 Simon J. Gerraty <sjg@bad.crufty.net>
* install-mk (MK_VERSION): bump version to 20100919
- include dirdeps.mk et al from Juniper Networks,
+ include dirdeps.mk et al from Juniper Networks,
for meta mode - requires filemon(9).
* sys.mk, subdir.mk: Add hooks for meta mode.
we do this as meta.sys.mk, meta.autodep.mk and meta.subdir.mk
@@ -1282,7 +1385,7 @@
* install-mk (MK_VERSION): bump version to 20100420
* sys/NetBSD.mk: add MACHINE_CPU to keep netbsd makefiles happy
* autoconf.mk allow AUTO_AUTOCONF
-
+
2010-04-19 Simon J. Gerraty <sjg@bad.crufty.net>
* obj.mk: add objwarn to keep freebsd makefiles happy
@@ -1294,7 +1397,7 @@
* auto.dep.mk: add some explanation of how/what we do.
* autodep.mk: skip the .OPTIONAL frobbing of .depend
bmake's FROM_DEPEND flag makes it redundant.
-
+
2010-04-13 Simon J. Gerraty <sjg@bad.crufty.net>
* install-mk (MK_VERSION): bump version to 20100404
@@ -1335,7 +1438,7 @@
* sys.mk,libnames.mk add .-include <local.*>
this allows local customization without the need to edit the
- distributed files.
+ distributed files.
2009-12-14 Simon J. Gerraty <sjg@void.crufty.net>
@@ -1361,7 +1464,7 @@
2009-11-17 Simon J. Gerraty <sjg@void.crufty.net>
* install-mk (MK_VERSION): bump version
- * host-target.mk: only export the expensive stuff
+ * host-target.mk: only export the expensive stuff
* Generic.sys.mk (sys_mk): for SunOS we need to look for
${HOST_OS}.${HOST_OSMAJOR} too!
@@ -1411,7 +1514,7 @@
* install-mk (MK_VERSION): bump version
* general cleanup
- * dpadd.mk introduce DPMAGIC_LIBS_*
+ * dpadd.mk introduce DPMAGIC_LIBS_*
2007-04-30 Simon J. Gerraty <sjg@void.crufty.net>
@@ -1453,10 +1556,10 @@
* install-mk (MK_VERSION): bump version to 20061126
* warnings.mk: detect invalid WARNINGS_SET
-
+
* warnings.mk: use ${.TARGET:T:R}.o when looking for target
- specific warnings.
-
+ specific warnings.
+
* For .cc sources, turn off warnings that g++ vomits on.
2006-11-08 Simon J. Gerraty <sjg@void.crufty.net>
@@ -1483,11 +1586,11 @@
2006-03-01 Simon J. Gerraty <sjg@void.crufty.net>
* install-mk (MK_VERSION): bump version to 20060301
- * autodep.mk (.depend):
+ * autodep.mk (.depend):
if MAKE_VERSION is newer than 20050530 we can make .END depend on
.depend and make .depend depend on __depsrcs that exist.
* dpadd.mk: add SRC_PATHADD
-
+
2005-11-04 Simon J. Gerraty <sjg@void.crufty.net>
* install-mk (MK_VERSION): bump version to 20051104
@@ -1512,7 +1615,7 @@
2004-02-15 Simon J. Gerraty <sjg@void.crufty.net>
* own.mk: don't use NetBSD's _SRC_TOP_ it can
- cause confusion. Also don't take just 'mk' as a
+ cause confusion. Also don't take just 'mk' as a
srctop indicator.
2004-02-14 Simon J. Gerraty <sjg@void.crufty.net>
@@ -1538,7 +1641,7 @@
* set OS and ROOT_GROUP for those that we know the value.
for others (eg. Generic.sys.mk) wrap the != in an .ifndef so
we don't do it again for each sub-make.
-
+
2003-09-28 Simon J. Gerraty <sjg@void.crufty.net>
* install-mk (MK_VERSION): 20030928
@@ -1554,8 +1657,8 @@
2003-07-31 Simon J. Gerraty <sjg@void.crufty.net>
- * install-mk: add ability to use cp -f when updating
- destination .mk files. Also now possible to play games with
+ * install-mk: add ability to use cp -f when updating
+ destination .mk files. Also now possible to play games with
FORCE_SYS_MK=ln etc on *BSD machines to link /usr/share/mk/sys.mk
into dest - not recommended unless you seriously want to.
@@ -1575,4 +1678,4 @@
* install-mk: Allow FORCE_SYS_MK to come from env
-
+
diff --git a/contrib/bmake/mk/FILES b/contrib/bmake/mk/FILES
index 360fb613a15e..51b1acd716b2 100644
--- a/contrib/bmake/mk/FILES
+++ b/contrib/bmake/mk/FILES
@@ -61,6 +61,7 @@ warnings.mk
whats.mk
yacc.mk
dirdeps.mk
+dirdeps-cache-update.mk
dirdeps-options.mk
dirdeps-targets.mk
gendirdeps.mk
diff --git a/contrib/bmake/mk/README b/contrib/bmake/mk/README
index 3d79b6a2de7d..161426cfcd0c 100644
--- a/contrib/bmake/mk/README
+++ b/contrib/bmake/mk/README
@@ -1,10 +1,10 @@
-# $Id: README,v 1.1 1997/03/11 07:27:15 sjg Exp $
+# $Id: README,v 1.2 2020/08/19 17:51:53 sjg Exp $
This directory contains some macro's derrived from the NetBSD bsd.*.mk
macros. They have the same names but without the bsd., separate macro
files are needed to ensure we can make them do what we want for
builing things outside of /usr/src. Nearly all the comments below
-apply.
+apply.
# $NetBSD: bsd.README,v 1.18 1997/01/13 00:54:23 mark Exp $
# @(#)bsd.README 5.1 (Berkeley) 5/11/90
@@ -347,9 +347,9 @@ If foo has multiple source files, add the line:
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
The include file <bsd.subdir.mk> contains the default targets for building
-subdirectories. It has the same eight targets as <bsd.prog.mk>: all,
+subdirectories. It has the same eight targets as <bsd.prog.mk>: all,
clean, cleandir, depend, includes, install, lint, and tags. For all of
-the directories listed in the variable SUBDIRS, the specified directory
+the directories listed in the variable SUBDIRS, the specified directory
will be visited and the target made. There is also a default target which
allows the command "make subdir" where subdir is any directory listed in
the variable SUBDIRS.
diff --git a/contrib/bmake/mk/auto.dep.mk b/contrib/bmake/mk/auto.dep.mk
index 55b2971be6e0..d905649ab206 100644
--- a/contrib/bmake/mk/auto.dep.mk
+++ b/contrib/bmake/mk/auto.dep.mk
@@ -1,16 +1,16 @@
#
# RCSid:
-# $Id: auto.dep.mk,v 1.5 2016/04/05 15:58:37 sjg Exp $
+# $Id: auto.dep.mk,v 1.6 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 2010, 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
@@ -20,11 +20,11 @@
# set MKDEP_MK=auto.dep.mk and dep.mk will include us
-# This version differs from autodep.mk, in that
+# This version differs from autodep.mk, in that
# we use ${.TARGET:T}.d rather than ${.TARGET:T:R}.d
# this makes it simpler to get the args to -MF and -MT right
# and ensure we can simply include all the .d files.
-#
+#
# However suffix rules do not work with something like .o.d so we
# don't even try to handle 'make depend' gracefully.
# dep.mk will handle that itself.
@@ -37,7 +37,7 @@ __${.PARSEFILE}__:
# set this to -MMD to ignore /usr/include
# actually it ignores <> so may not be a great idea
-CFLAGS_MD ?= -MD
+CFLAGS_MD ?= -MD
# -MF etc not available on all gcc versions.
CFLAGS_MF ?= -MF ${.TARGET:T}.d -MT ${.TARGET:T}
CFLAGS += ${CFLAGS_MD} ${CFLAGS_MF}
diff --git a/contrib/bmake/mk/auto.obj.mk b/contrib/bmake/mk/auto.obj.mk
index a12ea7a81318..0405b5e6441d 100644
--- a/contrib/bmake/mk/auto.obj.mk
+++ b/contrib/bmake/mk/auto.obj.mk
@@ -1,14 +1,14 @@
-# $Id: auto.obj.mk,v 1.15 2017/11/04 21:05:04 sjg Exp $
+# $Id: auto.obj.mk,v 1.16 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 2004, 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
@@ -16,7 +16,7 @@
ECHO_TRACE ?= echo
.ifndef Mkdirs
-# A race condition in some versions of mkdir, means that it can bail
+# A race condition in some versions of mkdir, means that it can bail
# if another process made a dir that mkdir expected to.
# We repeat the mkdir -p a number of times to try and work around this.
# We stop looping as soon as the dir exists.
@@ -53,7 +53,7 @@ __objdir:= ${__objdir}
# We need to chdir, make the directory if needed
.if !exists(${__objdir}/) && \
(${.TARGETS} == "" || ${.TARGETS:Nclean*:N*clean:Ndestroy*} != "")
-# This will actually make it...
+# This will actually make it...
__objdir_made != echo ${__objdir}/; umask ${OBJDIR_UMASK:U002}; \
${ECHO_TRACE} "[Creating objdir ${__objdir}...]" >&2; \
${Mkdirs}; Mkdirs ${__objdir}
diff --git a/contrib/bmake/mk/autoconf.mk b/contrib/bmake/mk/autoconf.mk
index 38f4ece48ad6..61e6978043a8 100644
--- a/contrib/bmake/mk/autoconf.mk
+++ b/contrib/bmake/mk/autoconf.mk
@@ -1,14 +1,14 @@
-# $Id: autoconf.mk,v 1.9 2017/08/13 20:03:13 sjg Exp $
+# $Id: autoconf.mk,v 1.10 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 1996-2009, 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
@@ -49,7 +49,7 @@ CLEANFILES+= config.recheck config.gen config.status *.meta
AUTOCONF ?= autoconf
AUTOHEADER ?= autoheader
-# expand it to a full path
+# expand it to a full path
AUTOCONF := ${AUTOCONF:${M_whence}}
.if exists(${AUTOCONF})
diff --git a/contrib/bmake/mk/autodep.mk b/contrib/bmake/mk/autodep.mk
index 7b5029f15728..a7bb942278c9 100644
--- a/contrib/bmake/mk/autodep.mk
+++ b/contrib/bmake/mk/autodep.mk
@@ -1,16 +1,16 @@
#
# RCSid:
-# $Id: autodep.mk,v 1.37 2020/04/17 21:08:17 sjg Exp $
+# $Id: autodep.mk,v 1.38 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 1999-2010, 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
@@ -51,7 +51,7 @@ ${s:T:R}.d: $s
.endfor
__depsrcs:=${__depsrcs:T:R:S/$/.d/g}
-# we also need to handle makefiles where the .d's from __depsrcs
+# we also need to handle makefiles where the .d's from __depsrcs
# don't match those from OBJS
# we avoid using := here, since the modifier applied to OBJS
# can cause trouble if there are any undefined vars in OBJS.
@@ -64,7 +64,7 @@ __dependsrcs= ${__dependsrcsx:O:u}
# set this to -MMD to ignore /usr/include
# actually it ignores <> so may not be a great idea
-CFLAGS_MD?=-MD
+CFLAGS_MD?=-MD
# -MF etc not available on all gcc versions.
# we "fix" the .o later
CFLAGS_MF?=-MF ${.TARGET:T:R}.d -MT ${.TARGET:T:R}.o
@@ -73,8 +73,8 @@ RM?= rm
MAKE_SHELL?= sh
# watch out for people who don't use CPPFLAGS
-CPPFLAGS_MD=${CFLAGS:M-[IUD]*} ${CPPFLAGS}
-CXXFLAGS_MD=${CXXFLAGS:M-[IUD]*} ${CPPFLAGS}
+CPPFLAGS_MD=${CFLAGS:M-[IUD]*} ${CPPFLAGS}
+CXXFLAGS_MD=${CXXFLAGS:M-[IUD]*} ${CPPFLAGS}
# just in case these need to be different
CC_MD?=${CC}
diff --git a/contrib/bmake/mk/compiler.mk b/contrib/bmake/mk/compiler.mk
index 40a57c081ab4..b20ecaa047e3 100644
--- a/contrib/bmake/mk/compiler.mk
+++ b/contrib/bmake/mk/compiler.mk
@@ -1,14 +1,14 @@
-# $Id: compiler.mk,v 1.6 2019/09/28 17:12:00 sjg Exp $
+# $Id: compiler.mk,v 1.7 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 2019, 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
diff --git a/contrib/bmake/mk/cython.mk b/contrib/bmake/mk/cython.mk
index c1318b2174e9..d3c229c7269a 100644
--- a/contrib/bmake/mk/cython.mk
+++ b/contrib/bmake/mk/cython.mk
@@ -1,15 +1,15 @@
# RCSid:
-# $Id: cython.mk,v 1.7 2018/03/25 18:46:11 sjg Exp $
+# $Id: cython.mk,v 1.8 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 2014, 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
diff --git a/contrib/bmake/mk/dirdeps-cache-update.mk b/contrib/bmake/mk/dirdeps-cache-update.mk
new file mode 100644
index 000000000000..eb992e936eb8
--- /dev/null
+++ b/contrib/bmake/mk/dirdeps-cache-update.mk
@@ -0,0 +1,179 @@
+# $Id: dirdeps-cache-update.mk,v 1.21 2020/08/19 17:51:53 sjg Exp $
+#
+# @(#) Copyright (c) 2020, 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 makefile deals with the updating of STATIC_DIRDEPS_CACHE.
+# Some targets are so huge that computing dirdeps takes a significant
+# amount of time. For such targets a STATIC_DIRDEPS_CACHE can make
+# sense.
+#
+# If the target is represented by targets/pseudo/production
+# it's normal DIRDEPS would be in
+# targets/pseudo/production/Makefile.depend
+# and STATIC_DIRDEPS_CACHE would be
+# targets/pseudo/production/Makefile.dirdeps.cache
+# which is simply initialized by copying dirdeps.cache.production
+# from $OBJTOP
+#
+# When dirdeps-targets.mk is initializing DIRDEPS it will look for
+# Makefile.dirdeps.cache and unless told not to
+# (MK_STATIC_DIRDEPS_CACHE=no) will use it as DIRDEPS_CACHE.
+#
+# If MK_STATIC_DIRDEPS_CACHE_UPDATE is "yes", then this makefile
+# comes into play.
+#
+# We usually get included from local.dirdeps.mk
+# as well as Makefile.depend of RELDIR with a static Makefile.dirdeps.cache
+#
+# If we see that STATIC_DIRDEPS_CACHE is in use, we need to hook a
+# cache-update target into the build to regenerate dirdeps.cache
+# in parallel with the rest of the build.
+# If MK_STATIC_DIRDEPS_CACHE_UPDATE_IMMEDIATE is "yes" we update
+# STATIC_DIRDEPS_CACHE as soon as the update is ready,
+# otherwise it will be done at the end of the build.
+#
+# If STATIC_DIRDEPS_CACHE is not in use, but a DIRDEPS_CACHE is,
+# then we need do nothing except export STATIC_DIRDEPS_CACHE and
+# DYNAMIC_DIRDEPS_CACHE for use when we are include during the visit
+# to the ultimate target (targets/pseudo/production).
+#
+# Regardless of which happens, when included at .MAKE.LEVEL > 0
+# for a target other than cache-update we simply copy
+# DYNAMIC_DIRDEPS_CACHE to STATIC_DIRDEPS_CACHE with some optional
+# filtering.
+#
+# If we are included for the target cache-update we take care of
+# running dirdeps.mk again to generate the DYNAMIC_DIRDEPS_CACHE.
+#
+
+.if !target(_${.PARSEFILE}_)
+_${.PARSEFILE}_: .NOTMAIN
+
+STATIC_CACHE_SED += \
+ -e '/Autogenerated/s,-.*,- edit with care!,' \
+ -e '/cache-update/d'
+
+STATIC_DIRDEPS_CACHE_UPDATE_SCRIPT ?= \
+ { echo Saving ${DYNAMIC_DIRDEPS_CACHE} as ${STATIC_DIRDEPS_CACHE}; \
+ sed ${STATIC_CACHE_SED} ${DYNAMIC_DIRDEPS_CACHE} > ${STATIC_DIRDEPS_CACHE}; }
+.endif
+
+.if ${MK_DIRDEPS_CACHE:Uno} == "yes"
+.if ${MK_STATIC_DIRDEPS_CACHE_UPDATE:Uno} == "yes"
+.if ${_debug_reldir:U0} || ${DEBUG_DIRDEPS:U:Mcache*} != ""
+_debug_cache = 1
+.else
+_debug_cache = 0
+.endif
+
+.if ${.MAKE.LEVEL} == 0 && !make(cache-update)
+
+.if ${_debug_cache}
+.info ${MK_STATIC_DIRDEPS_CACHE_UPDATE MK_STATIC_DIRDEPS_CACHE MK_DIRDEPS_CACHE DIRDEPS_CACHE STATIC_DIRDEPS_CACHE:L:@v@$v=${$v}@}
+.endif
+
+.if ${MK_STATIC_DIRDEPS_CACHE} == "yes" && defined(STATIC_DIRDEPS_CACHE) && exists(${STATIC_DIRDEPS_CACHE})
+.if !make(dirdeps)
+# We are using static cache and this is the only look we will get.
+# We want to generate an updated cache while we build
+# so need to hook cache-update to dirdeps now.
+# Note: we are running as a sibling to dirdeps-cached,
+# attempting to do this in that context is problematic.
+
+# One of these should exist - to actually kick off the cache generation
+.for d in ${STATIC_DIRDEPS_CACHE:H}/cache-update ${STATIC_DIRDEPS_CACHE:H:H}/cache-update ${STATIC_DIRDEPS_CACHE:H:H:H}/cache-update
+.if exists($d)
+cache_update_dirdep ?= $d.${TARGET_SPEC}
+.endif
+.endfor
+.if !target(${cache_update_dirdep})
+dirdeps: ${cache_update_dirdep}
+${cache_update_dirdep}: _DIRDEP_USE
+DYNAMIC_DIRDEPS_CACHE := ${OBJTOP}/dirdeps.cache.${STATIC_DIRDEPS_CACHE:H:T}-update
+.export DYNAMIC_DIRDEPS_CACHE STATIC_DIRDEPS_CACHE
+.endif
+.endif # make(dirdeps)
+.endif # MK_*
+
+.endif # .MAKE.LEVEL 0
+
+.if ${.MAKE.LEVEL} > 0 && ${.CURDIR:T} == "cache-update"
+# we are the background update shim
+
+.if ${_debug_cache}
+.info level ${.MAKE.LEVEL}: ${MK_DIRDEPS_CACHE DYNAMIC_DIRDEPS_CACHE STATIC_DIRDEPS_CACHE:L:@v@$v=${$v}@}
+.endif
+
+all: cache-build
+cache-build: .META
+ @set -x; MAKELEVEL=0 \
+ ${.MAKE} -C ${SRCTOP} -f ${RELDIR}/Makefile cache-update \
+ -DWITHOUT_STATIC_DIRDEPS_CACHE_UPDATE
+
+.endif # cache-update
+
+.elif ${.MAKE.LEVEL} == 0 && make(cache-update) && !target(cache-update)
+# we were invoked above
+# we just leverage dirdeps.mk
+BUILD_DIRDEPS_TARGETS := ${STATIC_DIRDEPS_CACHE:H:T}
+DIRDEPS := ${STATIC_DIRDEPS_CACHE:H:S,^${SRCTOP}/,,}.${TARGET_SPEC}
+DIRDEPS_CACHE := ${DYNAMIC_DIRDEPS_CACHE}
+
+.if ${DEBUG_DIRDEPS:U:Mcache*} != ""
+.info level 0: ${MK_DIRDEPS_CACHE DIRDEPS_CACHE DIRDEPS:L:@v@$v=${$v}@}
+.endif
+
+# so cache-built below can check on us
+x!= echo; echo ${.MAKE.PID} > ${DIRDEPS_CACHE}.new.pid
+
+cache-update: ${DIRDEPS_CACHE}
+ @rm -f ${DIRDEPS_CACHE}.new.pid
+.if ${MK_STATIC_DIRDEPS_CACHE_UPDATE_IMMEDIATE:Uno} == "yes"
+ ${STATIC_DIRDEPS_CACHE_UPDATE_SCRIPT}
+.endif
+
+all:
+
+.include <dirdeps.mk>
+
+.endif # MK_STATIC_DIRDEPS_CACHE_UPDATE
+.endif # MK_DIRDEPS_CACHE
+
+.if ${.MAKE.LEVEL} > 0 && ${MK_STATIC_DIRDEPS_CACHE_UPDATE:Uno} == "yes" && \
+ ${STATIC_DIRDEPS_CACHE:Uno:H} == "${SRCTOP}/${RELDIR}"
+.if !defined(DYNAMIC_DIRDEPS_CACHE)
+all:
+.else
+# This is the easy bit, time to save the cache
+
+all: cache-update
+
+# ensure the cache update is completed
+cache-built:
+ @test -s ${DYNAMIC_DIRDEPS_CACHE}.new || exit 0; \
+ pid=`cat ${DYNAMIC_DIRDEPS_CACHE}.new.pid 2> /dev/null`; \
+ test $${pid:-0} -gt 1 || exit 0; \
+ echo "Waiting for $$pid to finish ${DYNAMIC_DIRDEPS_CACHE} ..."; \
+ while 'kill' -0 $$pid; do sleep 30; done > /dev/null 2>&1
+
+cache-update: cache-built
+.if ${MK_STATIC_DIRDEPS_CACHE_UPDATE_IMMEDIATE:Uno} == "no"
+ @test ! -s ${DYNAMIC_DIRDEPS_CACHE} || \
+ ${STATIC_DIRDEPS_CACHE_UPDATE_SCRIPT}
+.endif
+
+.endif
+.endif
diff --git a/contrib/bmake/mk/dirdeps-options.mk b/contrib/bmake/mk/dirdeps-options.mk
index 4f74c02e1b8c..74f54a4cf665 100644
--- a/contrib/bmake/mk/dirdeps-options.mk
+++ b/contrib/bmake/mk/dirdeps-options.mk
@@ -1,6 +1,6 @@
-# $Id: dirdeps-options.mk,v 1.9 2018/09/20 00:07:19 sjg Exp $
+# $Id: dirdeps-options.mk,v 1.17 2020/08/07 01:57:38 sjg Exp $
#
-# @(#) Copyright (c) 2018, Simon J. Gerraty
+# @(#) Copyright (c) 2018-2020, Simon J. Gerraty
#
# This file is provided in the hope that it will
# be of use. There is absolutely NO WARRANTY.
@@ -37,6 +37,11 @@
# to whatever applies for that dir, or it can rely on globals
# set in local.dirdeps-options.mk
# Either way, we will .undef DIRDEPS.* when done.
+#
+# In some cases the value of MK_FOO might depend on TARGET_SPEC
+# so we qualify MK_FOO with .${TARGET_SPEC} and each component
+# TARGET_SPEC_VAR (in reverse order) before using MK_FOO.
+#
# This should have been set by Makefile.depend.options
# before including us
@@ -47,21 +52,43 @@ DIRDEPS_OPTIONS ?=
.if ${.MAKE.LEVEL} == 0
# :U below avoids potential errors when we :=
-.for o in ${DIRDEPS_OPTIONS:tu}
-DIRDEPS += ${DIRDEPS.$o.${MK_$o:U}:U}
+# some options can depend on TARGET_SPEC!
+DIRDEPS_OPTIONS_QUALIFIER_LIST ?= \
+ ${DEP_TARGET_SPEC:U${TARGET_SPEC}} \
+ ${TARGET_SPEC_VARSr:U${TARGET_SPEC_VARS}:@v@${DEP_$v:U${$v}}@}
+# note that we need to include $o in the variable _o$o
+# to ensure correct evaluation.
+.for o in ${DIRDEPS_OPTIONS}
+.undef _o$o _v$o
+.for x in ${DIRDEPS_OPTIONS_QUALIFIER_LIST}
+.if defined(MK_$o.$x)
+_o$o ?= MK_$o.$x
+_v$o ?= ${MK_$o.$x}
+.endif
+.endfor
+_v$o ?= ${MK_$o}
+.if ${_debug_reldir:U0}
+.info ${DEP_RELDIR:U${RELDIR}}.${DEP_TARGET_SPEC:U${TARGET_SPEC}}: o=$o ${_o$o:UMK_$o}=${_v$o:U} DIRDEPS += ${DIRDEPS.$o.${_v$o:U}:U}
+.endif
+DIRDEPS += ${DIRDEPS.$o.${_v$o:U}:U}
.endfor
DIRDEPS := ${DIRDEPS:O:u}
+.if ${_debug_reldir:U0}
+.info ${DEP_RELDIR:U${RELDIR}}: DIRDEPS=${DIRDEPS}
+.endif
# avoid cross contamination
-.for o in ${DIRDEPS_OPTIONS:tu}
+.for o in ${DIRDEPS_OPTIONS}
.undef DIRDEPS.$o.yes
.undef DIRDEPS.$o.no
+.undef _o$o
+.undef _v$o
.endfor
.else
# whether options are enabled or not,
# we want to filter out the relevant DIRDEPS.*
# we should only be included by meta.autodep.mk
# if dependencies are to be updated
-.for o in ${DIRDEPS_OPTIONS:tu}
+.for o in ${DIRDEPS_OPTIONS}
.for d in ${DIRDEPS.$o.yes} ${DIRDEPS.$o.no}
.if exists(${SRCTOP}/$d)
GENDIRDEPS_FILTER += N$d*
diff --git a/contrib/bmake/mk/dirdeps-targets.mk b/contrib/bmake/mk/dirdeps-targets.mk
index 50a1970d6211..73dcf3639d3b 100644
--- a/contrib/bmake/mk/dirdeps-targets.mk
+++ b/contrib/bmake/mk/dirdeps-targets.mk
@@ -1,15 +1,15 @@
# RCSid:
-# $Id: dirdeps-targets.mk,v 1.10 2020/06/06 22:41:02 sjg Exp $
+# $Id: dirdeps-targets.mk,v 1.22 2020/08/15 18:00:11 sjg Exp $
#
# @(#) Copyright (c) 2019-2020 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
@@ -25,7 +25,16 @@
# We then search those dirs for any Makefile.depend*
# Finally we select any that match conditions like REQUESTED_MACHINE
# or TARGET_SPEC and initialize DIRDEPS accordingly.
-#
+#
+# We will check each of the initial DIRDEPS for Makefile.dirdeps.options
+# and include any found.
+# This makes it feasible to tweak options like MK_DIRDEPS_CACHE
+# for a specific target.
+#
+# If MK_STATIC_DIRDEPS_CACHE is defined we will check if the
+# initial DIRDEPS has a static cache (Makefile.dirdeps.cache).
+# This only makes sense for seriously expensive targets.
+#
.if ${.MAKE.LEVEL} == 0
# pickup customizations
@@ -125,12 +134,38 @@ DIRDEPS := ${DIRDEPS:O:u}
.endif
# if we got DIRDEPS get to work
.if !empty(DIRDEPS)
+DIRDEPS.dirs := ${DIRDEPS:S,^,${SRCTOP}/,:@d@${exists($d):?$d:${d:R}}@}
+# some targets what to tweak options we might want to process now
+.for m in ${DIRDEPS.dirs:S,$,/Makefile.dirdeps.options,}
+.-include <$m>
+.endfor
+.if defined(MK_STATIC_DIRDEPS_CACHE)
+# some targets are very expensive to compute dirdeps for
+# so we may have a static cache
+.for c in ${DIRDEPS.dirs:S,$,/Makefile.dirdeps.cache,}
+.if exists($c)
+STATIC_DIRDEPS_CACHE ?= $c
+.if ${MK_STATIC_DIRDEPS_CACHE} == "yes"
+DIRDEPS_CACHE ?= $c
+MK_DIRDEPS_CACHE = yes
+.endif
+.endif
+.endfor
+.if defined(STATIC_DIRDEPS_CACHE)
+.export STATIC_DIRDEPS_CACHE
+.endif
+.endif
+
+# allow a top-level makefile to do other stuff
+# before including dirdeps.mk
+.if ${MK_DIRDEPS_TARGETS_INCLUDE_DIRDEPS:Uyes} == "yes"
.include <dirdeps.mk>
+.endif
DIRDEPS_TARGETS_SKIP += all clean* destroy*
.for t in ${.TARGETS:${DIRDEPS_TARGETS_SKIP:${M_ListToSkip}}}
$t: dirdeps
-.endfor
+.endfor
.endif
.endif
diff --git a/contrib/bmake/mk/dirdeps.mk b/contrib/bmake/mk/dirdeps.mk
index 337692479898..16673a04c07b 100644
--- a/contrib/bmake/mk/dirdeps.mk
+++ b/contrib/bmake/mk/dirdeps.mk
@@ -1,4 +1,4 @@
-# $Id: dirdeps.mk,v 1.106 2020/07/11 16:25:17 sjg Exp $
+# $Id: dirdeps.mk,v 1.125 2020/08/26 21:49:45 sjg Exp $
# Copyright (c) 2010-2020, Simon J. Gerraty
# Copyright (c) 2010-2018, Juniper Networks, Inc.
@@ -41,7 +41,7 @@
# or .<target_spec> suffix (see TARGET_SPEC_VARS below),
# for example to force building something for the pseudo
# machines "host" or "common" regardless of current ${MACHINE}.
-#
+#
# All unqualified entries end up being qualified with .${TARGET_SPEC}
# and partially qualified (if TARGET_SPEC_VARS has multiple
# entries) are also expanded to a full .<target_spec>.
@@ -50,29 +50,22 @@
#
# The fully qualified directory entries are used to construct a
# dependency graph that will drive the build later.
-#
+#
# Also, for each fully qualified directory target, we will search
# using ${.MAKE.DEPENDFILE_PREFERENCE} to find additional
# dependencies. We use Makefile.depend (default value for
# .MAKE.DEPENDFILE_PREFIX) to refer to these makefiles to
# distinguish them from others.
-#
+#
# Before each Makefile.depend file is read, we set
# DEP_RELDIR to be the RELDIR (path relative to SRCTOP) for
# its directory, and DEP_MACHINE etc according to the .<target_spec>
# represented by the suffix of the corresponding target.
-#
+#
# Since each Makefile.depend file includes dirdeps.mk, this
# processing is recursive and results in .MAKE.LEVEL 0 learning the
# dependencies of the tree wrt the initial directory (_DEP_RELDIR).
#
-# BUILD_AT_LEVEL0
-# Indicates whether .MAKE.LEVEL 0 builds anything:
-# if "no" sub-makes are used to build everything,
-# if "yes" sub-makes are only used to build for other machines.
-# It is best to use "no", but this can require fixing some
-# makefiles to not do anything at .MAKE.LEVEL 0.
-#
# TARGET_SPEC_VARS
# The default value is just MACHINE, and for most environments
# this is sufficient. The _DIRDEP_USE target actually sets
@@ -113,12 +106,12 @@
# # make sure we know what TARGET_SPEC is
# # as we may need it to find Makefile.depend*
# TARGET_SPEC = ${TARGET_SPEC_VARS:@v@${$v:U}@:ts,}
-#
+#
# The following variables can influence the initial DIRDEPS
# computation with regard to the TARGET_SPECs that will be
# built.
# Most should also be considered by init.mk
-#
+#
# ONLY_TARGET_SPEC_LIST
# Defines a list of TARGET_SPECs for which the current
# directory can be built.
@@ -137,6 +130,19 @@
# A list of MACHINEs the current directory should not be
# built for.
#
+# _build_xtra_dirs
+# local.dirdeps.mk can add targets to this variable.
+# They will be hooked into the build, but independent of
+# any other DIRDEP.
+#
+# This allows for adding TESTS to the build, such that the build
+# if any test fails, but without the risk of introducing
+# circular dependencies.
+
+now_utc ?= ${%s:L:gmtime}
+.if !defined(start_utc)
+start_utc := ${now_utc}
+.endif
.if !target(bootstrap) && (make(bootstrap) || \
make(bootstrap-this) || \
@@ -157,11 +163,6 @@ _DIRDEP_USE_LEVEL?= 0
_CURDIR ?= ${.CURDIR}
_OBJDIR ?= ${.OBJDIR}
-now_utc = ${%s:L:gmtime}
-.if !defined(start_utc)
-start_utc := ${now_utc}
-.endif
-
.if ${MAKEFILE:T} == ${.PARSEFILE} && empty(DIRDEPS) && ${.TARGETS:Uall:M*/*} != ""
# This little trick let's us do
#
@@ -223,6 +224,7 @@ _tspec_m$i := ${TARGET_SPEC_VARS:[2..$i]:@w@[^,]+@:ts,}
_tspec_a$i := ,${TARGET_SPEC_VARS:[$i..-1]:@v@$$$${DEP_$v}@:ts,}
M_dep_qual_fixes += C;(\.${_tspec_m$i})$$;\1${_tspec_a$i};
.endfor
+TARGET_SPEC_VARSr := ${TARGET_SPEC_VARS:[-1..1]}
.else
# A harmless? default.
M_dep_qual_fixes = U
@@ -307,6 +309,7 @@ DEP_MACHINE := ${_DEP_TARGET_SPEC}
# reset each time through
_build_all_dirs =
+_build_xtra_dirs =
# the first time we are included the _DIRDEP_USE target will not be defined
# we can use this as a clue to do initialization and other one time things.
@@ -337,7 +340,7 @@ BUILD_DIRDEPS ?= yes
.if ${MK_DIRDEPS_CACHE} == "yes"
# this is where we will cache all our work
-DIRDEPS_CACHE ?= ${_OBJDIR:tA}/dirdeps.cache${.TARGETS:Nall:O:u:ts-:S,/,_,g:S,^,.,:N.}
+DIRDEPS_CACHE ?= ${_OBJDIR:tA}/dirdeps.cache${_TARGETS:U${.TARGETS}:Nall:O:u:ts-:S,/,_,g:S,^,.,:N.}
.endif
.if ${DEBUG_DIRDEPS:@x@${DEP_RELDIR:M$x}${${DEP_RELDIR}.${DEP_MACHINE}:L:M$x}@} != ""
@@ -393,6 +396,18 @@ DIRDEPS_FILTER += M${_DEP_RELDIR}
DIRDEP_MAKE ?= ${.MAKE}
DIRDEP_DIR ?= ${.TARGET:R}
+# if you want us to report load averages during build
+# DIRDEP_USE_PRELUDE += ${DIRDEP_LOADAVG_REPORT};
+
+DIRDEP_LOADAVG_CMD ?= ${UPTIME:Uuptime} | sed 's,.*\(load\),\1,'
+DIRDEP_LOADAVG_LAST = 0
+# yes the expression here is a bit complicated,
+# the trick is to only eval ${DIRDEP_LOADAVG_LAST::=${now_utc}}
+# when we want to report.
+DIRDEP_LOADAVG_REPORT = \
+ test -z "${"${expr ${now_utc} - ${DIRDEP_LOADAVG_INTEVAL:U60} - ${DIRDEP_LOADAVG_LAST}:L:sh:N-*}":?yes${DIRDEP_LOADAVG_LAST::=${now_utc}}:}" || \
+ echo "${TRACER}`${DIRDEP_LOADAVG_CMD}`"
+
# we suppress SUBDIR when visiting the leaves
# we assume sys.mk will set MACHINE_ARCH
# you can add extras to DIRDEP_USE_ENV
@@ -400,7 +415,7 @@ DIRDEP_DIR ?= ${.TARGET:R}
_DIRDEP_USE: .USE .MAKE
@for m in ${.MAKE.MAKEFILE_PREFERENCE}; do \
test -s ${.TARGET:R}/$$m || continue; \
- echo "${TRACER}Checking ${.TARGET:R} for ${.TARGET:E} ..."; \
+ echo "${TRACER}Checking ${.TARGET:S,${SRCTOP}/,,} for ${.TARGET:E} ..."; \
${DIRDEP_USE_PRELUDE} \
MACHINE_ARCH= NO_SUBDIR=1 ${DIRDEP_USE_ENV} \
TARGET_SPEC=${.TARGET:E} \
@@ -480,9 +495,13 @@ dirdeps-cached: ${DIRDEPS_CACHE} .MAKE
dirdeps MK_DIRDEPS_CACHE=no BUILD_DIRDEPS=no
# these should generally do
-BUILD_DIRDEPS_MAKEFILE ?= ${MAKEFILE}
+BUILD_DIRDEPS_MAKEFILE ?=
BUILD_DIRDEPS_TARGETS ?= ${.TARGETS}
+.if ${DIRDEPS_CACHE} != ${STATIC_DIRDEPS_CACHE:Uno} && ${DIRDEPS_CACHE:M${SRCTOP}/*} == ""
+# export this for dirdeps-cache-update.mk
+DYNAMIC_DIRDEPS_CACHE := ${DIRDEPS_CACHE}
+.export DYNAMIC_DIRDEPS_CACHE
# we need the .meta file to ensure we update if
# any of the Makefile.depend* changed.
# We do not want to compare the command line though.
@@ -494,26 +513,28 @@ ${DIRDEPS_CACHE}: .META .NOMETA_CMP
+@MAKELEVEL=${.MAKE.LEVEL} DIRDEPS_CACHE=${DIRDEPS_CACHE} \
DIRDEPS="${DIRDEPS}" \
TARGET_SPEC=${TARGET_SPEC} \
- MAKEFLAGS= ${.MAKE} -C ${_CURDIR} -f ${BUILD_DIRDEPS_MAKEFILE} \
+ MAKEFLAGS= ${DIRDEP_CACHE_MAKE:U${.MAKE}} -C ${_CURDIR} \
+ ${BUILD_DIRDEPS_MAKEFILE} \
${BUILD_DIRDEPS_TARGETS} BUILD_DIRDEPS_CACHE=yes \
.MAKE.DEPENDFILE=.none \
${.MAKEFLAGS:tW:S,-D ,-D,g:tw:M*WITH*} \
${.MAKEFLAGS:tW:S,-d ,-d,g:tw:M-d*} \
- 3>&1 1>&2 | sed 's,${SRCTOP},$${SRCTOP},g' >> ${.TARGET}.new && \
+ 3>&1 1>&2 | sed 's,${SRCTOP},$${SRCTOP},g;s,_{,$${,g' >> ${.TARGET}.new && \
mv ${.TARGET}.new ${.TARGET}
.endif
+.endif
.elif !target(_count_dirdeps)
# we want to capture the dirdeps count in the cache
.END: _count_dirdeps
_count_dirdeps: .NOMETA
- @{ echo; echo '.info $${.newline}$${TRACER}Makefiles read: total=${.MAKE.MAKEFILES:[#]} depend=${.MAKE.MAKEFILES:M*depend*:[#]} dirdeps=${.ALLTARGETS:M${SRCTOP}*:O:u:[#]}'; } >&3
+ @{ echo; echo '.info $${.newline}$${TRACER}Makefiles read: total=${.MAKE.MAKEFILES:[#]} depend=${.MAKE.MAKEFILES:M*depend*:[#]} dirdeps=${.ALLTARGETS:M${SRCTOP}*:O:u:[#]} ${DIRDEP_INFO_XTRAS}'; } >&3
.endif
.elif !make(dirdeps) && !target(_count_dirdeps)
beforedirdeps: _count_dirdeps
_count_dirdeps: .NOMETA
- @echo "${TRACER}Makefiles read: total=${.MAKE.MAKEFILES:[#]} depend=${.MAKE.MAKEFILES:M*depend*:[#]} dirdeps=${.ALLTARGETS:M${SRCTOP}*:O:u:[#]} seconds=`expr ${now_utc} - ${start_utc}`"
+ @echo "${TRACER}Makefiles read: total=${.MAKE.MAKEFILES:[#]} depend=${.MAKE.MAKEFILES:M*depend*:[#]} dirdeps=${.ALLTARGETS:M${SRCTOP}*:O:u:[#]} ${DIRDEP_INFO_XTRAS} seconds=`expr ${now_utc} - ${start_utc}`"
.endif
.endif
@@ -570,19 +591,7 @@ _build_dirs =
.if ${DEP_RELDIR} == ${_DEP_RELDIR}
# pickup other machines for this dir if necessary
-.if ${BUILD_AT_LEVEL0:Uyes} == "no"
_build_dirs += ${_machines:@m@${_CURDIR}.$m@}
-.else
-_build_dirs += ${_machines:N${DEP_TARGET_SPEC}:@m@${_CURDIR}.$m@}
-.if ${DEP_TARGET_SPEC} == ${TARGET_SPEC}
-# pickup local dependencies now
-.if ${MAKE_VERSION} < 20160220
-.-include <.depend>
-.else
-.dinclude <.depend>
-.endif
-.endif
-.endif
.endif
.if ${_debug_reldir}
@@ -635,7 +644,7 @@ _build_dirs := ${_build_dirs:${M_dep_qual_fixes:ts:}:O:u}
.endif # empty DIRDEPS
-_build_all_dirs += ${_build_dirs}
+_build_all_dirs += ${_build_dirs} ${_build_xtra_dirs}
_build_all_dirs := ${_build_all_dirs:O:u}
# Normally if doing make -V something,
@@ -647,12 +656,7 @@ _build_all_dirs := ${_build_all_dirs:O:u}
x!= echo; { echo; echo '\# ${DEP_RELDIR}.${DEP_TARGET_SPEC}'; } >&3
# guard against _new_dirdeps being too big for a single command line
_new_dirdeps := ${_build_all_dirs:@x@${target($x):?:$x}@}
-.if !empty(_new_dirdeps)
-.export _new_dirdeps
-x!= echo; { echo; echo "dirdeps: \\"; \
- for x in $$_new_dirdeps; do echo " $$x \\"; done; echo; \
- for x in $$_new_dirdeps; do echo "$$x: _DIRDEP_USE"; done; } >&3
-.endif
+.export _build_xtra_dirs _new_dirdeps
.if !empty(DEP_EXPORT_VARS)
# Discouraged, but there are always exceptions.
# Handle it here rather than explain how.
@@ -676,7 +680,8 @@ DEP_EXPORT_VARS=
# this builds the dependency graph
.for m in ${_machines}
.if ${BUILD_DIRDEPS_CACHE} == "yes" && !empty(_build_dirs)
-x!= echo; { echo; echo "${_this_dir}.$m: \\"; } >&3
+x!= echo; { echo; echo 'DIRDEPS.${_this_dir}.$m = \'; } >&3
+_cache_deps =
.endif
# it would be nice to do :N${.TARGET}
.if !empty(__qual_depdirs)
@@ -685,11 +690,7 @@ x!= echo; { echo; echo "${_this_dir}.$m: \\"; } >&3
.info ${DEP_RELDIR}.$m: graph: ${_build_dirs:M*.$q}
.endif
.if ${BUILD_DIRDEPS_CACHE} == "yes"
-_cache_deps := ${_build_dirs:M*.$q}
-.if !empty(_cache_deps)
-.export _cache_deps
-x!= echo; for x in $$_cache_deps; do echo " $$x \\"; done >&3
-.endif
+_cache_deps += ${_build_dirs:M*.$q}
.else
${_this_dir}.$m: ${_build_dirs:M*.$q}
.endif
@@ -699,11 +700,17 @@ ${_this_dir}.$m: ${_build_dirs:M*.$q}
.info ${DEP_RELDIR}.$m: graph: ${_build_dirs:M*.$m:N${_this_dir}.$m}
.endif
.if ${BUILD_DIRDEPS_CACHE} == "yes"
-_cache_deps := ${_build_dirs:M*.$m:N${_this_dir}.$m}
+.if !empty(_build_dirs)
+_cache_deps += ${_build_dirs:M*.$m:N${_this_dir}.$m}
.if !empty(_cache_deps)
.export _cache_deps
x!= echo; for x in $$_cache_deps; do echo " $$x \\"; done >&3
.endif
+x!= echo; { echo; echo '${_this_dir}.$m: $${DIRDEPS.${_this_dir}.$m}'; \
+ echo; echo 'dirdeps: ${_this_dir}.$m \'; \
+ for x in $$_build_xtra_dirs; do echo " $$x \\"; done; \
+ echo; for x in $$_new_dirdeps; do echo "$$x: _DIRDEP_USE"; done; } >&3
+.endif
.else
${_this_dir}.$m: ${_build_dirs:M*.$m:N${_this_dir}.$m}
.endif
@@ -773,6 +780,27 @@ _DEP_RELDIR := ${RELDIR}
# This is a final opportunity to add/hook global rules.
.-include <local.dirdeps-build.mk>
+# skip _reldir_{finish,failed} if not included from Makefile.depend*
+# or not in meta mode
+.if !defined(WITHOUT_META_STATS) && ${.INCLUDEDFROMFILE:U:M${.MAKE.DEPENDFILE_PREFIX}*} != "" && ${.MAKE.MODE:Mmeta} != ""
+
+meta_stats= meta=${empty(.MAKE.META.FILES):?0:${.MAKE.META.FILES:[#]}} \
+ created=${empty(.MAKE.META.CREATED):?0:${.MAKE.META.CREATED:[#]}}
+
+.if !target(_reldir_finish)
+.END: _reldir_finish
+_reldir_finish: .NOMETA
+ @echo "${TRACER}Finished ${RELDIR}.${TARGET_SPEC} seconds=$$(( ${now_utc} - ${start_utc} )) ${meta_stats}"
+.endif
+
+.if !target(_reldir_failed)
+.ERROR: _reldir_failed
+_reldir_failed: .NOMETA
+ @echo "${TRACER}Failed ${RELDIR}.${TARGET_SPEC} seconds=$$(( ${now_utc} - ${start_utc} )) ${meta_stats}"
+.endif
+
+.endif
+
# pickup local dependencies
.if ${MAKE_VERSION} < 20160220
.-include <.depend>
diff --git a/contrib/bmake/mk/dpadd.mk b/contrib/bmake/mk/dpadd.mk
index 02fa7f386921..45585ce76d9b 100644
--- a/contrib/bmake/mk/dpadd.mk
+++ b/contrib/bmake/mk/dpadd.mk
@@ -1,14 +1,14 @@
-# $Id: dpadd.mk,v 1.27 2019/05/17 13:58:53 sjg Exp $
+# $Id: dpadd.mk,v 1.28 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 2004, 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
@@ -75,7 +75,7 @@
# and -L${STAGE_OBJTOP}/usr/lib are sufficient, and we should
# have no need of anything else.
#
-
+
.if !target(__${.PARSEFILE}__)
__${.PARSEFILE}__:
@@ -98,7 +98,7 @@ RELOBJTOP?= ${OBJTOP}
RELSRCTOP?= ${SRCTOP}
# we get included just about everywhere so this is handy...
-# C*DEBUG_XTRA are for defining on cmd line etc
+# C*DEBUG_XTRA are for defining on cmd line etc
# so do not use in makefiles.
.ifdef CFLAGS_DEBUG_XTRA
CFLAGS_LAST += ${CFLAGS_DEBUG_XTRA}
@@ -180,14 +180,14 @@ SRC_LIBS+= ${_OBJDIR}/lib${LIB}.a
.endif
.endif
-#
+#
# This little bit of magic, assumes that SRC_libfoo will be
# set if it cannot be correctly derrived from ${LIBFOO}
# Note that SRC_libfoo and INCLUDES_libfoo should be named for the
# actual library name not the variable name that might refer to it.
# 99% of the time the two are the same, but the DPADD logic
# only has the library name available, so stick to that.
-#
+#
SRC_LIBS?=
# magic_libs includes those we want to link with
@@ -198,7 +198,7 @@ DPMAGIC_LIBS += ${__dpadd_magic_libs} \
# we skip this for staged libs
.for __lib in ${DPMAGIC_LIBS:O:u:N${STAGE_OBJTOP:Unot}*/lib/*}
-#
+#
# if SRC_libfoo is not set, then we assume that the srcdir corresponding
# to where we found the library is correct.
#
@@ -215,8 +215,8 @@ INCLUDES_${__lib:T:R}?= -I${exists(${SRC_${__lib:T:R}}/h):?${SRC_${__lib:T:R}}/h
.endfor
-# even for staged libs we sometimes
-# need to allow direct -I to avoid cicular dependencies
+# even for staged libs we sometimes
+# need to allow direct -I to avoid cicular dependencies
.for __lib in ${DPMAGIC_LIBS:O:u:T:R}
.if !empty(SRC_${__lib}) && empty(INCLUDES_${__lib})
# must be a staged lib
@@ -267,7 +267,7 @@ __dpadd_last_incs += ${__dpadd_magic_libs:O:u:@s@${SRC_LIBS_${s:T:R}:U}@:@x@${IN
__dpadd_last_incs := \
${__dpadd_last_incs:N-I/usr/*} \
${__dpadd_incs:M-I/usr/*} \
- ${__dpadd_last_incs:M-I/usr/*}
+ ${__dpadd_last_incs:M-I/usr/*}
__dpadd_incs := ${__dpadd_incs:N-I/usr/*}
.endif
@@ -310,12 +310,12 @@ dpadd: .NOTMAIN
.endif
.ifdef SRC_PATHADD
-# We don't want to assume that we need to .PATH every element of
+# We don't want to assume that we need to .PATH every element of
# SRC_LIBS, but the Makefile cannot do
# .PATH: ${SRC_libfoo}
# since the value of SRC_libfoo must be available at the time .PATH:
-# is read - and we only just worked it out.
-# Further, they can't wait until after include of {lib,prog}.mk as
+# is read - and we only just worked it out.
+# Further, they can't wait until after include of {lib,prog}.mk as
# the .PATH is needed before then.
# So we let the Makefile do
# SRC_PATHADD+= ${SRC_libfoo}
diff --git a/contrib/bmake/mk/files.mk b/contrib/bmake/mk/files.mk
index fa16b118fb6c..513ab1fd819e 100644
--- a/contrib/bmake/mk/files.mk
+++ b/contrib/bmake/mk/files.mk
@@ -1,14 +1,14 @@
-# $Id: files.mk,v 1.6 2017/05/07 02:21:02 sjg Exp $
+# $Id: files.mk,v 1.7 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 2017, 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
diff --git a/contrib/bmake/mk/gendirdeps.mk b/contrib/bmake/mk/gendirdeps.mk
index 82618f2225a3..b977f3c48d99 100644
--- a/contrib/bmake/mk/gendirdeps.mk
+++ b/contrib/bmake/mk/gendirdeps.mk
@@ -1,18 +1,18 @@
-# $Id: gendirdeps.mk,v 1.44 2020/06/23 04:21:51 sjg Exp $
+# $Id: gendirdeps.mk,v 1.46 2020/08/19 17:51:53 sjg Exp $
# Copyright (c) 2011-2020, Simon J. Gerraty
# Copyright (c) 2010-2018, Juniper Networks, 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:
+# modification, are permitted provided that the following conditions
+# are met:
# 1. Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
+# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-#
+# 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
@@ -23,7 +23,7 @@
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# This makefile [re]generates ${.MAKE.DEPENDFILE}
@@ -104,7 +104,7 @@ GENDIRDEPS_FILTER += ${GENDIRDEPS_FILTER_DIR_VARS:@v@S,${$v},_{${v}},@}
GENDIRDEPS_FILTER += ${GENDIRDEPS_FILTER_VARS:@v@S,/${$v}/,/_{${v}}/,@:NS,//,*:u}
.endif
-# this (*should* be set in meta.sys.mk)
+# this (*should* be set in meta.sys.mk)
# is the script that extracts what we want.
META2DEPS ?= ${.PARSEDIR}/meta2deps.sh
META2DEPS := ${META2DEPS}
@@ -122,7 +122,7 @@ _py_d =
.if ${META2DEPS:E} == "py"
# we can afford to do this all the time.
DPDEPS ?= no
-META2DEPS_CMD = ${_time} ${PYTHON} ${META2DEPS} ${_py_d}
+META2DEPS_CMD = ${_time} ${PYTHON} ${META2DEPS} ${_py_d}
.if ${DPDEPS:tl} != "no"
META2DEPS_CMD += -D ${DPDEPS}
.endif
@@ -155,8 +155,8 @@ M2D_OBJROOTS += ${STAGE_ROOT}
# and tell it not to add machine qualifiers
META2DEPS_ARGS += MACHINE=none
.endif
-.if defined(SB_BACKING_SB)
-META2DEPS_CMD += -S ${SB_BACKING_SB}/src
+.if defined(SB_BACKING_SB)
+META2DEPS_CMD += -S ${SB_BACKING_SB}/src
M2D_OBJROOTS += ${SB_BACKING_SB}/${SB_OBJPREFIX}
.endif
@@ -177,7 +177,7 @@ _meta_files := ${META_FILES:N\*.meta:O:u}
# assume a big list
_meta_files_arg= @meta.list
.if empty(_meta_files) && ${META_FILES:M\*.meta} != ""
-# XXX this should be considered a bad idea,
+# XXX this should be considered a bad idea,
# since we cannot ignore stale .meta
x != cd ${_OBJDIR} && find . -name '*.meta' -print -o \( -type d ! -name . -prune \) | sed 's,^./,,' > meta.list; echo
.elif ${_meta_files:[#]} > 500
@@ -234,10 +234,10 @@ dir_list += ${ddeps}
# DIRDEPS represent things that had to have been built first
# so they should all be undir OBJTOP.
-# Note that ${_OBJTOP}/bsd/include/machine will get reported
+# Note that ${_OBJTOP}/bsd/include/machine will get reported
# to us as $SRCTOP/bsd/sys/$MACHINE_ARCH/include meaning we
# will want to visit bsd/include
-# so we add
+# so we add
# ${"${dir_list:M*bsd/sys/${MACHINE_ARCH}/include}":?bsd/include:}
# to GENDIRDEPS_DIR_LIST_XTRAS
_objtops = ${OBJTOP} ${_OBJTOP} ${_objtop}
@@ -272,11 +272,11 @@ DIRDEPS = \
# We only consider things below $RELDIR/ if they have a makefile.
# This is the same test that _DIRDEP_USE applies.
-# We have do a double test with dirdep_list as it _may_ contain
+# We have do a double test with dirdep_list as it _may_ contain
# qualified dirs - if we got anything from a stage dir.
# qualdir_list we know are all qualified.
# It would be nice do peform this check for all of DIRDEPS,
-# but we cannot assume that all of the tree is present,
+# but we cannot assume that all of the tree is present,
# in fact we can only assume that RELDIR is.
DIRDEPS += \
${dirdep_list:M${RELDIR}/*:@d@${.MAKE.MAKEFILE_PREFERENCE:@m@${exists(${SRCTOP}/$d/$m):?$d:${exists(${SRCTOP}/${d:R}/$m):?$d:}}@}@} \
@@ -309,7 +309,7 @@ SRC_DIRDEPS = \
SRC_DIRDEPS := ${SRC_DIRDEPS:${GENDIRDEPS_SRC_FILTER:UN/*:ts:}:C,//+,/,g:O:u}
# if you want to capture SRC_DIRDEPS in .MAKE.DEPENDFILE put
-# SRC_DIRDEPS_FILE = ${_DEPENDFILE}
+# SRC_DIRDEPS_FILE = ${_DEPENDFILE}
# in local.gendirdeps.mk
.if ${SRC_DIRDEPS_FILE:Uno:tl} != "no"
ECHO_SRC_DIRDEPS = echo 'SRC_DIRDEPS = \'; echo '${SRC_DIRDEPS:@d@ $d \\${.newline}@}'; echo;
@@ -324,7 +324,7 @@ ${SRC_DIRDEPS_FILE}: ${META_FILES} ${_this} ${META2DEPS}
.endif
.endif
.endif
-_include_src_dirdeps ?=
+_include_src_dirdeps ?=
all: ${_DEPENDFILE}
@@ -339,11 +339,7 @@ CAT_DEPEND ?= .depend
.PHONY: ${_DEPENDFILE}
.endif
-.if ${BUILD_AT_LEVEL0:Uno:tl} == "no"
LOCAL_DEPENDS_GUARD ?= _{.MAKE.LEVEL} > 0
-.else
-LOCAL_DEPENDS_GUARD ?= _{DEP_RELDIR} == _{_DEP_RELDIR}
-.endif
# 'cat .depend' should suffice, but if we are mixing build modes
# .depend may contain things we don't want.
diff --git a/contrib/bmake/mk/host-target.mk b/contrib/bmake/mk/host-target.mk
index a83642c9698c..3e6094f16730 100644
--- a/contrib/bmake/mk/host-target.mk
+++ b/contrib/bmake/mk/host-target.mk
@@ -1,5 +1,5 @@
# RCSid:
-# $Id: host-target.mk,v 1.12 2020/07/08 23:35:29 sjg Exp $
+# $Id: host-target.mk,v 1.13 2020/08/05 23:32:08 sjg Exp $
# Host platform information; may be overridden
.if !defined(_HOST_OSNAME)
@@ -16,7 +16,7 @@ _HOST_MACHINE != uname -m
.endif
.if !defined(_HOST_ARCH)
# for NetBSD prefer $MACHINE (amd64 rather than x86_64)
-.if ${_HOST_OSNAME:NNetBSD} == ""
+.if ${_HOST_OSNAME:NDarwin:NNetBSD} == ""
_HOST_ARCH := ${_HOST_MACHINE}
.else
_HOST_ARCH != uname -p 2> /dev/null || uname -m
diff --git a/contrib/bmake/mk/host.libnames.mk b/contrib/bmake/mk/host.libnames.mk
index c0a13d756d16..3afa73f9d2a3 100644
--- a/contrib/bmake/mk/host.libnames.mk
+++ b/contrib/bmake/mk/host.libnames.mk
@@ -1,14 +1,14 @@
-# $Id: host.libnames.mk,v 1.4 2010/01/11 23:01:31 sjg Exp $
+# $Id: host.libnames.mk,v 1.5 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 2007-2009, 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
diff --git a/contrib/bmake/mk/inc.mk b/contrib/bmake/mk/inc.mk
index 74626d34646f..5fc14b32d88d 100644
--- a/contrib/bmake/mk/inc.mk
+++ b/contrib/bmake/mk/inc.mk
@@ -1,14 +1,14 @@
-# $Id: inc.mk,v 1.7 2017/05/06 17:29:45 sjg Exp $
+# $Id: inc.mk,v 1.8 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 2008, 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
diff --git a/contrib/bmake/mk/init.mk b/contrib/bmake/mk/init.mk
index d4ae45372d9c..0dae997beb54 100644
--- a/contrib/bmake/mk/init.mk
+++ b/contrib/bmake/mk/init.mk
@@ -1,14 +1,14 @@
-# $Id: init.mk,v 1.17 2020/05/25 20:15:07 sjg Exp $
+# $Id: init.mk,v 1.21 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
@@ -65,7 +65,11 @@ CC_PIC?= -DPIC
CXX_PIC?= ${CC_PIC}
PROFFLAGS?= -DGPROF -DPROF
-.if ${.MAKE.LEVEL:U1} == 0 && ${BUILD_AT_LEVEL0:Uyes:tl} == "no"
+# targets that are ok at level 0
+LEVEL0_TARGETS += clean* destory*
+M_ListToSkip= O:u:S,^,N,:ts:
+
+.if ${.MAKE.LEVEL:U1} == 0 && ${MK_DIRDEPS_BUILD:Uno} == "yes" && ${.TARGETS:Uall:${LEVEL0_TARGETS:${M_ListToSkip}}} != ""
# this tells lib.mk and prog.mk to not actually build anything
_SKIP_BUILD = not building at level 0
.endif
diff --git a/contrib/bmake/mk/install-mk b/contrib/bmake/mk/install-mk
index 486bcd25d05f..66185e42cc6e 100644
--- a/contrib/bmake/mk/install-mk
+++ b/contrib/bmake/mk/install-mk
@@ -33,7 +33,7 @@
#
# All our *.mk files are copied to "dest" with appropriate
# ownership and permissions.
-#
+#
# By default if a sys.mk can be found in a standard location
# (that bmake will find) then no sys.mk will be put in "dest".
#
@@ -55,22 +55,22 @@
# Simon J. Gerraty <sjg@crufty.net>
# RCSid:
-# $Id: install-mk,v 1.174 2020/07/10 21:50:14 sjg Exp $
+# $Id: install-mk,v 1.179 2020/08/26 21:49:45 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
-MK_VERSION=20200710
+MK_VERSION=20200826
OWNER=
GROUP=
MODE=444
@@ -137,7 +137,7 @@ if [ -s $SYS_MK -a -d $dest ]; then
sys_mk_dir=`realpath $SYS_MK_DIR`
if [ $dest = $sys_mk_dir ]; then
case "$os" in
- *BSD*) SKIP_SYS_MK=:
+ *BSD*) SKIP_SYS_MK=:
SKIP_BSD_MK=:
;;
*) # could be fake?
diff --git a/contrib/bmake/mk/install-new.mk b/contrib/bmake/mk/install-new.mk
index ddfff20e3b85..d312bdc26d46 100644
--- a/contrib/bmake/mk/install-new.mk
+++ b/contrib/bmake/mk/install-new.mk
@@ -1,14 +1,14 @@
-# $Id: install-new.mk,v 1.3 2012/03/24 18:25:49 sjg Exp $
+# $Id: install-new.mk,v 1.4 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 2009, 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
diff --git a/contrib/bmake/mk/java.mk b/contrib/bmake/mk/java.mk
index ef4a5ea0ba10..e2149e7089a7 100644
--- a/contrib/bmake/mk/java.mk
+++ b/contrib/bmake/mk/java.mk
@@ -1,16 +1,16 @@
#
# RCSid:
-# $Id: java.mk,v 1.14 2007/11/22 08:16:25 sjg Exp $
+# $Id: java.mk,v 1.15 2020/08/19 17:51:53 sjg Exp $
# @(#) Copyright (c) 1998-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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
@@ -53,7 +53,7 @@ JAVAC_FLAGS+= ${JAVAC_DBG}
.if defined(MAKE_VERSION) && !defined(NO_CLASSES_COOKIE)
# java works best by compiling a bunch of classes at once.
-# this lot does that but needs a recent netbsd make or
+# this lot does that but needs a recent netbsd make or
# or its portable cousin bmake.
.for __s in ${SRCS}
__c:= ${__classdest}${__s:.java=.class}
diff --git a/contrib/bmake/mk/lib.mk b/contrib/bmake/mk/lib.mk
index 03d24e27cbe6..c3979414ec49 100644
--- a/contrib/bmake/mk/lib.mk
+++ b/contrib/bmake/mk/lib.mk
@@ -1,4 +1,4 @@
-# $Id: lib.mk,v 1.70 2020/05/02 02:10:20 sjg Exp $
+# $Id: lib.mk,v 1.71 2020/08/19 17:51:53 sjg Exp $
.if !target(__${.PARSEFILE}__)
__${.PARSEFILE}__:
@@ -67,7 +67,7 @@ META_NOECHO?= echo
# Alpha-specific shared library flags
FPICFLAGS ?= -fPIC
CPICFLAGS ?= -fPIC -DPIC
-CPPPICFLAGS?= -DPIC
+CPPPICFLAGS?= -DPIC
CAPICFLAGS?= ${CPPPICFLAGS} ${CPICFLAGS}
APICFLAGS ?=
.elif ${MACHINE_ARCH} == "mipsel" || ${MACHINE_ARCH} == "mipseb"
@@ -87,7 +87,7 @@ MKPICLIB= no
.elif (${MACHINE_ARCH} == "sparc" || ${MACHINE_ARCH} == "sparc64") && \
${OBJECT_FMT} == "ELF"
-# If you use -fPIC you need to define BIGPIC to turn on 32-bit
+# If you use -fPIC you need to define BIGPIC to turn on 32-bit
# relocations in asm code
FPICFLAGS ?= -fPIC
CPICFLAGS ?= -fPIC -DPIC
@@ -102,7 +102,7 @@ SHLIB_SOVERSION=${SHLIB_FULLVERSION}
SHLIB_SHFLAGS=
FPICFLAGS ?= -fPIC
CPICFLAGS?= -fPIC -DPIC
-CPPPICFLAGS?= -DPIC
+CPPPICFLAGS?= -DPIC
CAPICFLAGS?= ${CPPPICFLAGS} ${CPICFLAGS}
APICFLAGS?= -k
@@ -246,7 +246,7 @@ DLLIB ?= -ldl
.endif
# some libs have lots of objects, and scanning all .o, .po and ${PICO} meta files
-# is a waste of time, this tells meta.autodep.mk to just pick one
+# is a waste of time, this tells meta.autodep.mk to just pick one
# (typically ${PICO})
# yes, 42 is a random number.
.if ${MK_DIRDEPS_BUILD} == "yes" && ${SRCS:Uno:[\#]} > 42
@@ -272,7 +272,7 @@ ${CXX_SUFFIXES:%=%.o}:
${COMPILE.cc} ${.IMPSRC}
.S.o .s.o:
- ${COMPILE.S} ${CFLAGS:M-[ID]*} ${AINC} ${.IMPSRC}
+ ${COMPILE.S} ${CFLAGS:M-[ID]*} ${AINC} ${.IMPSRC}
.if (${LD_X} == "")
.c.po:
@@ -382,7 +382,7 @@ _LIBS+= ${libLDORDER_INC}
.endif
.if !defined(_SKIP_BUILD)
-realbuild: ${_LIBS}
+realbuild: ${_LIBS}
.endif
all: _SUBDIRUSE
diff --git a/contrib/bmake/mk/libnames.mk b/contrib/bmake/mk/libnames.mk
index b0eabed5ea53..8140360714df 100644
--- a/contrib/bmake/mk/libnames.mk
+++ b/contrib/bmake/mk/libnames.mk
@@ -1,14 +1,14 @@
-# $Id: libnames.mk,v 1.8 2016/04/05 15:58:37 sjg Exp $
+# $Id: libnames.mk,v 1.9 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 2007-2009, 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
diff --git a/contrib/bmake/mk/libs.mk b/contrib/bmake/mk/libs.mk
index 7f974bfa86b9..9f0079d6e511 100644
--- a/contrib/bmake/mk/libs.mk
+++ b/contrib/bmake/mk/libs.mk
@@ -1,14 +1,14 @@
-# $Id: libs.mk,v 1.3 2013/08/02 18:28:48 sjg Exp $
+# $Id: libs.mk,v 1.6 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 2006, 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
@@ -82,12 +82,18 @@ UPDATE_DEPENDFILE = NO
LIBS_TARGETS+= cleandepend cleandir cleanobj depend install
.for b in ${LIBS:R:T:S,^lib,,}
-lib$b.a: ${SRCS} ${DPADD} ${SRCS_lib$b} ${DPADD_lib$b}
- (cd ${.CURDIR} && ${.MAKE} -f ${MAKEFILE} LIB=$b)
+lib$b.a: ${SRCS} ${DPADD} ${SRCS_lib$b} ${DPADD_lib$b}
+ (cd ${.CURDIR} && ${.MAKE} -f ${MAKEFILE} LIB=$b -DWITHOUT_META_STATS)
.for t in ${LIBS_TARGETS:O:u}
$b.$t: .PHONY .MAKE
- (cd ${.CURDIR} && ${.MAKE} -f ${MAKEFILE} LIB=$b ${@:E})
+ (cd ${.CURDIR} && ${.MAKE} -f ${MAKEFILE} LIB=$b ${@:E} -DWITHOUT_META_STATS)
.endfor
.endfor
+
+.if !defined(WITHOUT_META_STATS) && ${.MAKE.LEVEL} > 0
+.END: _reldir_finish
+.ERROR: _reldir_failed
+.endif
+
.endif
diff --git a/contrib/bmake/mk/links.mk b/contrib/bmake/mk/links.mk
index aac3914fdd00..6bf0db080c23 100644
--- a/contrib/bmake/mk/links.mk
+++ b/contrib/bmake/mk/links.mk
@@ -1,14 +1,14 @@
-# $Id: links.mk,v 1.6 2014/09/29 17:14:40 sjg Exp $
+# $Id: links.mk,v 1.7 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 2005, 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
diff --git a/contrib/bmake/mk/manifest.mk b/contrib/bmake/mk/manifest.mk
index 797038d19391..1e2f728f094e 100644
--- a/contrib/bmake/mk/manifest.mk
+++ b/contrib/bmake/mk/manifest.mk
@@ -1,14 +1,14 @@
-# $Id: manifest.mk,v 1.2 2014/10/31 18:06:17 sjg Exp $
+# $Id: manifest.mk,v 1.3 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 2014, 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
@@ -21,12 +21,12 @@
# ${MANIFEST}.DIRS += bin sbin usr/bin ...
# for each dir we have a ${MANIFEST}.SRCS.$dir
# that provides the absolute path to the contents
-# ${MANIFEST}.SRCS.bin += ${OBJTOP}/bin/sh/sh
+# ${MANIFEST}.SRCS.bin += ${OBJTOP}/bin/sh/sh
# ${MANIFEST}.SYMLINKS is a list of src target pairs
# for each file/dir there are a number of attributes
# UID GID MODE FLAGS
# which can be set per dir, per file or we use defaults
-# eg.
+# eg.
# MODE.sbin = 550
# MODE.usr/sbin = 550
# MODE.dirs = 555
@@ -37,7 +37,7 @@
# means passwd gets 4555 other files in usr/bin get 555 and
# files in usr/sbin get 500
# STORE defaults to basename of src and target directory
-# but we can use
+# but we can use
# ${MANIFEST}.SRCS.sbin += ${OBJTOP}/bin/sh-static/sh-static
# STORE.sbin/sh-static = sbin/sh
#
@@ -47,7 +47,7 @@
UID.dirs ?= 0
GID.dirs ?= 0
MODE.dirs ?= 775
-FLAGS.dirs ?=
+FLAGS.dirs ?=
UID.files ?= 0
GID.files ?= 0
diff --git a/contrib/bmake/mk/meta.autodep.mk b/contrib/bmake/mk/meta.autodep.mk
index 9cb3f14ea0c6..5e18c35fa560 100644
--- a/contrib/bmake/mk/meta.autodep.mk
+++ b/contrib/bmake/mk/meta.autodep.mk
@@ -1,4 +1,4 @@
-# $Id: meta.autodep.mk,v 1.50 2018/06/08 01:25:31 sjg Exp $
+# $Id: meta.autodep.mk,v 1.52 2020/07/18 05:57:57 sjg Exp $
#
# @(#) Copyright (c) 2010, Simon J. Gerraty
@@ -57,7 +57,7 @@ _OBJTOP ?= ${OBJTOP}
_OBJROOT ?= ${OBJROOT:U${_OBJTOP}}
_DEPENDFILE := ${_CURDIR}/${.MAKE.DEPENDFILE:T}
-.if ${.MAKE.LEVEL} > 0 || ${BUILD_AT_LEVEL0:Uyes:tl} == "yes"
+.if ${.MAKE.LEVEL} > 0
# do not allow auto update if we ever built this dir without filemon
NO_FILEMON_COOKIE = .nofilemon
CLEANFILES += ${NO_FILEMON_COOKIE}
@@ -73,10 +73,8 @@ UPDATE_DEPENDFILE = NO
.endif
.if ${.MAKE.LEVEL} == 0
-.if ${BUILD_AT_LEVEL0:Uyes:tl} == "no"
UPDATE_DEPENDFILE = NO
.endif
-.endif
.if !exists(${_DEPENDFILE})
_bootstrap_dirdeps = yes
.endif
@@ -283,9 +281,7 @@ ${_DEPENDFILE}: ${_depend} ${.PARSEDIR}/gendirdeps.mk ${META2DEPS} $${.MAKE.MET
.endif
.if ${_bootstrap_dirdeps} == "yes"
-.if ${BUILD_AT_LEVEL0:Uno} == "no"
DIRDEPS+= ${RELDIR}.${TARGET_SPEC:U${MACHINE}}
-.endif
# make sure this is included at least once
.include <dirdeps.mk>
.else
@@ -312,7 +308,7 @@ _reldir_finish: .NOMETA
_reldir_failed: .NOMETA
@echo "${TIME_STAMP} Failed ${RELDIR}.${TARGET_SPEC} seconds=$$(( ${now_utc} - ${start_utc} )) ${meta_stats}"
-.if defined(WITH_META_STATS) && ${.MAKE.LEVEL} > 0
+.if !defined(WITHOUT_META_STATS) && ${.MAKE.LEVEL} > 0
.END: _reldir_finish
.ERROR: _reldir_failed
.endif
diff --git a/contrib/bmake/mk/meta.stage.mk b/contrib/bmake/mk/meta.stage.mk
index 3b4624fc4599..c78685760c11 100644
--- a/contrib/bmake/mk/meta.stage.mk
+++ b/contrib/bmake/mk/meta.stage.mk
@@ -1,14 +1,14 @@
-# $Id: meta.stage.mk,v 1.59 2020/04/25 18:18:27 sjg Exp $
+# $Id: meta.stage.mk,v 1.60 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 2011-2017, 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
diff --git a/contrib/bmake/mk/meta.subdir.mk b/contrib/bmake/mk/meta.subdir.mk
index d27de1079f87..39cf875d6b77 100644
--- a/contrib/bmake/mk/meta.subdir.mk
+++ b/contrib/bmake/mk/meta.subdir.mk
@@ -1,4 +1,4 @@
-# $Id: meta.subdir.mk,v 1.11 2015/11/24 22:26:51 sjg Exp $
+# $Id: meta.subdir.mk,v 1.12 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 2010, Simon J. Gerraty
@@ -6,10 +6,10 @@
# 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
@@ -32,7 +32,7 @@ DIRDEPS = ${SUBDIR:N.WAIT:O:u:@d@${RELDIR}/$d@}
.include <meta.autodep.mk>
.else
# this is the cunning bit
-# actually it is probably a bit risky
+# actually it is probably a bit risky
# since we may pickup subdirs which are not relevant
# the alternative is a walk through the tree though
# which is difficult without a sub-make.
diff --git a/contrib/bmake/mk/meta.sys.mk b/contrib/bmake/mk/meta.sys.mk
index a561e04534f8..77b4893a8785 100644
--- a/contrib/bmake/mk/meta.sys.mk
+++ b/contrib/bmake/mk/meta.sys.mk
@@ -1,4 +1,4 @@
-# $Id: meta.sys.mk,v 1.36 2020/05/16 23:21:48 sjg Exp $
+# $Id: meta.sys.mk,v 1.38 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 2010-2020, Simon J. Gerraty
@@ -6,10 +6,10 @@
# 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
@@ -155,12 +155,6 @@ dirdeps:
# the first .MAIN: is what counts
# by default dirdeps is all we want at level0
.MAIN: dirdeps
-# tell dirdeps.mk what we want
-BUILD_AT_LEVEL0 = no
-.endif
-.if ${.TARGETS:Nall} == ""
-# it works best if we do everything via sub-makes
-BUILD_AT_LEVEL0 ?= no
.endif
.endif
diff --git a/contrib/bmake/mk/meta2deps.py b/contrib/bmake/mk/meta2deps.py
index 253287a87d1b..9231003b70df 100755
--- a/contrib/bmake/mk/meta2deps.py
+++ b/contrib/bmake/mk/meta2deps.py
@@ -25,7 +25,7 @@ We only pay attention to a subset of the information in the
'W' files opened for write or read-write,
for filemon V3 and earlier.
-
+
'E' files executed.
'L' files linked
@@ -37,20 +37,20 @@ We only pay attention to a subset of the information in the
"""
RCSid:
- $Id: meta2deps.py,v 1.30 2020/06/08 23:05:00 sjg Exp $
+ $Id: meta2deps.py,v 1.33 2020/08/19 17:51:53 sjg Exp $
- Copyright (c) 2011-2019, Simon J. Gerraty
+ Copyright (c) 2011-2020, Simon J. Gerraty
Copyright (c) 2011-2017, Juniper Networks, 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:
+ modification, are permitted provided that the following conditions
+ are met:
1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
+ notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
+ 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
@@ -62,8 +62,8 @@ RCSid:
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
"""
import os, re, sys
@@ -164,7 +164,7 @@ def sort_unique(list, cmp=None, key=None, reverse=False):
def add_trims(x):
return ['/' + x + '/',
- '/' + x,
+ '/' + x,
x + '/',
x]
@@ -181,7 +181,7 @@ class MetaFile:
obj_deps = []
src_deps = []
file_deps = []
-
+
def __init__(self, name, conf={}):
"""if name is set we will parse it now.
conf can have the follwing keys:
@@ -198,7 +198,7 @@ class MetaFile:
TARGET_SPEC
Sometimes MACHINE isn't enough.
-
+
HOST_TARGET
when we build for the pseudo machine 'host'
the object tree uses HOST_TARGET rather than MACHINE.
@@ -222,7 +222,7 @@ class MetaFile:
debug_out open file to send debug output to (sys.stderr)
"""
-
+
self.name = name
self.debug = getv(conf, 'debug', 0)
self.debug_out = getv(conf, 'debug_out', sys.stderr)
@@ -310,11 +310,11 @@ class MetaFile:
self.obj_deps = []
self.src_deps = []
self.file_deps = []
-
+
def dirdeps(self, sep='\n'):
"""return DIRDEPS"""
return sep.strip() + sep.join(self.obj_deps)
-
+
def src_dirdeps(self, sep='\n'):
"""return SRC_DIRDEPS"""
return sep.strip() + sep.join(self.src_deps)
@@ -333,7 +333,7 @@ class MetaFile:
def seenit(self, dir):
"""rememer that we have seen dir."""
self.seen[dir] = 1
-
+
def add(self, list, data, clue=''):
"""add data to list if it isn't already there."""
if data not in list:
@@ -392,10 +392,10 @@ class MetaFile:
# give a useful clue
print('{}:{}: '.format(self.name, self.line), end=' ', file=sys.stderr)
raise
-
+
def parse(self, name=None, file=None):
"""A meta file looks like:
-
+
# Meta data file "path"
CMD "command-line"
CWD "cwd"
@@ -506,6 +506,8 @@ class MetaFile:
continue
elif w[0] in 'ERWS':
path = w[2]
+ if path == '.':
+ continue
self.parse_path(path, cwd, w[0], w)
if not file:
@@ -601,13 +603,13 @@ class MetaFile:
self.seenit(w[2])
self.seenit(dir)
-
+
def main(argv, klass=MetaFile, xopts='', xoptf=None):
"""Simple driver for class MetaFile.
Usage:
script [options] [key=value ...] "meta" ...
-
+
Options and key=value pairs contribute to the
dictionary passed to MetaFile.
@@ -615,7 +617,7 @@ def main(argv, klass=MetaFile, xopts='', xoptf=None):
add "SRCTOP" to the "SRCTOPS" list.
-C "CURDIR"
-
+
-O "OBJROOT"
add "OBJROOT" to the "OBJROOTS" list.
@@ -626,7 +628,7 @@ def main(argv, klass=MetaFile, xopts='', xoptf=None):
-H "HOST_TARGET"
-D "DPDEPS"
-
+
-d bumps debug level
"""
@@ -666,7 +668,7 @@ def main(argv, klass=MetaFile, xopts='', xoptf=None):
debug = 0
output = True
-
+
opts, args = getopt.getopt(argv[1:], 'a:dS:C:O:R:m:D:H:qT:X:' + xopts)
for o, a in opts:
if o == '-a':
diff --git a/contrib/bmake/mk/meta2deps.sh b/contrib/bmake/mk/meta2deps.sh
index 15e033447100..af79417345ca 100755
--- a/contrib/bmake/mk/meta2deps.sh
+++ b/contrib/bmake/mk/meta2deps.sh
@@ -5,11 +5,11 @@
#
# SYNOPSIS:
# meta2deps.sh SB="SB" "meta" ...
-#
+#
# DESCRIPTION:
# This script looks each "meta" file and extracts the
# information needed to deduce build and src dependencies.
-#
+#
# To do this, we extract the 'CWD' record as well as all the
# syscall traces which describe 'R'ead, 'C'hdir and 'E'xec
# syscalls.
@@ -75,20 +75,20 @@
# RCSid:
-# $Id: meta2deps.sh,v 1.12 2016/12/13 20:44:16 sjg Exp $
+# $Id: meta2deps.sh,v 1.13 2020/08/19 17:51:53 sjg Exp $
# Copyright (c) 2010-2013, Juniper Networks, 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:
+# modification, are permitted provided that the following conditions
+# are met:
# 1. Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
+# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-#
+# 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
@@ -99,14 +99,14 @@
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
meta2src() {
cat /dev/null "$@" |
sed -n '/^R .*\.[chyl]$/s,^..[0-9]* ,,p' |
sort -u
}
-
+
meta2dirs() {
cat /dev/null "$@" |
sed -n '/^R .*\/.*\.[a-z0-9][^\/]*$/s,^..[0-9]* \(.*\)/[^/]*$,\1,p' |
@@ -167,7 +167,7 @@ meta2deps() {
case "$MACHINE" in
host) _ht=$HOST_TARGET;;
esac
-
+
for o in $OBJROOTS
do
case "$MACHINE,/$o/" in
@@ -201,7 +201,7 @@ meta2deps() {
obj_re=
add_list '|' -s '/*' src_re $SRCTOPS
add_list '|' -s '*' obj_re $OBJROOTS
-
+
[ -z "$RELDIR" ] && unset DPDEPS
tf=/tmp/m2d$$-$USER
rm -f $tf.*
@@ -272,10 +272,10 @@ meta2deps() {
eval cwd_$pid=$cwd
continue
;;
- F,*) # $path is new pid
+ F,*) # $path is new pid
eval cwd_$path=$cwd ldir_$path=$ldir
continue
- ;;
+ ;;
*) dir=${path%/*}
case "$path" in
$src_re|$obj_re) ;;
diff --git a/contrib/bmake/mk/mk-files.txt b/contrib/bmake/mk/mk-files.txt
index 4e69dfec0808..282f9b87a63c 100644
--- a/contrib/bmake/mk/mk-files.txt
+++ b/contrib/bmake/mk/mk-files.txt
@@ -14,17 +14,17 @@ Many years ago, when building large software projects, I used GNU make
to simplify developing complex build trees.
Since the early 90's my main development machines, run BSD
-(NetBSD_ to be precise), and the BSD source tree is good example of a
-large software project. It quickly became clear that
-``/usr/share/mk/*.mk`` were a great model, but were quite tightly
-linked to building the BSD tree.
+(NetBSD_ to be precise, and more recently FreeBSD), and the BSD source
+tree is good example of a large software project.
+It quickly became clear that ``/usr/share/mk/*.mk`` were a great
+model, but at the time were quite tightly linked to building the BSD tree.
Much as I liked using NetBSD, my customers were more likely to be
using SunOS, HP-UX etc, so I started on bmake_ and a portable collection
of mk-files (mk.tar.gz_). NetBSD provided much of the original structure.
Since then I've added a lot of features to NetBSD's make and hence to
-bmake which is kept closely in sync. The mk-files however have
+bmake which is kept closely in sync. The mk-files however have
diverged quite a bit, though ideas are still picked up from NetBSD
and FreeBSD.
@@ -59,7 +59,7 @@ in such cases even the ``SRCS`` line is unnecessary as ``prog.mk``
will default it to ``${PROG}.c``.
It is the sensible use of defaults and the plethora of macro modifiers
-provided by bmake_ that allow simple makefiles such as the above
+provided by bmake_ that allow simple makefiles such as the above to
*just work* on many different systems.
@@ -67,7 +67,7 @@ mk-files
========
This section provides a brief description of some of the ``*.mk``
-files.
+files.
sys.mk
------
@@ -76,7 +76,7 @@ When bmake starts, it looks for ``sys.mk`` and reads it before doing
anything else. Thus, this is the place to setup the environment for
everyone else.
-In this distribution, sys.mk avoids doing anything platform dependent.
+In this distribution, ``sys.mk`` avoids doing anything platform dependent.
It is quite short, and includes a number of other files (which may or
may not exists)
@@ -97,7 +97,7 @@ examples/sys.clean-env.mk
PATH HOME USER LOGNAME \
SRCTOP OBJTOP OBJROOT \
${_env_vars}
-
+
_env_vars != env | egrep '^(${MAKE_SAVE_ENV_PREFIX:ts|})' | sed 's,=.*,,'; echo
_export_list =
.for v in ${MAKE_SAVE_ENV_VARS:O:u}
@@ -169,7 +169,7 @@ lib${LIB}.a
An archive lib of ``.o`` files, this is the default
lib${LIB}_p.a
- A profiled lib of ``.po`` files.
+ A profiled lib of ``.po`` files.
Still an archive lib, but all the objects are built with
profiling in mind - hence the different extension.
It is skipped if ``MKPROFILE`` is "no".
@@ -194,7 +194,7 @@ lib${LIB}.${LD_so}
There is a lot of platform specific tweaking in ``lib.mk``, largely the
result of the original distributions trying to avoid interfering with
-the system's ``sys.mk``.
+the system's ``sys.mk``.
libnames.mk
-----------
@@ -204,22 +204,19 @@ include ``*.libnames.mk`` of which:
local.libnames.mk
does not exist unless you create it. It is a handy way for you
- to customize without touching the distributed files.
+ to customize without touching the distributed files.
For example, on a test machine I needed to build openssl but
- not install it, so put the following in ``local.libnames.mk``::
+ not install it, so put the following in ``local.libnames.mk``::
.if ${host_os} == "sunos"
LIBCRYPTO = ${OBJTOP}/openssl/lib/crypto/libcrypto${DLIBEXT}
LIBSSL = ${OBJTOP}/openssl/lib/ssl/libssl${DLIBEXT}
INCLUDES_libcrypto = -I${OBJ_libcrypto}
.endif
-
+
The makefile created an openssl dir in ``${OBJ_libcrypto}`` to
gather all the headers. dpadd.mk_ did the rest.
-sjg.libnames.mk
- not part of the mk-files distribution.
-
host.libnames.mk
contains logic to find any libs named in ``HOST_LIBS`` in
``HOST_LIBDIRS``.
@@ -248,7 +245,7 @@ else in various ways::
# it also has the same effect as SRC_LIBS
DPADD += ${LIBSSLFD}
- # indicate that not only must libsslfd be built,
+ # indicate that not only must libsslfd be built,
# but that we need to link with it.
# this is almost exactly equivalent to
# DPADD += ${LIBSSLFD}
@@ -324,7 +321,7 @@ If ``SUBDIR_MUST_EXIST`` is set, missing directories cause an error,
otherwise a warning is issued. If you don't even want the warning,
set ``MISSING_DIR=continue``.
-Traditionally, ``subdir.mk`` prints clue as it visits each subdir::
+Traditionally, ``subdir.mk`` prints clues as it visits each subdir::
===> ssl
===> ssl/lib
@@ -342,7 +339,7 @@ links.mk
Provides rules for processing lists of ``LINKS`` and ``SYMLINKS``.
Each is expected to be a list of ``link`` and ``target`` pairs
-(``link`` -> ``target``).
+(``link`` -> ``target``).
The logic is generally in a ``_*_SCRIPT`` which is referenced in a
``_*_USE`` (``.USE``) target.
@@ -353,7 +350,7 @@ For example::
SYMLINKS += ${.CURDIR}/${MACHINE_ARCH}/include machine
header_links: _BUILD_SYMLINKS_USE
-
+
md.o: header_links
would create a symlink called ``machine`` in ``${.OBJDIR}`` pointing to
@@ -394,10 +391,10 @@ post process the ``.d`` files into ``.depend``.
auto.dep.mk
-----------
-A much simpler implementation than autodep.mk_ it uses
+A much simpler implementation than autodep.mk_ it uses
``-MF ${.TARGET:T}.d``
-to avoid possible conflicts during parallel builds.
-This precludes the use of suffix rules to drive ``make depend``, so
+to avoid possible conflicts during parallel builds.
+This precludes the use of suffix rules to drive ``make depend``, so
dep.mk_ handles that if specifically requested.
options.mk
@@ -452,7 +449,7 @@ on sparc, ``-Wno-unused`` would replace ``-Wunused``.
You should never need to edit ``warnings.mk``, it will include
``warnings-sets.mk`` if it exists and you use that to make any local
-customizations.
+customizations.
rst2htm.mk
----------
@@ -470,7 +467,7 @@ Meta mode
=========
The 20110505 and later versions of ``mk-files`` include a number of
-makefiles contributed by Juniper Networks, Inc.
+makefiles contributed by Juniper Networks, Inc.
These allow the latest version of bmake_ to run in `meta mode`_
see `dirdeps.mk`_
@@ -499,5 +496,5 @@ where you unpacked the tar file, you can::
.. _mk.tar.gz: http://www.crufty.net/ftp/pub/sjg/mk.tar.gz
:Author: sjg@crufty.net
-:Revision: $Id: mk-files.txt,v 1.18 2018/12/08 07:27:15 sjg Exp $
+:Revision: $Id: mk-files.txt,v 1.20 2020/08/19 17:51:53 sjg Exp $
:Copyright: Crufty.NET
diff --git a/contrib/bmake/mk/mkopt.sh b/contrib/bmake/mk/mkopt.sh
index 929a5aa83a66..4a42c0ddf122 100755
--- a/contrib/bmake/mk/mkopt.sh
+++ b/contrib/bmake/mk/mkopt.sh
@@ -1,16 +1,16 @@
#!/bin/sh
-# $Id: mkopt.sh,v 1.12 2020/06/23 04:16:35 sjg Exp $
+# $Id: mkopt.sh,v 1.13 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 2014, 2020, 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
diff --git a/contrib/bmake/mk/obj.mk b/contrib/bmake/mk/obj.mk
index b936379d4f6b..487e25a55b6c 100644
--- a/contrib/bmake/mk/obj.mk
+++ b/contrib/bmake/mk/obj.mk
@@ -1,14 +1,14 @@
-# $Id: obj.mk,v 1.15 2012/11/11 22:37:02 sjg Exp $
+# $Id: obj.mk,v 1.16 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 1999-2010, 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
diff --git a/contrib/bmake/mk/options.mk b/contrib/bmake/mk/options.mk
index a03c6cde34dc..eb5253a6b7e8 100644
--- a/contrib/bmake/mk/options.mk
+++ b/contrib/bmake/mk/options.mk
@@ -1,14 +1,14 @@
-# $Id: options.mk,v 1.11 2020/05/02 21:23:52 sjg Exp $
+# $Id: options.mk,v 1.13 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 2012, 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
@@ -77,4 +77,6 @@ ${OPTION_PREFIX}${o:H} ?= no
${OPTION_PREFIX}${o:H} ?= ${${OPTION_PREFIX}${o:T}}
.endif
.endfor
-.undef OPTIONS_DEFAULT_VALUES OPTIONS_DEFAULT_NO OPTIONS_DEFAULT_YES
+.undef OPTIONS_DEFAULT_VALUES
+.undef OPTIONS_DEFAULT_NO
+.undef OPTIONS_DEFAULT_YES
diff --git a/contrib/bmake/mk/own.mk b/contrib/bmake/mk/own.mk
index 4c8425b0ee7c..b20b9e5e2c35 100644
--- a/contrib/bmake/mk/own.mk
+++ b/contrib/bmake/mk/own.mk
@@ -1,4 +1,4 @@
-# $Id: own.mk,v 1.40 2018/04/23 04:53:57 sjg Exp $
+# $Id: own.mk,v 1.41 2020/08/19 17:51:53 sjg Exp $
.if !target(__${.PARSEFILE}__)
__${.PARSEFILE}__:
@@ -61,7 +61,7 @@ YACC.y?= ${YACC} ${YFLAGS}
# for suffix rules
IMPFLAGS?= ${COPTS.${.IMPSRC:T}} ${CPUFLAGS.${.IMPSRC:T}} ${CPPFLAGS.${.IMPSRC:T}}
-.for s in .c .cc
+.for s in .c .cc
COMPILE.$s += ${IMPFLAGS}
LINK.$s += ${IMPFLAGS}
.endfor
@@ -211,8 +211,8 @@ CFLAGS+= ${CPPFLAGS}
# allow for per target flags
# apply the :T:R first, so the more specific :T can override if needed
-CPPFLAGS += ${CPPFLAGS_${.TARGET:T:R}} ${CPPFLAGS_${.TARGET:T}}
-CFLAGS += ${CFLAGS_${.TARGET:T:R}} ${CFLAGS_${.TARGET:T}}
+CPPFLAGS += ${CPPFLAGS_${.TARGET:T:R}} ${CPPFLAGS_${.TARGET:T}}
+CFLAGS += ${CFLAGS_${.TARGET:T:R}} ${CFLAGS_${.TARGET:T}}
# Define SYS_INCLUDE to indicate whether you want symbolic links to the system
# source (``symlinks''), or a separate copy (``copies''); (latter useful
diff --git a/contrib/bmake/mk/prlist.mk b/contrib/bmake/mk/prlist.mk
index 09d7dfdacc97..aca1fde25555 100644
--- a/contrib/bmake/mk/prlist.mk
+++ b/contrib/bmake/mk/prlist.mk
@@ -1,14 +1,14 @@
-# $Id: prlist.mk,v 1.3 2008/07/17 16:24:57 sjg Exp $
+# $Id: prlist.mk,v 1.4 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 2006, 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
diff --git a/contrib/bmake/mk/prog.mk b/contrib/bmake/mk/prog.mk
index 4bc6260d208b..ea48837d5544 100644
--- a/contrib/bmake/mk/prog.mk
+++ b/contrib/bmake/mk/prog.mk
@@ -1,4 +1,4 @@
-# $Id: prog.mk,v 1.35 2018/01/26 20:04:07 sjg Exp $
+# $Id: prog.mk,v 1.36 2020/08/19 17:51:53 sjg Exp $
.if !target(__${.PARSEFILE}__)
__${.PARSEFILE}__:
@@ -99,7 +99,7 @@ _PROGLDOPTS+= -Wl,-dynamic-linker=${_SHLINKER}
_PROGLDOPTS+= -Wl,-rpath-link,${DESTDIR}${SHLIBDIR}:${DESTDIR}/usr/lib \
-L${DESTDIR}${SHLIBDIR}
.endif
-_PROGLDOPTS+= -Wl,-rpath,${SHLIBDIR}:/usr/lib
+_PROGLDOPTS+= -Wl,-rpath,${SHLIBDIR}:/usr/lib
.if defined(PROG_CXX)
_CCLINK= ${CXX}
diff --git a/contrib/bmake/mk/progs.mk b/contrib/bmake/mk/progs.mk
index 7ccebbffb44c..16c381a50bf9 100644
--- a/contrib/bmake/mk/progs.mk
+++ b/contrib/bmake/mk/progs.mk
@@ -1,14 +1,14 @@
-# $Id: progs.mk,v 1.13 2013/08/02 18:28:48 sjg Exp $
+# $Id: progs.mk,v 1.16 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 2006, 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
@@ -87,11 +87,11 @@ x.$p= PROG_CXX=$p
.endif
$p ${p}_p: .PHONY .MAKE
- (cd ${.CURDIR} && ${.MAKE} -f ${MAKEFILE} PROG=$p ${x.$p})
+ (cd ${.CURDIR} && ${.MAKE} -f ${MAKEFILE} PROG=$p ${x.$p} -DWITHOUT_META_STATS)
.for t in ${PROGS_TARGETS:O:u}
$p.$t: .PHONY .MAKE
- (cd ${.CURDIR} && ${.MAKE} -f ${MAKEFILE} PROG=$p ${x.$p} ${@:E})
+ (cd ${.CURDIR} && ${.MAKE} -f ${MAKEFILE} PROG=$p ${x.$p} ${@:E} -DWITHOUT_META_STATS)
.endfor
.endfor
@@ -99,4 +99,9 @@ $p.$t: .PHONY .MAKE
$t: ${PROGS:%=%.$t}
.endfor
+.if !defined(WITHOUT_META_STATS) && ${.MAKE.LEVEL} > 0
+.END: _reldir_finish
+.ERROR: _reldir_failed
+.endif
+
.endif
diff --git a/contrib/bmake/mk/rst2htm.mk b/contrib/bmake/mk/rst2htm.mk
index 296b73c68574..1db9792f4127 100644
--- a/contrib/bmake/mk/rst2htm.mk
+++ b/contrib/bmake/mk/rst2htm.mk
@@ -1,14 +1,14 @@
-# $Id: rst2htm.mk,v 1.10 2015/09/08 22:17:46 sjg Exp $
+# $Id: rst2htm.mk,v 1.11 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 2009, 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
diff --git a/contrib/bmake/mk/scripts.mk b/contrib/bmake/mk/scripts.mk
index 9b6d82f49253..5ea2474e65a3 100644
--- a/contrib/bmake/mk/scripts.mk
+++ b/contrib/bmake/mk/scripts.mk
@@ -1,14 +1,14 @@
-# $Id: scripts.mk,v 1.3 2017/05/06 17:29:45 sjg Exp $
+# $Id: scripts.mk,v 1.4 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 2006, 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
diff --git a/contrib/bmake/mk/srctop.mk b/contrib/bmake/mk/srctop.mk
index fab090c80d39..91594c7a98e4 100644
--- a/contrib/bmake/mk/srctop.mk
+++ b/contrib/bmake/mk/srctop.mk
@@ -1,14 +1,14 @@
-# $Id: srctop.mk,v 1.3 2012/11/11 23:20:18 sjg Exp $
+# $Id: srctop.mk,v 1.4 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 2012, 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
@@ -46,7 +46,7 @@ SRCTOP!= cd ${.CURDIR}; while :; do \
here=`pwd`; \
${_SRCTOP_TEST_} && { echo $$here; break; }; \
case $$here in /*/*/*) cd ..;; *) echo ""; break;; esac; \
- done
+ done
.endif
.if defined(SRCTOP) && exists(${SRCTOP}/.)
.export SRCTOP
diff --git a/contrib/bmake/mk/stage-install.sh b/contrib/bmake/mk/stage-install.sh
index 64d044fa048c..674652d1d482 100755
--- a/contrib/bmake/mk/stage-install.sh
+++ b/contrib/bmake/mk/stage-install.sh
@@ -28,22 +28,26 @@
# "file".dirdep placed in "dest" or "dest".dirdep if it happed
# to be a file rather than a directory.
#
+# Before we run install(1), we check if "dest" needs to be a
+# directory (more than one file in "args") and create it
+# if necessary.
+#
# SEE ALSO:
# meta.stage.mk
-#
+#
# RCSid:
-# $Id: stage-install.sh,v 1.5 2013/04/19 16:32:24 sjg Exp $
+# $Id: stage-install.sh,v 1.9 2020/08/28 01:04:13 sjg Exp $
#
-# @(#) Copyright (c) 2013, Simon J. Gerraty
+# @(#) Copyright (c) 2013-2020, 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
@@ -59,6 +63,45 @@ do
esac
done
+# get last entry from "$@" without side effects
+last_entry() {
+ while [ $# -gt 8 ]
+ do
+ shift 8
+ done
+ eval last=\$$#
+ echo $last
+}
+
+# mkdir $dest if needed (more than one file)
+mkdir_if_needed() {
+ (
+ lf=
+ while [ $# -gt 8 ]
+ do
+ shift 4
+ done
+ for f in "$@"
+ do
+ [ -f $f ] || continue
+ [ $f = $dest ] && continue
+ if [ -n "$lf" ]; then
+ # dest must be a directory
+ mkdir -p $dest
+ break
+ fi
+ lf=$f
+ done
+ )
+}
+
+args="$@"
+dest=`last_entry "$@"`
+case " $args " in
+*" -d "*) ;;
+*) [ -e $dest ] || mkdir_if_needed "$@";;
+esac
+
# if .dirdep doesn't exist, just run install and be done
_DIRDEP=${_DIRDEP:-$OBJDIR/.dirdep}
[ -s $_DIRDEP ] && EXEC= || EXEC=exec
@@ -80,12 +123,6 @@ StageDirdep() {
LnCp $_DIRDEP $t.dirdep || exit 1
}
-args="$@"
-while [ $# -gt 8 ]
-do
- shift 8
-done
-eval dest=\$$#
if [ -f $dest ]; then
# a file, there can be only one .dirdep needed
StageDirdep $dest
diff --git a/contrib/bmake/mk/sys.clean-env.mk b/contrib/bmake/mk/sys.clean-env.mk
index 5ac74bba9342..88d32cb3c6e9 100644
--- a/contrib/bmake/mk/sys.clean-env.mk
+++ b/contrib/bmake/mk/sys.clean-env.mk
@@ -1,14 +1,14 @@
-# $Id: sys.clean-env.mk,v 1.22 2017/10/25 23:44:20 sjg Exp $
+# $Id: sys.clean-env.mk,v 1.23 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 2009, 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
@@ -55,7 +55,7 @@ MAKE_ENV_SAVE_PREFIX_LIST += \
MAKE_ENV_SAVE_EXCLUDE_LIST ?= _
# This is the actual list that we will save
-# HOME is probably something worth clobbering eg.
+# HOME is probably something worth clobbering eg.
# HOME=/var/empty
MAKE_ENV_SAVE_VAR_LIST += \
HOME \
diff --git a/contrib/bmake/mk/sys.debug.mk b/contrib/bmake/mk/sys.debug.mk
index 7debfc6cf051..7fde27c24fdb 100644
--- a/contrib/bmake/mk/sys.debug.mk
+++ b/contrib/bmake/mk/sys.debug.mk
@@ -1,14 +1,14 @@
-# $Id: sys.debug.mk,v 1.1 2016/10/01 19:11:55 sjg Exp $
+# $Id: sys.debug.mk,v 1.2 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 2009, 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
diff --git a/contrib/bmake/mk/sys.dependfile.mk b/contrib/bmake/mk/sys.dependfile.mk
index 5389c24fd1bb..7c1fd94d3eb8 100644
--- a/contrib/bmake/mk/sys.dependfile.mk
+++ b/contrib/bmake/mk/sys.dependfile.mk
@@ -1,14 +1,14 @@
-# $Id: sys.dependfile.mk,v 1.8 2016/03/11 01:34:13 sjg Exp $
+# $Id: sys.dependfile.mk,v 1.9 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 2012, 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
diff --git a/contrib/bmake/mk/sys.mk b/contrib/bmake/mk/sys.mk
index 37e37390eb27..b21dfa8b4ebf 100644
--- a/contrib/bmake/mk/sys.mk
+++ b/contrib/bmake/mk/sys.mk
@@ -1,14 +1,14 @@
-# $Id: sys.mk,v 1.47 2020/04/17 21:08:17 sjg Exp $
+# $Id: sys.mk,v 1.51 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 2003-2009, 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
@@ -77,6 +77,7 @@ OPTIONS_DEFAULT_DEPENDENT += \
AUTO_OBJ/DIRDEPS_BUILD \
META_MODE/DIRDEPS_BUILD \
STAGING/DIRDEPS_BUILD \
+ STATIC_DIRDEPS_CACHE/DIRDEPS_CACHE \
.-include <options.mk>
@@ -116,7 +117,7 @@ ROOT_GROUP != sed -n /:0:/s/:.*//p /etc/group
unix ?= We run ${_HOST_OSNAME}.
-# We need a Bourne/POSIX shell
+# We need a Bourne/POSIX shell
MAKE_SHELL ?= sh
SHELL ?= ${MAKE_SHELL}
diff --git a/contrib/bmake/mk/sys.vars.mk b/contrib/bmake/mk/sys.vars.mk
index 2f2e66700266..24e0ed26a15f 100644
--- a/contrib/bmake/mk/sys.vars.mk
+++ b/contrib/bmake/mk/sys.vars.mk
@@ -1,20 +1,20 @@
-# $Id: sys.vars.mk,v 1.4 2019/05/27 20:22:52 sjg Exp $
+# $Id: sys.vars.mk,v 1.5 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 2003-2009, 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
# We use the following paradigm for preventing multiple inclusion.
-# It relies on the fact that conditionals and dependencies are resolved
+# It relies on the fact that conditionals and dependencies are resolved
# at the time they are read.
#
# _this ?= ${.PARSEFILE}
diff --git a/contrib/bmake/mk/sys/AIX.mk b/contrib/bmake/mk/sys/AIX.mk
index b848d99fb4e8..d591385be603 100644
--- a/contrib/bmake/mk/sys/AIX.mk
+++ b/contrib/bmake/mk/sys/AIX.mk
@@ -28,9 +28,9 @@ CC ?= gcc
DBG ?= -O -g
STATIC ?= -static
.else
-CC ?= cc
+CC ?= cc
DBG ?= -g
-STATIC ?=
+STATIC ?=
.endif
CFLAGS ?= ${DBG}
COMPILE.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} -c
@@ -138,7 +138,7 @@ ${CXX_SUFFIXES:%=%.a}:
.s:
${LINK.s} -o ${.TARGET} ${.IMPSRC} ${LDLIBS}
.s.o:
- ${COMPILE.s} -o ${.TARGET} ${.IMPSRC}
+ ${COMPILE.s} -o ${.TARGET} ${.IMPSRC}
.s.a:
${COMPILE.s} ${.IMPSRC}
${AR} ${ARFLAGS} $@ $*.o
@@ -162,7 +162,7 @@ ${CXX_SUFFIXES:%=%.a}:
mv lex.yy.c ${.TARGET}
.l.o:
${LEX.l} ${.IMPSRC}
- ${COMPILE.c} -o ${.TARGET} lex.yy.c
+ ${COMPILE.c} -o ${.TARGET} lex.yy.c
rm -f lex.yy.c
# Yacc
diff --git a/contrib/bmake/mk/sys/Darwin.mk b/contrib/bmake/mk/sys/Darwin.mk
index ea04b416ff3a..06918a11a4ad 100644
--- a/contrib/bmake/mk/sys/Darwin.mk
+++ b/contrib/bmake/mk/sys/Darwin.mk
@@ -176,7 +176,7 @@ ${CXX_SUFFIXES:%=%.a}:
.s:
${LINK.s} -o ${.TARGET} ${.IMPSRC} ${LDLIBS}
.s.o:
- ${COMPILE.s} -o ${.TARGET} ${.IMPSRC}
+ ${COMPILE.s} -o ${.TARGET} ${.IMPSRC}
.s.a:
${COMPILE.s} ${.IMPSRC}
${AR} ${ARFLAGS} $@ $*.o
@@ -200,7 +200,7 @@ ${CXX_SUFFIXES:%=%.a}:
mv lex.yy.c ${.TARGET}
.l.o:
${LEX.l} ${.IMPSRC}
- ${COMPILE.c} -o ${.TARGET} lex.yy.c
+ ${COMPILE.c} -o ${.TARGET} lex.yy.c
rm -f lex.yy.c
# Yacc
diff --git a/contrib/bmake/mk/sys/Generic.mk b/contrib/bmake/mk/sys/Generic.mk
index 9a3d3bffc183..51c72990f2ea 100644
--- a/contrib/bmake/mk/sys/Generic.mk
+++ b/contrib/bmake/mk/sys/Generic.mk
@@ -1,4 +1,4 @@
-# $Id: Generic.mk,v 1.16 2020/06/29 14:34:42 sjg Exp $
+# $Id: Generic.mk,v 1.17 2020/08/19 17:51:53 sjg Exp $
#
# some reasonable defaults
@@ -31,7 +31,7 @@ ARFLAGS ?= rl
AS ?= as
AFLAGS ?=
-.if ${MACHINE_ARCH} == "sparc64"
+.if ${MACHINE_ARCH} == "sparc64"
AFLAGS+= -Wa,-Av9a
.endif
COMPILE.s ?= ${CC} ${AFLAGS} -c
@@ -56,7 +56,7 @@ COMPILE.m ?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} -c
LINK.m ?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} ${LDFLAGS}
CPP ?= cpp
-CPPFLAGS ?=
+CPPFLAGS ?=
FC ?= f77
FFLAGS ?= -O
@@ -181,7 +181,7 @@ ${CXX_SUFFIXES:%=%.a}:
mv lex.yy.c ${.TARGET}
.l.o:
${LEX.l} ${.IMPSRC}
- ${COMPILE.c} -o ${.TARGET} lex.yy.c
+ ${COMPILE.c} -o ${.TARGET} lex.yy.c
rm -f lex.yy.c
# Yacc
diff --git a/contrib/bmake/mk/sys/HP-UX.mk b/contrib/bmake/mk/sys/HP-UX.mk
index 34dd2881d83d..f1c23148c186 100644
--- a/contrib/bmake/mk/sys/HP-UX.mk
+++ b/contrib/bmake/mk/sys/HP-UX.mk
@@ -1,4 +1,4 @@
-# $Id: HP-UX.mk,v 1.14 2020/06/29 14:34:42 sjg Exp $
+# $Id: HP-UX.mk,v 1.15 2020/08/19 17:51:53 sjg Exp $
# $NetBSD: sys.mk,v 1.19.2.1 1994/07/26 19:58:31 cgd Exp $
# @(#)sys.mk 5.11 (Berkeley) 3/13/91
@@ -8,7 +8,7 @@ unix ?= We run ${OS}.
# HP-UX's cc does not provide any clues as to wether this is 9.x or 10.x
# nor does sys/param.h, so we'll use the existence of /hp-ux
-.if exists("/hp-ux")
+.if exists("/hp-ux")
OSMAJOR ?=9
.endif
OSMAJOR ?=10
@@ -60,7 +60,7 @@ PICFLAG ?= +z
LD_x=
DBG ?=-g -O
.endif
-DBG ?=
+DBG ?=
STATIC ?= -Wl,-a,archive
.endif
.if (${__HPUX_VERSION} == "10")
@@ -180,7 +180,7 @@ ${CXX_SUFFIXES:%=%.a}:
.s:
${LINK.s} -o ${.TARGET} ${.IMPSRC} ${LDLIBS}
.s.o:
- ${COMPILE.s} -o ${.TARGET} ${.IMPSRC}
+ ${COMPILE.s} -o ${.TARGET} ${.IMPSRC}
.s.a:
${COMPILE.s} ${.IMPSRC}
${AR} ${ARFLAGS} $@ $*.o
@@ -204,7 +204,7 @@ ${CXX_SUFFIXES:%=%.a}:
mv lex.yy.c ${.TARGET}
.l.o:
${LEX.l} ${.IMPSRC}
- ${COMPILE.c} -o ${.TARGET} lex.yy.c
+ ${COMPILE.c} -o ${.TARGET} lex.yy.c
rm -f lex.yy.c
# Yacc
diff --git a/contrib/bmake/mk/sys/IRIX.mk b/contrib/bmake/mk/sys/IRIX.mk
index a194c1e78a11..00af15027f6e 100644
--- a/contrib/bmake/mk/sys/IRIX.mk
+++ b/contrib/bmake/mk/sys/IRIX.mk
@@ -44,7 +44,7 @@ COMPILE.m ?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} -c
LINK.m ?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} ${LDFLAGS}
CPP ?= CC
-CPPFLAGS ?=
+CPPFLAGS ?=
FC ?= f77
FFLAGS ?= -O
@@ -173,7 +173,7 @@ ${CXX_SUFFIXES:%=%.a}:
mv lex.yy.c ${.TARGET}
.l.o:
${LEX.l} ${.IMPSRC}
- ${COMPILE.c} -o ${.TARGET} lex.yy.c
+ ${COMPILE.c} -o ${.TARGET} lex.yy.c
rm -f lex.yy.c
# Yacc
diff --git a/contrib/bmake/mk/sys/Linux.mk b/contrib/bmake/mk/sys/Linux.mk
index bf80bb3addce..3cdc4dbe1a62 100644
--- a/contrib/bmake/mk/sys/Linux.mk
+++ b/contrib/bmake/mk/sys/Linux.mk
@@ -1,4 +1,4 @@
-# $Id: Linux.mk,v 1.12 2020/06/29 14:34:42 sjg Exp $
+# $Id: Linux.mk,v 1.13 2020/08/19 17:51:53 sjg Exp $
# $NetBSD: sys.mk,v 1.19.2.1 1994/07/26 19:58:31 cgd Exp $
# @(#)sys.mk 5.11 (Berkeley) 3/13/91
@@ -141,7 +141,7 @@ ${CXX_SUFFIXES:%=%.a}:
.s:
${LINK.s} -o ${.TARGET} ${.IMPSRC} ${LDLIBS}
.s.o:
- ${COMPILE.s} -o ${.TARGET} ${.IMPSRC}
+ ${COMPILE.s} -o ${.TARGET} ${.IMPSRC}
.s.a:
${COMPILE.s} ${.IMPSRC}
${AR} ${ARFLAGS} $@ $*.o
@@ -165,7 +165,7 @@ ${CXX_SUFFIXES:%=%.a}:
mv lex.yy.c ${.TARGET}
.l.o:
${LEX.l} ${.IMPSRC}
- ${COMPILE.c} -o ${.TARGET} lex.yy.c
+ ${COMPILE.c} -o ${.TARGET} lex.yy.c
rm -f lex.yy.c
# Yacc
diff --git a/contrib/bmake/mk/sys/NetBSD.mk b/contrib/bmake/mk/sys/NetBSD.mk
index 2e72cc742e4f..6629a4445a2e 100644
--- a/contrib/bmake/mk/sys/NetBSD.mk
+++ b/contrib/bmake/mk/sys/NetBSD.mk
@@ -79,7 +79,7 @@ COMPILE.m ?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} -c
LINK.m ?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} ${LDFLAGS}
CPP ?= cpp
-CPPFLAGS ?=
+CPPFLAGS ?=
FC ?= f77
FFLAGS ?= -O
@@ -208,7 +208,7 @@ ${CXX_SUFFIXES:%=%.a}:
mv lex.yy.c ${.TARGET}
.l.o:
${LEX.l} ${.IMPSRC}
- ${COMPILE.c} -o ${.TARGET} lex.yy.c
+ ${COMPILE.c} -o ${.TARGET} lex.yy.c
rm -f lex.yy.c
# Yacc
diff --git a/contrib/bmake/mk/sys/OSF1.mk b/contrib/bmake/mk/sys/OSF1.mk
index 4c1a09f6f5f7..88e0ea28b930 100644
--- a/contrib/bmake/mk/sys/OSF1.mk
+++ b/contrib/bmake/mk/sys/OSF1.mk
@@ -1,4 +1,4 @@
-# $Id: OSF1.mk,v 1.11 2020/06/29 14:34:42 sjg Exp $
+# $Id: OSF1.mk,v 1.12 2020/08/19 17:51:53 sjg Exp $
# $NetBSD: sys.mk,v 1.19.2.1 1994/07/26 19:58:31 cgd Exp $
# @(#)sys.mk 5.11 (Berkeley) 3/13/91
@@ -31,7 +31,7 @@ LINK.s ?= ${CC} ${AFLAGS} ${LDFLAGS}
COMPILE.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} -c
LINK.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} ${LDFLAGS}
.if exists(/opt/gnu/bin/gcc) || exists(/usr/local/bin/gcc)
-CC ?= gcc
+CC ?= gcc
.else
CC ?= cc -std
.endif
@@ -176,7 +176,7 @@ ${CXX_SUFFIXES:%=%.a}:
mv lex.yy.c ${.TARGET}
.l.o:
${LEX.l} ${.IMPSRC}
- ${COMPILE.c} -o ${.TARGET} lex.yy.c
+ ${COMPILE.c} -o ${.TARGET} lex.yy.c
rm -f lex.yy.c
# Yacc
diff --git a/contrib/bmake/mk/sys/OpenBSD.mk b/contrib/bmake/mk/sys/OpenBSD.mk
index d73bb0d82c9a..7440a231e3bf 100644
--- a/contrib/bmake/mk/sys/OpenBSD.mk
+++ b/contrib/bmake/mk/sys/OpenBSD.mk
@@ -21,7 +21,7 @@ RANLIB ?= ranlib
AS ?= as
AFLAGS ?=
-.if ${MACHINE_ARCH} == "sparc64"
+.if ${MACHINE_ARCH} == "sparc64"
AFLAGS+= -Wa,-Av9a
.endif
COMPILE.s ?= ${CC} ${AFLAGS} -c
@@ -54,7 +54,7 @@ COMPILE.m ?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} -c
LINK.m ?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} ${LDFLAGS}
CPP ?= cpp
-CPPFLAGS ?=
+CPPFLAGS ?=
FC ?= f77
FFLAGS ?= -O
@@ -183,7 +183,7 @@ ${CXX_SUFFIXES:%=%.a}:
mv lex.yy.c ${.TARGET}
.l.o:
${LEX.l} ${.IMPSRC}
- ${COMPILE.c} -o ${.TARGET} lex.yy.c
+ ${COMPILE.c} -o ${.TARGET} lex.yy.c
rm -f lex.yy.c
# Yacc
diff --git a/contrib/bmake/mk/sys/SunOS.mk b/contrib/bmake/mk/sys/SunOS.mk
index e79cd3a7dbd4..4369c8d43b93 100644
--- a/contrib/bmake/mk/sys/SunOS.mk
+++ b/contrib/bmake/mk/sys/SunOS.mk
@@ -1,4 +1,4 @@
-# $Id: SunOS.mk,v 1.11 2020/06/29 14:34:42 sjg Exp $
+# $Id: SunOS.mk,v 1.12 2020/08/19 17:51:53 sjg Exp $
.if ${.PARSEFILE} == "sys.mk"
.include <host-target.mk>
@@ -197,7 +197,7 @@ ${CXX_SUFFIXES:%=%.a}:
mv lex.yy.c ${.TARGET}
.l.o:
${LEX.l} ${.IMPSRC}
- ${COMPILE.c} -o ${.TARGET} lex.yy.c
+ ${COMPILE.c} -o ${.TARGET} lex.yy.c
rm -f lex.yy.c
# Yacc
diff --git a/contrib/bmake/mk/sys/UnixWare.mk b/contrib/bmake/mk/sys/UnixWare.mk
index 888f0d90c0b4..272d3e65c2d8 100644
--- a/contrib/bmake/mk/sys/UnixWare.mk
+++ b/contrib/bmake/mk/sys/UnixWare.mk
@@ -1,4 +1,4 @@
-# $Id: UnixWare.mk,v 1.6 2020/06/29 14:34:42 sjg Exp $
+# $Id: UnixWare.mk,v 1.7 2020/08/19 17:51:53 sjg Exp $
# based on "Id: SunOS.5.sys.mk,v 1.6 2003/09/30 16:42:23 sjg Exp "
# $NetBSD: sys.mk,v 1.19.2.1 1994/07/26 19:58:31 cgd Exp $
# @(#)sys.mk 5.11 (Berkeley) 3/13/91
@@ -219,7 +219,7 @@ ${CXX_SUFFIXES:%=%.a}:
mv lex.yy.c ${.TARGET}
.l.o:
${LEX.l} ${.IMPSRC}
- ${COMPILE.c} -o ${.TARGET} lex.yy.c
+ ${COMPILE.c} -o ${.TARGET} lex.yy.c
rm -f lex.yy.c
# Yacc
diff --git a/contrib/bmake/mk/target-flags.mk b/contrib/bmake/mk/target-flags.mk
index 4525dbd08d7b..789f09b23115 100644
--- a/contrib/bmake/mk/target-flags.mk
+++ b/contrib/bmake/mk/target-flags.mk
@@ -27,27 +27,27 @@
# variables. The original version of this macro file did
# elaborate things with CFLAGS. The current, simpler
# implementation is ultimately more flexible.
-#
+#
# It is important that target-flags.mk is included after other
# macro files and that target specific flags that may reference
# _$v are set after that.
-#
+#
# Only works with a make(1) that does nested evaluation correctly.
# RCSid:
-# $Id: target-flags.mk,v 1.9 2014/04/05 22:56:54 sjg Exp $
+# $Id: target-flags.mk,v 1.10 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 1998-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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
diff --git a/contrib/bmake/mk/warnings.mk b/contrib/bmake/mk/warnings.mk
index 7fb3ebd4a8f8..77635fbc8a29 100644
--- a/contrib/bmake/mk/warnings.mk
+++ b/contrib/bmake/mk/warnings.mk
@@ -1,15 +1,15 @@
# RCSid:
-# $Id: warnings.mk,v 1.14 2016/04/05 15:58:37 sjg Exp $
+# $Id: warnings.mk,v 1.15 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
@@ -28,10 +28,10 @@ MIN_WARNINGS?= -Wall \
-Wformat \
-Wimplicit \
-Wunused \
- -Wuninitialized
+ -Wuninitialized
LOW_WARNINGS?= ${MIN_WARNINGS} -W -Wstrict-prototypes -Wmissing-prototypes
-
+
MEDIUM_WARNINGS?= ${LOW_WARNINGS} -Werror
HIGH_WARNINGS?= ${MEDIUM_WARNINGS} \
@@ -84,7 +84,7 @@ W_uninitialized=
# .for loops have the [dis]advantage of being evaluated when read,
-# so adding to WARNINGS_SET[_${MACHINE_ARCH}] after this file is
+# so adding to WARNINGS_SET[_${MACHINE_ARCH}] after this file is
# read has no effect.
# Replacing the above .for loops with the WARNINGS+= below solves that
# but tiggers a double free bug in bmake-20040118 and earlier.
@@ -95,11 +95,11 @@ W_uninitialized=
# In the second :@ "loop", the ::?= noise sets W_foo?=-Wfoo etc
# which makes it easy to turn off override individual flags
# (see W_uninitialized above).
-#
+#
# The last bit expands to ${W_foo_${.TARGET:T}:U${W_foo}}
# which is the bit we ultimately want. It allows W_* to be set on a
# per target basis.
-#
+#
# NOTE: that we force the target extension to be .o
#
@@ -110,7 +110,7 @@ M_warnings_list = @s@$${$$s_WARNINGS}@:O:u:@w@$${$${w:C/-(.)/\1_/}::?=$$w} $${$$
_warnings = ${WARNINGS_SET_${MACHINE_ARCH}:U${WARNINGS_SET}:${M_warnings_list}}
# now a list of all -Wno-* overrides not just those defined by WARNINGS_SET
# since things like -Wall imply lots of others.
-# this should be a super-set of the -Wno-* in _warnings, but
+# this should be a super-set of the -Wno-* in _warnings, but
# just in case...
_no_warnings = ${_warnings:M-Wno-*} ${ALL_WARNINGS_SETS:${M_warnings_list}:M-Wno-*}
# -Wno-* must follow any others
diff --git a/contrib/bmake/mk/whats.mk b/contrib/bmake/mk/whats.mk
index 1dacb4c14365..e10964463d4a 100644
--- a/contrib/bmake/mk/whats.mk
+++ b/contrib/bmake/mk/whats.mk
@@ -1,20 +1,20 @@
-# $Id: whats.mk,v 1.9 2020/05/09 19:48:53 sjg Exp $
+# $Id: whats.mk,v 1.10 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 2014-2020, 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
.if ${MK_WHATSTRING:Uno} == "yes"
-# it can be useful to embed a what(1) string in binaries
+# it can be useful to embed a what(1) string in binaries
# so that the build location can be seen from a core file.
.if defined(KMOD)
what_thing ?= ${KMOD}
diff --git a/contrib/bmake/mk/yacc.mk b/contrib/bmake/mk/yacc.mk
index 910b004431df..7f7e99578d70 100644
--- a/contrib/bmake/mk/yacc.mk
+++ b/contrib/bmake/mk/yacc.mk
@@ -1,4 +1,4 @@
-# $Id: yacc.mk,v 1.6 2011/06/10 22:45:46 sjg Exp $
+# $Id: yacc.mk,v 1.7 2020/08/19 17:51:53 sjg Exp $
#
# @(#) Copyright (c) 1999-2011, Simon J. Gerraty
@@ -6,10 +6,10 @@
# 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
+# use this file is hereby granted provided that
# the above copyright notice and this notice are
-# left intact.
-#
+# left intact.
+#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
diff --git a/contrib/bmake/nonints.h b/contrib/bmake/nonints.h
index 3126650c9b59..6bcbd7690adb 100644
--- a/contrib/bmake/nonints.h
+++ b/contrib/bmake/nonints.h
@@ -1,4 +1,4 @@
-/* $NetBSD: nonints.h,v 1.78 2020/07/03 07:40:13 rillig Exp $ */
+/* $NetBSD: nonints.h,v 1.102 2020/08/30 19:56:02 rillig Exp $ */
/*-
* Copyright (c) 1988, 1989, 1990, 1993
@@ -73,7 +73,7 @@
*/
/* arch.c */
-ReturnStatus Arch_ParseArchive(char **, Lst, GNode *);
+Boolean Arch_ParseArchive(char **, Lst, GNode *);
void Arch_Touch(GNode *);
void Arch_TouchLib(GNode *);
time_t Arch_MTime(GNode *);
@@ -82,7 +82,7 @@ void Arch_FindLib(GNode *, Lst);
Boolean Arch_LibOODate(GNode *);
void Arch_Init(void);
void Arch_End(void);
-int Arch_IsLib(GNode *);
+Boolean Arch_IsLib(GNode *);
/* compat.c */
int CompatRunCommand(void *, void *);
@@ -91,8 +91,8 @@ int Compat_Make(void *, void *);
/* cond.c */
struct If;
-int Cond_EvalExpression(const struct If *, char *, Boolean *, int, Boolean);
-int Cond_Eval(char *);
+CondEvalResult Cond_EvalExpression(const struct If *, char *, Boolean *, int, Boolean);
+CondEvalResult Cond_Eval(char *);
void Cond_restore_depth(unsigned int);
unsigned int Cond_save_depth(void);
@@ -125,7 +125,6 @@ char *cached_realpath(const char *, char *);
/* parse.c */
void Parse_Error(int, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3);
-Boolean Parse_AnyExport(void);
Boolean Parse_IsVar(char *);
void Parse_DoVar(char *, GNode *);
void Parse_AddIncludeDir(char *);
@@ -136,12 +135,24 @@ 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 **);
+typedef struct {
+ char **words;
+ size_t len;
+ void *freeIt;
+} Words;
+
+Words Str_Words(const char *, Boolean);
+static inline void MAKE_ATTR_UNUSED
+Words_Free(Words w) {
+ free(w.words);
+ free(w.freeIt);
+}
+
+char *str_concat2(const char *, const char *);
+char *str_concat3(const char *, const char *, const char *);
+char *str_concat4(const char *, const char *, const char *, const char *);
char *Str_FindSubstring(const char *, const char *);
Boolean Str_Match(const char *, const char *);
-char *Str_SYSVMatch(const char *, const char *, size_t *, Boolean *);
-void Str_SYSVSubst(Buffer *, char *, char *, size_t, Boolean);
#ifndef HAVE_STRLCPY
/* strlcpy.c */
@@ -153,11 +164,11 @@ void Suff_ClearSuffixes(void);
Boolean Suff_IsTransform(char *);
GNode *Suff_AddTransform(char *);
int Suff_EndTransform(void *, void *);
-void Suff_AddSuffix(char *, GNode **);
+void Suff_AddSuffix(const char *, GNode **);
Lst Suff_GetPath(char *);
void Suff_DoPaths(void);
void Suff_AddInclude(char *);
-void Suff_AddLib(char *);
+void Suff_AddLib(const char *);
void Suff_FindDeps(GNode *);
Lst Suff_FindPath(GNode *);
void Suff_SetNull(char *);
@@ -168,6 +179,7 @@ void Suff_PrintAll(void);
/* targ.c */
void Targ_Init(void);
void Targ_End(void);
+void Targ_Stats(void);
Lst Targ_List(void);
GNode *Targ_NewGN(const char *);
GNode *Targ_FindNode(const char *, int);
@@ -182,31 +194,40 @@ char *Targ_FmtTime(time_t);
void Targ_PrintType(int);
void Targ_PrintGraph(int);
void Targ_Propagate(void);
-void Targ_Propagate_Wait(void);
/* var.c */
typedef enum {
- VARF_UNDEFERR = 1,
- VARF_WANTRES = 2,
- VARF_ASSIGN = 4
-} Varf_Flags;
+ /* Treat undefined variables as errors. */
+ VARE_UNDEFERR = 0x01,
+ /* Expand and evaluate variables during parsing. */
+ VARE_WANTRES = 0x02,
+ VARE_ASSIGN = 0x04
+} VarEvalFlags;
+
+typedef enum {
+ VAR_NO_EXPORT = 0x01, /* do not export */
+ /* Make the variable read-only. No further modification is possible,
+ * except for another call to Var_Set with the same flag. */
+ VAR_SET_READONLY = 0x02
+} VarSet_Flags;
+
void Var_Delete(const char *, GNode *);
void Var_Set(const char *, const char *, GNode *);
+void Var_Set_with_flags(const char *, const char *, GNode *, VarSet_Flags);
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 *, Varf_Flags, int *, void **);
-char *Var_Subst(const char *, const char *, GNode *, Varf_Flags);
-char *Var_GetTail(const char *);
-char *Var_GetHead(const char *);
+const char *Var_Value(const char *, GNode *, char **);
+const char *Var_Parse(const char *, GNode *, VarEvalFlags, int *, void **);
+char *Var_Subst(const char *, GNode *, VarEvalFlags);
void Var_Init(void);
void Var_End(void);
+void Var_Stats(void);
void Var_Dump(GNode *);
void Var_ExportVars(void);
-void Var_Export(char *, int);
-void Var_UnExport(char *);
+void Var_Export(const char *, Boolean);
+void Var_UnExport(const char *);
/* util.c */
void (*bmake_signal(int, void (*)(int)))(int);
diff --git a/contrib/bmake/os.sh b/contrib/bmake/os.sh
index e13f7ff58ece..7e6823b240c3 100755
--- a/contrib/bmake/os.sh
+++ b/contrib/bmake/os.sh
@@ -17,7 +17,7 @@
# Simon J. Gerraty <sjg@crufty.net>
# RCSid:
-# $Id: os.sh,v 1.55 2017/12/11 20:31:41 sjg Exp $
+# $Id: os.sh,v 1.56 2020/08/05 23:25:22 sjg Exp $
#
# @(#) Copyright (c) 1994 Simon J. Gerraty
#
@@ -86,6 +86,9 @@ AIX) # everyone loves to be different...
PS_AXC=-e
SHARE_ARCH=$OS/$OSMAJOR.X
;;
+Darwin) # a bit like BSD
+ HOST_ARCH=$MACHINE
+ ;;
SunOS)
CHOWN=`Which chown /usr/etc:/usr/bin`
export CHOWN
@@ -208,12 +211,12 @@ esac
TMP_DIRS=${TMP_DIRS:-"/tmp /var/tmp"}
MACHINE_ARCH=${MACHINE_ARCH:-$MACHINE}
-case "$MACHINE_ARCH" in
+HOST_ARCH=${HOST_ARCH:-$MACHINE_ARCH}
+case "$HOST_ARCH" in
x86*64|amd64) MACHINE32_ARCH=i386;;
*64) MACHINE32_ARCH=`echo $MACHINE_ARCH | sed 's,64,32,'`;;
*) MACHINE32_ARCH=$MACHINE_ARCH;;
esac
-HOST_ARCH=${HOST_ARCH:-$MACHINE_ARCH}
HOST_ARCH32=${HOST_ARCH32:-$MACHINE32_ARCH}
# we mount server:/share/arch/$SHARE_ARCH as /usr/local
SHARE_ARCH_DEFAULT=$OS/$OSMAJOR.X/$HOST_ARCH
diff --git a/contrib/bmake/parse.c b/contrib/bmake/parse.c
index 348595824507..ee065dd709dc 100644
--- a/contrib/bmake/parse.c
+++ b/contrib/bmake/parse.c
@@ -1,4 +1,4 @@
-/* $NetBSD: parse.c,v 1.236 2020/07/03 08:13:23 rillig Exp $ */
+/* $NetBSD: parse.c,v 1.275 2020/09/01 17:38:26 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -69,14 +69,14 @@
*/
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: parse.c,v 1.236 2020/07/03 08:13:23 rillig Exp $";
+static char rcsid[] = "$NetBSD: parse.c,v 1.275 2020/09/01 17:38:26 rillig Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)parse.c 8.3 (Berkeley) 3/19/94";
#else
-__RCSID("$NetBSD: parse.c,v 1.236 2020/07/03 08:13:23 rillig Exp $");
+__RCSID("$NetBSD: parse.c,v 1.275 2020/09/01 17:38:26 rillig Exp $");
#endif
#endif /* not lint */
#endif
@@ -125,17 +125,13 @@ __RCSID("$NetBSD: parse.c,v 1.236 2020/07/03 08:13:23 rillig Exp $");
#include <sys/types.h>
#include <sys/stat.h>
-#include <assert.h>
-#include <ctype.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include "make.h"
-#include "hash.h"
#include "dir.h"
#include "job.h"
-#include "buf.h"
#include "pathnames.h"
#ifdef HAVE_STDINT_H
@@ -153,8 +149,7 @@ __RCSID("$NetBSD: parse.c,v 1.236 2020/07/03 08:13:23 rillig Exp $");
#endif
#endif
-////////////////////////////////////////////////////////////
-// types and constants
+/* types and constants */
/*
* Structure for a file being read ("included file")
@@ -231,8 +226,7 @@ typedef enum {
#define RPAREN ')'
-////////////////////////////////////////////////////////////
-// result data
+/* result data */
/*
* The main target to create. This is the first target on the first
@@ -240,8 +234,7 @@ typedef enum {
*/
static GNode *mainNode;
-////////////////////////////////////////////////////////////
-// eval state
+/* eval state */
/* targets we're working on */
static Lst targets;
@@ -265,8 +258,7 @@ static ParseSpecial specType;
*/
static GNode *predecessor;
-////////////////////////////////////////////////////////////
-// parser state
+/* parser state */
/* true if currently in a dependency line or its commands */
static Boolean inLine;
@@ -289,8 +281,7 @@ Lst parseIncPath; /* dirs for "..." includes */
Lst sysIncPath; /* dirs for <...> includes */
Lst defIncPath; /* default for sysIncPath */
-////////////////////////////////////////////////////////////
-// parser tables
+/* parser tables */
/*
* The parseKeywords table is searched using binary search when deciding
@@ -350,8 +341,7 @@ static const struct {
{ ".WAIT", Wait, 0 },
};
-////////////////////////////////////////////////////////////
-// local functions
+/* local functions */
static int ParseIsEscaped(const char *, const char *);
static void ParseErrorInternal(const char *, size_t, int, const char *, ...)
@@ -379,8 +369,7 @@ static char *ParseReadLine(void);
static void ParseFinishLine(void);
static void ParseMark(GNode *);
-////////////////////////////////////////////////////////////
-// file loader
+/* file loader */
struct loadedfile {
const char *path; /* name, for error reports */
@@ -399,7 +388,7 @@ loadedfile_create(const char *path)
struct loadedfile *lf;
lf = bmake_malloc(sizeof(*lf));
- lf->path = (path == NULL ? "(stdin)" : path);
+ lf->path = path == NULL ? "(stdin)" : path;
lf->buf = NULL;
lf->len = 0;
lf->maplen = 0;
@@ -442,17 +431,17 @@ loadedfile_nextbuf(void *x, size_t *len)
/*
* Try to get the size of a file.
*/
-static ReturnStatus
+static Boolean
load_getsize(int fd, size_t *ret)
{
struct stat st;
if (fstat(fd, &st) < 0) {
- return FAILURE;
+ return FALSE;
}
if (!S_ISREG(st.st_mode)) {
- return FAILURE;
+ return FALSE;
}
/*
@@ -465,11 +454,11 @@ load_getsize(int fd, size_t *ret)
* While we're at it reject negative sizes too, just in case.
*/
if (st.st_size < 0 || st.st_size > 0x7fffffff) {
- return FAILURE;
+ return FALSE;
}
*ret = (size_t) st.st_size;
- return SUCCESS;
+ return TRUE;
}
/*
@@ -509,7 +498,7 @@ loadfile(const char *path, int fd)
}
#ifdef HAVE_MMAP
- if (load_getsize(fd, &lf->len) == SUCCESS) {
+ if (load_getsize(fd, &lf->len)) {
/* found a size, try mmap */
#ifdef _SC_PAGESIZE
if (pagesize == 0)
@@ -600,8 +589,7 @@ done:
return lf;
}
-////////////////////////////////////////////////////////////
-// old code
+/* old code */
/*-
*----------------------------------------------------------------------
@@ -714,8 +702,8 @@ ParseVErrorInternal(FILE *f, const char *cfname, size_t clineno, int type,
fname = cfname;
}
(void)fprintf(f, "%s/%s", dir, fname);
- free(cp2);
- free(cp);
+ bmake_free(cp2);
+ bmake_free(cp);
} else
(void)fprintf(f, "%s", cfname);
@@ -840,7 +828,7 @@ ParseMessage(char *line)
while (isspace((unsigned char)*line))
line++;
- line = Var_Subst(NULL, line, VAR_CMD, VARF_WANTRES);
+ line = Var_Subst(line, VAR_CMD, VARE_WANTRES);
Parse_Error(mtype, "%s", line);
free(line);
@@ -877,11 +865,11 @@ 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 ((pgn->type & OP_DOUBLEDEP) && !Lst_IsEmpty(pgn->cohorts))
+ pgn = LstNode_Datum(Lst_Last(pgn->cohorts));
+ Lst_Append(pgn->children, cgn);
if (specType == Not)
- (void)Lst_AtEnd(cgn->parents, pgn);
+ Lst_Append(cgn->parents, pgn);
pgn->unmade += 1;
if (DEBUG(PARSE)) {
fprintf(debug_file, "# %s: added child %s - %s\n", __func__,
@@ -929,7 +917,7 @@ ParseDoOp(void *gnp, void *opp)
return 1;
}
- if ((op == OP_DOUBLEDEP) && ((gn->type & OP_OPMASK) == OP_DOUBLEDEP)) {
+ 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
@@ -957,7 +945,7 @@ ParseDoOp(void *gnp, void *opp)
* traversals will no longer see this node anyway. -mycroft)
*/
cohort->type = op | OP_INVISIBLE;
- (void)Lst_AtEnd(gn->cohorts, cohort);
+ Lst_Append(gn->cohorts, cohort);
cohort->centurion = gn;
gn->unmade_cohorts += 1;
snprintf(cohort->cohort_num, sizeof cohort->cohort_num, "#%d",
@@ -1006,7 +994,8 @@ ParseDoSrc(int tOp, const char *src)
if (keywd != -1) {
int op = parseKeywords[keywd].op;
if (op != 0) {
- Lst_ForEach(targets, ParseDoOp, &op);
+ if (targets != NULL)
+ Lst_ForEach(targets, ParseDoOp, &op);
return;
}
if (parseKeywords[keywd].spec == Wait) {
@@ -1024,7 +1013,8 @@ ParseDoSrc(int tOp, const char *src)
if (doing_depend)
ParseMark(gn);
gn->type = OP_WAIT | OP_PHONY | OP_DEPENDS | OP_NOTMAIN;
- Lst_ForEach(targets, ParseLinkSrc, gn);
+ if (targets != NULL)
+ Lst_ForEach(targets, ParseLinkSrc, gn);
return;
}
}
@@ -1040,7 +1030,7 @@ ParseDoSrc(int tOp, const char *src)
* 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));
+ Lst_Append(create, bmake_strdup(src));
/*
* Add the name to the .TARGETS variable as well, so the user can
* employ that, if desired.
@@ -1057,8 +1047,8 @@ ParseDoSrc(int tOp, const char *src)
if (doing_depend)
ParseMark(gn);
if (predecessor != NULL) {
- (void)Lst_AtEnd(predecessor->order_succ, gn);
- (void)Lst_AtEnd(gn->order_pred, predecessor);
+ Lst_Append(predecessor->order_succ, gn);
+ Lst_Append(gn->order_pred, predecessor);
if (DEBUG(PARSE)) {
fprintf(debug_file, "# %s: added Order dependency %s - %s\n",
__func__, predecessor->name, gn->name);
@@ -1092,7 +1082,8 @@ ParseDoSrc(int tOp, const char *src)
if (tOp) {
gn->type |= tOp;
} else {
- Lst_ForEach(targets, ParseLinkSrc, gn);
+ if (targets != NULL)
+ Lst_ForEach(targets, ParseLinkSrc, gn);
}
break;
}
@@ -1120,7 +1111,7 @@ static int
ParseFindMain(void *gnp, void *dummy MAKE_ATTR_UNUSED)
{
GNode *gn = (GNode *)gnp;
- if ((gn->type & OP_NOTARGET) == 0) {
+ if (!(gn->type & OP_NOTARGET)) {
mainNode = gn;
Targ_SetMain(gn);
return 1;
@@ -1229,7 +1220,7 @@ ParseDoDependency(char *line)
specType = Not;
paths = NULL;
- curTargs = Lst_Init(FALSE);
+ curTargs = Lst_Init();
/*
* First, grind through the targets.
@@ -1257,10 +1248,10 @@ ParseDoDependency(char *line)
int length;
void *freeIt;
- (void)Var_Parse(cp, VAR_CMD, VARF_UNDEFERR|VARF_WANTRES,
+ (void)Var_Parse(cp, VAR_CMD, VARE_UNDEFERR|VARE_WANTRES,
&length, &freeIt);
free(freeIt);
- cp += length-1;
+ cp += length - 1;
}
}
@@ -1275,11 +1266,11 @@ ParseDoDependency(char *line)
* 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
+ * and places them on the given list, returning TRUE if all
+ * went well and FALSE if there was an error in the
* specification. On error, line should remain untouched.
*/
- if (Arch_ParseArchive(&line, targets, VAR_CMD) != SUCCESS) {
+ if (!Arch_ParseArchive(&line, targets, VAR_CMD)) {
Parse_Error(PARSE_FATAL,
"Error in archive specification: \"%s\"", line);
goto out;
@@ -1304,9 +1295,18 @@ ParseDoDependency(char *line)
(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");
+ else if (lstart[0] == '.') {
+ const char *dirstart = lstart + 1;
+ const char *dirend;
+ while (isspace((unsigned char)*dirstart))
+ dirstart++;
+ dirend = dirstart;
+ while (isalnum((unsigned char)*dirend) || *dirend == '-')
+ dirend++;
+ Parse_Error(PARSE_FATAL, "Unknown directive \"%.*s\"",
+ (int)(dirend - dirstart), dirstart);
+ } else
+ Parse_Error(PARSE_FATAL, "Need an operator");
goto out;
}
@@ -1368,9 +1368,9 @@ ParseDoDependency(char *line)
switch (specType) {
case ExPath:
if (paths == NULL) {
- paths = Lst_Init(FALSE);
+ paths = Lst_Init();
}
- (void)Lst_AtEnd(paths, dirSearchPath);
+ Lst_Append(paths, dirSearchPath);
break;
case Main:
if (!Lst_IsEmpty(create)) {
@@ -1386,12 +1386,12 @@ ParseDoDependency(char *line)
if (doing_depend)
ParseMark(gn);
gn->type |= OP_NOTMAIN|OP_SPECIAL;
- (void)Lst_AtEnd(targets, gn);
+ Lst_Append(targets, gn);
break;
case Default:
gn = Targ_NewGN(".DEFAULT");
gn->type |= (OP_NOTMAIN|OP_TRANSFORM);
- (void)Lst_AtEnd(targets, gn);
+ Lst_Append(targets, gn);
DEFAULT = gn;
break;
case DeleteOnError:
@@ -1426,9 +1426,9 @@ ParseDoDependency(char *line)
goto out;
} else {
if (paths == NULL) {
- paths = Lst_Init(FALSE);
+ paths = Lst_Init();
}
- (void)Lst_AtEnd(paths, path);
+ Lst_Append(paths, path);
}
}
}
@@ -1437,7 +1437,7 @@ ParseDoDependency(char *line)
* 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 (specType == Not && *line != '\0') {
if (Dir_HasWildcards(line)) {
/*
* Targets are to be sought only in the current directory,
@@ -1445,7 +1445,7 @@ ParseDoDependency(char *line)
* 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);
+ Lst emptyPath = Lst_Init();
Dir_Expand(line, emptyPath, curTargs);
@@ -1455,13 +1455,13 @@ ParseDoDependency(char *line)
* No wildcards, but we want to avoid code duplication,
* so create a list with the word on it.
*/
- (void)Lst_AtEnd(curTargs, line);
+ Lst_Append(curTargs, line);
}
/* Apply the targets. */
while(!Lst_IsEmpty(curTargs)) {
- char *targName = (char *)Lst_DeQueue(curTargs);
+ char *targName = Lst_Dequeue(curTargs);
if (!Suff_IsTransform (targName)) {
gn = Targ_FindNode(targName, TARG_CREATE);
@@ -1471,7 +1471,7 @@ ParseDoDependency(char *line)
if (doing_depend)
ParseMark(gn);
- (void)Lst_AtEnd(targets, gn);
+ Lst_Append(targets, gn);
}
} else if (specType == ExPath && *line != '.' && *line != '\0') {
Parse_Error(PARSE_WARNING, "Extra target (%s) ignored", line);
@@ -1488,7 +1488,7 @@ ParseDoDependency(char *line)
Boolean warning = FALSE;
while (*cp && (ParseIsEscaped(lstart, cp) ||
- ((*cp != '!') && (*cp != ':')))) {
+ (*cp != '!' && *cp != ':'))) {
if (ParseIsEscaped(lstart, cp) ||
(*cp != ' ' && *cp != '\t')) {
warning = TRUE;
@@ -1505,15 +1505,15 @@ ParseDoDependency(char *line)
}
line = cp;
} while (*line && (ParseIsEscaped(lstart, line) ||
- ((*line != '!') && (*line != ':'))));
+ (*line != '!' && *line != ':')));
/*
* Don't need the list of target names anymore...
*/
- Lst_Destroy(curTargs, NULL);
+ Lst_Free(curTargs);
curTargs = NULL;
- if (!Lst_IsEmpty(targets)) {
+ if (targets != NULL && !Lst_IsEmpty(targets)) {
switch(specType) {
default:
Parse_Error(PARSE_WARNING, "Special and mundane targets don't mix. Mundane ones ignored");
@@ -1563,7 +1563,8 @@ ParseDoDependency(char *line)
* operator a target was defined with. It fails if the operator
* used isn't consistent across all references.
*/
- Lst_ForEach(targets, ParseDoOp, &op);
+ if (targets != NULL)
+ Lst_ForEach(targets, ParseDoOp, &op);
/*
* Onward to the sources.
@@ -1600,13 +1601,14 @@ ParseDoDependency(char *line)
beSilent = TRUE;
break;
case ExPath:
- Lst_ForEach(paths, ParseClearPath, NULL);
+ if (paths != NULL)
+ Lst_ForEach(paths, ParseClearPath, NULL);
Dir_SetPATH();
break;
#ifdef POSIX
- case Posix:
- Var_Set("%POSIX", "1003.2", VAR_GLOBAL);
- break;
+ case Posix:
+ Var_Set("%POSIX", "1003.2", VAR_GLOBAL);
+ break;
#endif
default:
break;
@@ -1620,22 +1622,22 @@ ParseDoDependency(char *line)
Main_ParseArgLine(line);
*line = '\0';
} else if (specType == ExShell) {
- if (Job_ParseShell(line) != SUCCESS) {
+ if (!Job_ParseShell(line)) {
Parse_Error(PARSE_FATAL, "improper shell specification");
goto out;
}
*line = '\0';
- } else if ((specType == NotParallel) || (specType == SingleShell) ||
- (specType == DeleteOnError)) {
+ } else if (specType == NotParallel || specType == SingleShell ||
+ specType == DeleteOnError) {
*line = '\0';
}
/*
* NOW GO FOR THE SOURCES
*/
- if ((specType == Suffixes) || (specType == ExPath) ||
- (specType == Includes) || (specType == Libs) ||
- (specType == Null) || (specType == ExObjdir))
+ if (specType == Suffixes || specType == ExPath ||
+ specType == Includes || specType == Libs ||
+ specType == Null || specType == ExObjdir)
{
while (*line) {
/*
@@ -1675,7 +1677,8 @@ ParseDoDependency(char *line)
Suff_AddSuffix(line, &mainNode);
break;
case ExPath:
- Lst_ForEach(paths, ParseAddDir, line);
+ if (paths != NULL)
+ Lst_ForEach(paths, ParseAddDir, line);
break;
case Includes:
Suff_AddInclude(line);
@@ -1702,7 +1705,7 @@ ParseDoDependency(char *line)
line = cp;
}
if (paths) {
- Lst_Destroy(paths, NULL);
+ Lst_Free(paths);
paths = NULL;
}
if (specType == ExPath)
@@ -1716,7 +1719,7 @@ ParseDoDependency(char *line)
* and handle them accordingly.
*/
for (; *cp && !isspace ((unsigned char)*cp); cp++) {
- if ((*cp == LPAREN) && (cp > line) && (cp[-1] != '$')) {
+ 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
@@ -1728,18 +1731,18 @@ ParseDoDependency(char *line)
}
if (*cp == LPAREN) {
- sources = Lst_Init(FALSE);
- if (Arch_ParseArchive(&line, sources, VAR_CMD) != SUCCESS) {
+ sources = Lst_Init();
+ if (!Arch_ParseArchive(&line, sources, VAR_CMD)) {
Parse_Error(PARSE_FATAL,
"Error in source archive spec \"%s\"", line);
goto out;
}
- while (!Lst_IsEmpty (sources)) {
- gn = (GNode *)Lst_DeQueue(sources);
+ while (!Lst_IsEmpty(sources)) {
+ gn = Lst_Dequeue(sources);
ParseDoSrc(tOp, gn->name);
}
- Lst_Destroy(sources, NULL);
+ Lst_Free(sources);
cp = line;
} else {
if (*cp) {
@@ -1756,7 +1759,7 @@ ParseDoDependency(char *line)
}
}
- if (mainNode == NULL) {
+ if (mainNode == NULL && targets != 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
@@ -1767,10 +1770,10 @@ ParseDoDependency(char *line)
}
out:
- if (paths)
- Lst_Destroy(paths, NULL);
- if (curTargs)
- Lst_Destroy(curTargs, NULL);
+ if (paths != NULL)
+ Lst_Free(paths);
+ if (curTargs != NULL)
+ Lst_Free(curTargs);
}
/*-
@@ -1804,8 +1807,8 @@ Parse_IsVar(char *line)
/*
* Skip to variable name
*/
- for (;(*line == ' ') || (*line == '\t'); line++)
- continue;
+ while (*line == ' ' || *line == '\t')
+ line++;
/* Scan for one of the assignment operators outside a variable expansion */
while ((ch = *line++) != 0) {
@@ -1882,16 +1885,15 @@ Parse_DoVar(char *line, GNode *ctxt)
/*
* Skip to variable name
*/
- while ((*line == ' ') || (*line == '\t')) {
+ 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++) {
+ for (depth = 0, cp = line; depth > 0 || *cp != '='; cp++) {
if (*cp == '(' || *cp == '{') {
depth++;
continue;
@@ -1953,8 +1955,17 @@ Parse_DoVar(char *line, GNode *ctxt)
break;
}
- while (isspace ((unsigned char)*cp)) {
+ while (isspace((unsigned char)*cp))
cp++;
+
+ if (DEBUG(LINT)) {
+ if (type != VAR_SUBST && strchr(cp, '$') != NULL) {
+ /* sanity check now */
+ char *cp2;
+
+ cp2 = Var_Subst(cp, ctxt, VARE_ASSIGN);
+ free(cp2);
+ }
}
if (type == VAR_APPEND) {
@@ -1982,7 +1993,7 @@ Parse_DoVar(char *line, GNode *ctxt)
if (!Var_Exists(line, ctxt))
Var_Set(line, "", ctxt);
- cp = Var_Subst(NULL, cp, ctxt, VARF_WANTRES|VARF_ASSIGN);
+ cp = Var_Subst(cp, ctxt, VARE_WANTRES|VARE_ASSIGN);
oldVars = oldOldVars;
freeCp = TRUE;
@@ -1995,9 +2006,9 @@ Parse_DoVar(char *line, GNode *ctxt)
/*
* 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.
+ * freeing when we're done.
*/
- cp = Var_Subst(NULL, cp, VAR_CMD, VARF_UNDEFERR|VARF_WANTRES);
+ cp = Var_Subst(cp, VAR_CMD, VARE_UNDEFERR|VARE_WANTRES);
freeCp = TRUE;
}
@@ -2026,7 +2037,7 @@ Parse_DoVar(char *line, GNode *ctxt)
} else if (strcmp(line, MAKE_JOB_PREFIX) == 0) {
Job_SetPrefix();
} else if (strcmp(line, MAKE_EXPORTED) == 0) {
- Var_Export(cp, 0);
+ Var_Export(cp, FALSE);
}
if (freeCp)
free(cp);
@@ -2089,19 +2100,19 @@ 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 ((gn->type & OP_DOUBLEDEP) && !Lst_IsEmpty(gn->cohorts))
+ gn = LstNode_Datum(Lst_Last(gn->cohorts));
/* if target already supplied, ignore commands */
if (!(gn->type & OP_HAS_COMMANDS)) {
- (void)Lst_AtEnd(gn->commands, cmd);
+ Lst_Append(gn->commands, cmd);
if (ParseMaybeSubMake(cmd))
gn->type |= OP_SUBMAKE;
ParseMark(gn);
} else {
#ifdef notyet
/* XXX: We cannot do this until we fix the tree */
- (void)Lst_AtEnd(gn->commands, cmd);
+ Lst_Append(gn->commands, cmd);
Parse_Error(PARSE_WARNING,
"overriding commands for target \"%s\"; "
"previous commands defined at %s: %d ignored",
@@ -2225,7 +2236,7 @@ Parse_include_file(char *file, Boolean isSystem, Boolean depinc, int silent)
break;
*prefEnd = '\0';
}
- newName = str_concat(incdir, file + i, STR_ADDSLASH);
+ newName = str_concat3(incdir, "/", file + i);
fullname = Dir_FindFile(newName, parseIncPath);
if (fullname == NULL)
fullname = Dir_FindFile(newName, dirSearchPath);
@@ -2235,7 +2246,7 @@ Parse_include_file(char *file, Boolean isSystem, Boolean depinc, int silent)
if (fullname == NULL) {
/*
- * Makefile wasn't found in same directory as included makefile.
+ * 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.
@@ -2298,7 +2309,7 @@ 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;
+ int silent = *line != 'i';
char *file = &line[7 + silent];
/* Skip to delimiter character so we know where to look */
@@ -2338,9 +2349,9 @@ ParseDoInclude(char *line)
* Substitute for any variables in the file name before trying to
* find the thing.
*/
- file = Var_Subst(NULL, file, VAR_CMD, VARF_WANTRES);
+ file = Var_Subst(file, VAR_CMD, VARE_WANTRES);
- Parse_include_file(file, endc == '>', (*line == 'd'), silent);
+ Parse_include_file(file, endc == '>', *line == 'd', silent);
free(file);
}
@@ -2363,20 +2374,20 @@ ParseDoInclude(char *line)
static void
ParseSetIncludedFile(void)
{
- char *pf, *fp = NULL;
- char *pd, *dp = NULL;
+ const char *pf, *pd;
+ char *pf_freeIt, *pd_freeIt;
- pf = Var_Value(".PARSEFILE", VAR_GLOBAL, &fp);
+ pf = Var_Value(".PARSEFILE", VAR_GLOBAL, &pf_freeIt);
Var_Set(".INCLUDEDFROMFILE", pf, VAR_GLOBAL);
- pd = Var_Value(".PARSEDIR", VAR_GLOBAL, &dp);
+ pd = Var_Value(".PARSEDIR", VAR_GLOBAL, &pd_freeIt);
Var_Set(".INCLUDEDFROMDIR", pd, VAR_GLOBAL);
if (DEBUG(PARSE))
fprintf(debug_file, "%s: ${.INCLUDEDFROMDIR} = `%s' "
"${.INCLUDEDFROMFILE} = `%s'\n", __func__, pd, pf);
- free(fp);
- free(dp);
+ bmake_free(pf_freeIt);
+ bmake_free(pd_freeIt);
}
/*-
*---------------------------------------------------------------------
@@ -2397,18 +2408,14 @@ 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);
Var_Set(".PARSEFILE", pf = filename, VAR_GLOBAL);
- dirname= NULL;
+ dirname = NULL;
} else {
- len = slash - filename;
- dirname = bmake_malloc(len + 1);
- memcpy(dirname, filename, len);
- dirname[len] = '\0';
+ dirname = bmake_strsedup(filename, slash);
Var_Set(".PARSEDIR", pd = dirname, VAR_GLOBAL);
Var_Set(".PARSEFILE", pf = slash + 1, VAR_GLOBAL);
}
@@ -2427,14 +2434,12 @@ ParseSetParseFile(const char *filename)
static void
ParseTrackInput(const char *name)
{
- char *old;
- char *ep;
char *fp = NULL;
- size_t name_len = strlen(name);
- old = Var_Value(MAKE_MAKEFILES, VAR_GLOBAL, &fp);
+ const char *old = Var_Value(MAKE_MAKEFILES, VAR_GLOBAL, &fp);
if (old) {
- ep = old + strlen(old) - name_len;
+ size_t name_len = strlen(name);
+ const char *ep = old + strlen(old) - name_len;
/* does it contain name? */
for (; old != NULL; old = strchr(old, ' ')) {
if (*old == ' ')
@@ -2448,9 +2453,7 @@ ParseTrackInput(const char *name)
}
Var_Append (MAKE_MAKEFILES, name, VAR_GLOBAL);
cleanup:
- if (fp) {
- free(fp);
- }
+ bmake_free(fp);
}
@@ -2489,7 +2492,7 @@ Parse_SetInput(const char *name, int line, int fd,
if (curFile != NULL)
/* Save exiting file info */
- Lst_AtFront(includes, curFile);
+ Lst_Prepend(includes, curFile);
/* Allocate and fill in new structure */
curFile = bmake_malloc(sizeof *curFile);
@@ -2513,7 +2516,7 @@ Parse_SetInput(const char *name, int line, int fd,
/* Get first block of input data */
buf = curFile->nextbuf(curFile->nextbuf_arg, &len);
if (buf == NULL) {
- /* Was all a waste of time ... */
+ /* Was all a waste of time ... */
if (curFile->fname)
free(curFile->fname);
free(curFile);
@@ -2546,13 +2549,13 @@ IsInclude(const char *line, Boolean sysv)
static const char inc[] = "include";
static const size_t inclen = sizeof(inc) - 1;
- // 'd' is not valid for sysv
- int o = strchr(&("ds-"[sysv]), *line) != NULL;
+ /* 'd' is not valid for sysv */
+ int o = strchr(sysv ? "s-" : "ds-", *line) != NULL;
if (strncmp(line + o, inc, inclen) != 0)
return FALSE;
- // Space is not mandatory for BSD .include
+ /* Space is not mandatory for BSD .include */
return !sysv || isspace((unsigned char)line[inclen + o]);
}
@@ -2614,7 +2617,7 @@ ParseTraditionalInclude(char *line)
{
char *cp; /* current position in file spec */
int done = 0;
- int silent = (line[0] != 'i') ? 1 : 0;
+ int silent = line[0] != 'i';
char *file = &line[silent + 7];
char *all_files;
@@ -2632,7 +2635,7 @@ ParseTraditionalInclude(char *line)
* Substitute for any variables in the file name before trying to
* find the thing.
*/
- all_files = Var_Subst(NULL, file, VAR_CMD, VARF_WANTRES);
+ all_files = Var_Subst(file, VAR_CMD, VARE_WANTRES);
if (*file == '\0') {
Parse_Error(PARSE_FATAL,
@@ -2701,7 +2704,7 @@ ParseGmakeExport(char *line)
/*
* Expand the value before putting it in the environment.
*/
- value = Var_Subst(NULL, value, VAR_CMD, VARF_WANTRES);
+ value = Var_Subst(value, VAR_CMD, VARE_WANTRES);
setenv(variable, value, 1);
free(value);
}
@@ -2755,9 +2758,8 @@ ParseEOF(void)
free(curFile->P_str);
free(curFile);
- curFile = Lst_DeQueue(includes);
-
- if (curFile == NULL) {
+ if (Lst_IsEmpty(includes)) {
+ curFile = NULL;
/* We've run out of input */
Var_Delete(".PARSEDIR", VAR_GLOBAL);
Var_Delete(".PARSEFILE", VAR_GLOBAL);
@@ -2766,6 +2768,7 @@ ParseEOF(void)
return DONE;
}
+ curFile = Lst_Dequeue(includes);
if (DEBUG(PARSE))
fprintf(debug_file, "ParseEOF: returning to file %s, line %d\n",
curFile->fname, curFile->lineno);
@@ -3030,8 +3033,10 @@ static void
ParseFinishLine(void)
{
if (inLine) {
- Lst_ForEach(targets, Suff_EndTransform, NULL);
- Lst_Destroy(targets, ParseHasCommands);
+ if (targets != NULL) {
+ Lst_ForEach(targets, Suff_EndTransform, NULL);
+ Lst_Destroy(targets, ParseHasCommands);
+ }
targets = NULL;
inLine = FALSE;
}
@@ -3070,9 +3075,8 @@ Parse_File(const char *name, int fd)
inLine = FALSE;
fatals = 0;
- if (name == NULL) {
- name = "(stdin)";
- }
+ if (name == NULL)
+ name = "(stdin)";
Parse_SetInput(name, 0, -1, loadedfile_nextbuf, lf);
curFile->lf = lf;
@@ -3101,7 +3105,7 @@ Parse_File(const char *name, int fd)
for (cp += 5; isspace((unsigned char) *cp); cp++)
continue;
for (cp2 = cp; !isspace((unsigned char) *cp2) &&
- (*cp2 != '\0'); cp2++)
+ *cp2 != '\0'; cp2++)
continue;
*cp2 = '\0';
Var_Delete(cp, VAR_GLOBAL);
@@ -3109,7 +3113,7 @@ Parse_File(const char *name, int fd)
} else if (strncmp(cp, "export", 6) == 0) {
for (cp += 6; isspace((unsigned char) *cp); cp++)
continue;
- Var_Export(cp, 1);
+ Var_Export(cp, TRUE);
continue;
} else if (strncmp(cp, "unexport", 8) == 0) {
Var_UnExport(cp);
@@ -3146,7 +3150,7 @@ Parse_File(const char *name, int fd)
cp = bmake_strdup(cp);
Lst_ForEach(targets, ParseAddCmd, cp);
#ifdef CLEANUP
- Lst_AtEnd(targCmds, cp);
+ Lst_Append(targCmds, cp);
#endif
}
}
@@ -3189,10 +3193,10 @@ Parse_File(const char *name, int fd)
*/
cp = line;
if (isspace((unsigned char) line[0])) {
- while ((*cp != '\0') && isspace((unsigned char) *cp))
+ while (isspace((unsigned char) *cp))
cp++;
while (*cp && (ParseIsEscaped(line, cp) ||
- (*cp != ':') && (*cp != '!'))) {
+ *cp != ':' && *cp != '!')) {
cp++;
}
if (*cp == '\0') {
@@ -3245,15 +3249,15 @@ Parse_File(const char *name, int fd)
* variables expanded before being parsed. Tell the variable
* module to complain if some variable is undefined...
*/
- line = Var_Subst(NULL, line, VAR_CMD, VARF_UNDEFERR|VARF_WANTRES);
+ line = Var_Subst(line, VAR_CMD, VARE_UNDEFERR|VARE_WANTRES);
/*
* Need a non-circular list for the target nodes
*/
- if (targets)
- Lst_Destroy(targets, NULL);
+ if (targets != NULL)
+ Lst_Free(targets);
- targets = Lst_Init(FALSE);
+ targets = Lst_Init();
inLine = TRUE;
ParseDoDependency(line);
@@ -3295,12 +3299,12 @@ void
Parse_Init(void)
{
mainNode = NULL;
- parseIncPath = Lst_Init(FALSE);
- sysIncPath = Lst_Init(FALSE);
- defIncPath = Lst_Init(FALSE);
- includes = Lst_Init(FALSE);
+ parseIncPath = Lst_Init();
+ sysIncPath = Lst_Init();
+ defIncPath = Lst_Init();
+ includes = Lst_Init();
#ifdef CLEANUP
- targCmds = Lst_Init(FALSE);
+ targCmds = Lst_Init();
#endif
}
@@ -3308,13 +3312,13 @@ void
Parse_End(void)
{
#ifdef CLEANUP
- Lst_Destroy(targCmds, (FreeProc *)free);
+ Lst_Destroy(targCmds, free);
if (targets)
- Lst_Destroy(targets, NULL);
+ Lst_Free(targets);
Lst_Destroy(defIncPath, Dir_Destroy);
Lst_Destroy(sysIncPath, Dir_Destroy);
Lst_Destroy(parseIncPath, Dir_Destroy);
- Lst_Destroy(includes, NULL); /* Should be empty now */
+ Lst_Free(includes); /* Should be empty now */
#endif
}
@@ -3338,17 +3342,17 @@ Parse_MainName(void)
{
Lst mainList; /* result list */
- mainList = Lst_Init(FALSE);
+ mainList = Lst_Init();
if (mainNode == NULL) {
Punt("no target to make.");
- /*NOTREACHED*/
+ /*NOTREACHED*/
} else if (mainNode->type & OP_DOUBLEDEP) {
- (void)Lst_AtEnd(mainList, mainNode);
- Lst_Concat(mainList, mainNode->cohorts, LST_CONCNEW);
+ Lst_Append(mainList, mainNode);
+ Lst_AppendAll(mainList, mainNode->cohorts);
}
else
- (void)Lst_AtEnd(mainList, mainNode);
+ Lst_Append(mainList, mainNode);
Var_Append(".TARGETS", mainNode->name, VAR_GLOBAL);
return mainList;
}
diff --git a/contrib/bmake/sprite.h b/contrib/bmake/sprite.h
deleted file mode 100644
index cdcffd99abed..000000000000
--- a/contrib/bmake/sprite.h
+++ /dev/null
@@ -1,116 +0,0 @@
-/* $NetBSD: sprite.h,v 1.14 2017/05/31 22:02:06 maya Exp $ */
-
-/*
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Adam de Boor.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * from: @(#)sprite.h 8.1 (Berkeley) 6/6/93
- */
-
-/*
- * Copyright (c) 1989 by Berkeley Softworks
- * All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Adam de Boor.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * from: @(#)sprite.h 8.1 (Berkeley) 6/6/93
- */
-
-/*
- * sprite.h --
- *
- * Common constants and type declarations for Sprite.
- */
-
-#ifndef MAKE_SPRITE_H
-#define MAKE_SPRITE_H
-
-
-/*
- * A boolean type is defined as an integer, not an enum. This allows a
- * boolean argument to be an expression that isn't strictly 0 or 1 valued.
- */
-
-typedef int Boolean;
-#ifndef TRUE
-#define TRUE 1
-#endif /* TRUE */
-#ifndef FALSE
-#define FALSE 0
-#endif /* FALSE */
-
-/*
- * Functions that must return a status can return a ReturnStatus to
- * indicate success or type of failure.
- */
-
-typedef int ReturnStatus;
-
-/*
- * The following statuses overlap with the first 2 generic statuses
- * defined in status.h:
- *
- * SUCCESS There was no error.
- * FAILURE There was a general error.
- */
-
-#define SUCCESS 0x00000000
-#define FAILURE 0x00000001
-
-#endif /* MAKE_SPRITE_H */
diff --git a/contrib/bmake/str.c b/contrib/bmake/str.c
index c3138b67b95b..59fdccb28867 100644
--- a/contrib/bmake/str.c
+++ b/contrib/bmake/str.c
@@ -1,4 +1,4 @@
-/* $NetBSD: str.c,v 1.51 2020/07/03 07:40:13 rillig Exp $ */
+/* $NetBSD: str.c,v 1.64 2020/08/30 19:56:02 rillig Exp $ */
/*-
* Copyright (c) 1988, 1989, 1990, 1993
@@ -69,85 +69,85 @@
*/
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: str.c,v 1.51 2020/07/03 07:40:13 rillig Exp $";
+static char rcsid[] = "$NetBSD: str.c,v 1.64 2020/08/30 19:56:02 rillig Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)str.c 5.8 (Berkeley) 6/1/90";
#else
-__RCSID("$NetBSD: str.c,v 1.51 2020/07/03 07:40:13 rillig Exp $");
+__RCSID("$NetBSD: str.c,v 1.64 2020/08/30 19:56:02 rillig Exp $");
#endif
#endif /* not lint */
#endif
#include "make.h"
-/*-
- * str_concat --
- * concatenate the two strings, inserting a space or slash between them,
- * freeing them if requested.
- *
- * returns --
- * the resulting string in allocated space.
- */
+/* Return the concatenation of s1 and s2, freshly allocated. */
char *
-str_concat(const char *s1, const char *s2, int flags)
+str_concat2(const char *s1, const char *s2)
{
- int len1, len2;
- char *result;
-
- /* get the length of both strings */
- len1 = strlen(s1);
- len2 = strlen(s2);
-
- /* allocate length plus separator plus EOS */
- result = bmake_malloc((unsigned int)(len1 + len2 + 2));
-
- /* copy first string into place */
+ size_t len1 = strlen(s1);
+ size_t len2 = strlen(s2);
+ char *result = bmake_malloc(len1 + len2 + 1);
memcpy(result, s1, len1);
-
- /* add separator character */
- if (flags & STR_ADDSPACE) {
- result[len1] = ' ';
- ++len1;
- } else if (flags & STR_ADDSLASH) {
- result[len1] = '/';
- ++len1;
- }
-
- /* copy second string plus EOS into place */
memcpy(result + len1, s2, len2 + 1);
+ return result;
+}
+/* Return the concatenation of s1, s2 and s3, freshly allocated. */
+char *
+str_concat3(const char *s1, const char *s2, const char *s3)
+{
+ size_t len1 = strlen(s1);
+ size_t len2 = strlen(s2);
+ size_t len3 = strlen(s3);
+ char *result = bmake_malloc(len1 + len2 + len3 + 1);
+ memcpy(result, s1, len1);
+ memcpy(result + len1, s2, len2);
+ memcpy(result + len1 + len2, s3, len3 + 1);
return result;
}
-/*-
- * brk_string --
- * Fracture a string into an array of words (as delineated by tabs or
- * spaces) taking quotation marks into account. Leading tabs/spaces
- * are ignored.
+/* Return the concatenation of s1, s2, s3 and s4, freshly allocated. */
+char *
+str_concat4(const char *s1, const char *s2, const char *s3, const char *s4)
+{
+ size_t len1 = strlen(s1);
+ size_t len2 = strlen(s2);
+ size_t len3 = strlen(s3);
+ size_t len4 = strlen(s4);
+ char *result = bmake_malloc(len1 + len2 + len3 + len4 + 1);
+ memcpy(result, s1, len1);
+ memcpy(result + len1, s2, len2);
+ memcpy(result + len1 + len2, s3, len3);
+ memcpy(result + len1 + len2 + len3, s4, len4 + 1);
+ return result;
+}
+
+/* Fracture a string into an array of words (as delineated by tabs or spaces)
+ * taking quotation marks into account. Leading tabs/spaces are ignored.
*
- * If expand is TRUE, quotes are removed and escape sequences
- * such as \r, \t, etc... are expanded.
+ * If expand is TRUE, quotes are removed and escape sequences such as \r, \t,
+ * etc... are expanded. In this case, the return value is NULL on parse
+ * errors.
*
- * returns --
- * Pointer to the array of pointers to the words.
- * Memory containing the actual words in *store_words_buf.
- * Both of these must be free'd by the caller.
- * Number of words in *store_words_len.
+ * Returns the fractured words, which must be freed later using Words_Free.
+ * If expand was TRUE and there was a parse error, words is NULL, and in that
+ * case, nothing needs to be freed.
*/
-char **
-brk_string(const char *str, int *store_words_len, Boolean expand,
- char **store_words_buf)
+Words
+Str_Words(const char *str, Boolean expand)
{
+ size_t str_len;
+ char *words_buf;
+ size_t words_cap;
+ char **words;
+ size_t words_len;
char inquote;
+ char *word_start;
+ char *word_end;
const char *str_p;
- size_t str_len;
- char **words;
- int words_len;
- int words_cap = 50;
- char *words_buf, *word_start, *word_end;
/* skip leading space chars. */
for (; *str == ' ' || *str == '\t'; ++str)
@@ -166,10 +166,11 @@ brk_string(const char *str, int *store_words_len, Boolean expand,
*/
words_len = 0;
inquote = '\0';
- word_start = word_end = words_buf;
+ word_start = words_buf;
+ word_end = words_buf;
for (str_p = str;; ++str_p) {
char ch = *str_p;
- switch(ch) {
+ switch (ch) {
case '"':
case '\'':
if (inquote) {
@@ -177,9 +178,8 @@ brk_string(const char *str, int *store_words_len, Boolean expand,
inquote = '\0';
else
break;
- }
- else {
- inquote = (char) ch;
+ } else {
+ inquote = (char)ch;
/* Don't miss "" or '' */
if (word_start == NULL && str_p[1] == inquote) {
if (!expand) {
@@ -212,13 +212,14 @@ brk_string(const char *str, int *store_words_len, Boolean expand,
* space and save off a pointer.
*/
if (word_start == NULL)
- goto done;
+ goto done;
*word_end++ = '\0';
if (words_len == words_cap) {
+ size_t new_size;
words_cap *= 2; /* ramp up fast */
- words = (char **)bmake_realloc(words,
- (words_cap + 1) * sizeof(char *));
+ new_size = (words_cap + 1) * sizeof(char *);
+ words = bmake_realloc(words, new_size);
}
words[words_len++] = word_start;
word_start = NULL;
@@ -226,8 +227,7 @@ brk_string(const char *str, int *store_words_len, Boolean expand,
if (expand && inquote) {
free(words);
free(words_buf);
- *store_words_buf = NULL;
- return NULL;
+ return (Words){ NULL, 0, NULL };
}
goto done;
}
@@ -273,10 +273,9 @@ brk_string(const char *str, int *store_words_len, Boolean expand,
word_start = word_end;
*word_end++ = ch;
}
-done: words[words_len] = NULL;
- *store_words_len = words_len;
- *store_words_buf = words_buf;
- return words;
+done:
+ words[words_len] = NULL;
+ return (Words){ words, words_len, words_buf };
}
/*
@@ -375,7 +374,7 @@ Str_Match(const char *str, const char *pat)
*/
if (*pat == '[') {
Boolean neg = pat[1] == '^';
- pat += 1 + neg;
+ pat += neg ? 2 : 1;
for (;;) {
if (*pat == ']' || *pat == 0) {
@@ -423,107 +422,3 @@ Str_Match(const char *str, const char *pat)
str++;
}
}
-
-/*-
- *-----------------------------------------------------------------------
- * Str_SYSVMatch --
- * Check word against pattern for a match (% is wild),
- *
- * Input:
- * word Word to examine
- * pattern Pattern to examine against
- * len Number of characters to substitute
- *
- * Results:
- * Returns the beginning position of a match or null. The number
- * of characters matched is returned in len.
- *
- * Side Effects:
- * None
- *
- *-----------------------------------------------------------------------
- */
-char *
-Str_SYSVMatch(const char *word, const char *pattern, size_t *len,
- Boolean *hasPercent)
-{
- const char *p = pattern;
- const char *w = word;
- const char *m;
-
- *hasPercent = FALSE;
- if (*p == '\0') {
- /* Null pattern is the whole string */
- *len = strlen(w);
- return UNCONST(w);
- }
-
- if ((m = strchr(p, '%')) != NULL) {
- *hasPercent = TRUE;
- if (*w == '\0') {
- /* empty word does not match pattern */
- return NULL;
- }
- /* check that the prefix matches */
- for (; p != m && *w && *w == *p; w++, p++)
- continue;
-
- if (p != m)
- return NULL; /* No match */
-
- if (*++p == '\0') {
- /* No more pattern, return the rest of the string */
- *len = strlen(w);
- return UNCONST(w);
- }
- }
-
- m = w;
-
- /* Find a matching tail */
- do
- if (strcmp(p, w) == 0) {
- *len = w - m;
- return UNCONST(m);
- }
- while (*w++ != '\0');
-
- return NULL;
-}
-
-
-/*-
- *-----------------------------------------------------------------------
- * Str_SYSVSubst --
- * Substitute '%' on the pattern with len characters from src.
- * If the pattern does not contain a '%' prepend len characters
- * from src.
- *
- * Results:
- * None
- *
- * Side Effects:
- * Places result on buf
- *
- *-----------------------------------------------------------------------
- */
-void
-Str_SYSVSubst(Buffer *buf, char *pat, char *src, size_t len,
- Boolean lhsHasPercent)
-{
- char *m;
-
- if ((m = strchr(pat, '%')) != NULL && lhsHasPercent) {
- /* Copy the prefix */
- Buf_AddBytes(buf, m - pat, pat);
- /* skip the % */
- pat = m + 1;
- }
- if (m != NULL || !lhsHasPercent) {
- /* Copy the pattern */
- Buf_AddBytes(buf, len, src);
- }
-
- /* append the rest */
- Buf_AddBytes(buf, strlen(pat), pat);
-}
diff --git a/contrib/bmake/strlist.c b/contrib/bmake/strlist.c
index 3fb2f7dbb6c8..905e78fd8caa 100644
--- a/contrib/bmake/strlist.c
+++ b/contrib/bmake/strlist.c
@@ -1,4 +1,4 @@
-/* $NetBSD: strlist.c,v 1.4 2009/01/24 11:59:39 dsl Exp $ */
+/* $NetBSD: strlist.c,v 1.6 2020/08/25 17:37:09 rillig Exp $ */
/*-
* Copyright (c) 2008 - 2009 The NetBSD Foundation, Inc.
@@ -33,18 +33,16 @@
*/
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: strlist.c,v 1.4 2009/01/24 11:59:39 dsl Exp $";
+static char rcsid[] = "$NetBSD: strlist.c,v 1.6 2020/08/25 17:37:09 rillig Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
-__RCSID("$NetBSD: strlist.c,v 1.4 2009/01/24 11:59:39 dsl Exp $");
+__RCSID("$NetBSD: strlist.c,v 1.6 2020/08/25 17:37:09 rillig Exp $");
#endif /* not lint */
#endif
-#include <stddef.h>
-#include <stdlib.h>
+#include "make.h"
#include "strlist.h"
-#include "make_malloc.h"
void
strlist_init(strlist_t *sl)
@@ -76,18 +74,18 @@ strlist_add_str(strlist_t *sl, char *str, unsigned int info)
strlist_item_t *items;
if (str == NULL)
- return;
+ 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 = 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 */
+ items[1].si_str = NULL; /* STRLIST_FOREACH() terminator */
}
diff --git a/contrib/bmake/strlist.h b/contrib/bmake/strlist.h
index 2fc049e8643a..551317c090f3 100644
--- a/contrib/bmake/strlist.h
+++ b/contrib/bmake/strlist.h
@@ -1,4 +1,4 @@
-/* $NetBSD: strlist.h,v 1.3 2009/01/16 21:15:34 dsl Exp $ */
+/* $NetBSD: strlist.h,v 1.4 2020/08/13 03:54:57 rillig Exp $ */
/*-
* Copyright (c) 2008 - 2009 The NetBSD Foundation, Inc.
@@ -32,8 +32,8 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef _STRLIST_H
-#define _STRLIST_H
+#ifndef MAKE_STRLIST_H
+#define MAKE_STRLIST_H
typedef struct {
char *si_str;
@@ -59,4 +59,4 @@ void strlist_add_str(strlist_t *, char *, unsigned int);
if ((sl)->sl_items != NULL) \
for (index = 0; (v = strlist_str(sl, index)) != NULL; index++)
-#endif /* _STRLIST_H */
+#endif /* MAKE_STRLIST_H */
diff --git a/contrib/bmake/suff.c b/contrib/bmake/suff.c
index 5a40b73d788d..bcab3027e6dd 100644
--- a/contrib/bmake/suff.c
+++ b/contrib/bmake/suff.c
@@ -1,4 +1,4 @@
-/* $NetBSD: suff.c,v 1.88 2020/07/03 08:02:55 rillig Exp $ */
+/* $NetBSD: suff.c,v 1.142 2020/08/31 16:44:25 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -69,14 +69,14 @@
*/
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: suff.c,v 1.88 2020/07/03 08:02:55 rillig Exp $";
+static char rcsid[] = "$NetBSD: suff.c,v 1.142 2020/08/31 16:44:25 rillig Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)suff.c 8.4 (Berkeley) 3/21/94";
#else
-__RCSID("$NetBSD: suff.c,v 1.88 2020/07/03 08:02:55 rillig Exp $");
+__RCSID("$NetBSD: suff.c,v 1.142 2020/08/31 16:44:25 rillig Exp $");
#endif
#endif /* not lint */
#endif
@@ -135,12 +135,21 @@ __RCSID("$NetBSD: suff.c,v 1.88 2020/07/03 08:02:55 rillig Exp $");
* order to find the node.
*/
-#include <assert.h>
-#include <stdio.h>
#include "make.h"
-#include "hash.h"
#include "dir.h"
+#define SUFF_DEBUG0(fmt) \
+ if (!DEBUG(SUFF)) (void) 0; else fprintf(debug_file, fmt)
+
+#define SUFF_DEBUG1(fmt, arg1) \
+ if (!DEBUG(SUFF)) (void) 0; else fprintf(debug_file, fmt, arg1)
+
+#define SUFF_DEBUG2(fmt, arg1, arg2) \
+ if (!DEBUG(SUFF)) (void) 0; else fprintf(debug_file, fmt, arg1, arg2)
+
+#define SUFF_DEBUG3(fmt, arg1, arg2, arg3) \
+ if (!DEBUG(SUFF)) (void) 0; else fprintf(debug_file, fmt, arg1, arg2, arg3)
+
static Lst sufflist; /* Lst of suffixes */
#ifdef CLEANUP
static Lst suffClean; /* Lst of suffixes to be cleaned */
@@ -150,16 +159,23 @@ static Lst transforms; /* Lst of transformation rules */
static int sNum = 0; /* Counter for assigning suffix numbers */
+typedef enum {
+ SUFF_INCLUDE = 0x01, /* One which is #include'd */
+ SUFF_LIBRARY = 0x02, /* One which contains a library */
+ SUFF_NULL = 0x04 /* The empty suffix */
+ /* XXX: Why is SUFF_NULL needed? Wouldn't nameLen == 0 mean the same? */
+} SuffFlags;
+
+ENUM_FLAGS_RTTI_3(SuffFlags,
+ SUFF_INCLUDE, SUFF_LIBRARY, SUFF_NULL);
+
/*
* Structure describing an individual suffix.
*/
-typedef struct _Suff {
- char *name; /* The suffix itself */
- int nameLen; /* Length of the suffix */
- short flags; /* Type of suffix */
-#define SUFF_INCLUDE 0x01 /* One which is #include'd */
-#define SUFF_LIBRARY 0x02 /* One which contains a library */
-#define SUFF_NULL 0x04 /* The empty suffix */
+typedef struct Suff {
+ char *name; /* The suffix itself, such as ".c" */
+ int nameLen; /* Length of the name, to avoid strlen calls */
+ SuffFlags flags; /* Type of suffix */
Lst searchPath; /* The path along which files of this suffix
* may be found */
int sNum; /* The suffix number */
@@ -170,14 +186,6 @@ typedef struct _Suff {
} Suff;
/*
- * for SuffSuffIsSuffix
- */
-typedef struct {
- char *ename; /* The end of the name */
- int len; /* Length of the name */
-} SuffixCmpData;
-
-/*
* Structure used in the search for implied sources.
*/
typedef struct _Src {
@@ -213,12 +221,6 @@ static Suff *emptySuff; /* The empty suffix required for POSIX
* single-suffix transformation rules */
-static const char *SuffStrIsPrefix(const char *, const char *);
-static char *SuffSuffIsSuffix(const Suff *, const SuffixCmpData *);
-static int SuffSuffIsSuffixP(const void *, const void *);
-static int SuffSuffHasNameP(const void *, const void *);
-static int SuffSuffIsPrefix(const void *, const void *);
-static int SuffGNHasNameP(const void *, const void *);
static void SuffUnRef(void *, void *);
static void SuffFree(void *);
static void SuffInsert(Lst, Suff *);
@@ -227,10 +229,7 @@ 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 *);
@@ -269,35 +268,33 @@ SuffStrIsPrefix(const char *pref, const char *str)
return *pref ? NULL : str;
}
-/*-
- *-----------------------------------------------------------------------
- * SuffSuffIsSuffix --
- * See if suff is a suffix of str. sd->ename should point to THE END
- * of the string to check. (THE END == the null byte)
+typedef struct {
+ char *ename; /* The end of the name */
+ int len; /* Length of the name */
+} SuffSuffGetSuffixArgs;
+
+/* See if suff is a suffix of str. str->ename should point to THE END
+ * of the string to check. (THE END == the null byte)
*
* Input:
* s possible suffix
- * sd string to examine
+ * str string to examine
*
* Results:
* NULL if it ain't, pointer to character in str before suffix if
* it is.
- *
- * Side Effects:
- * None
- *-----------------------------------------------------------------------
*/
static char *
-SuffSuffIsSuffix(const Suff *s, const SuffixCmpData *sd)
+SuffSuffGetSuffix(const Suff *s, const SuffSuffGetSuffixArgs *str)
{
char *p1; /* Pointer into suffix name */
char *p2; /* Pointer into string being examined */
- if (sd->len < s->nameLen)
+ if (str->len < s->nameLen)
return NULL; /* this string is shorter than the suffix */
p1 = s->name + s->nameLen;
- p2 = sd->ename;
+ p2 = str->ename;
while (p1 >= s->name && *p1 == *p2) {
p1--;
@@ -307,126 +304,55 @@ SuffSuffIsSuffix(const Suff *s, const SuffixCmpData *sd)
return p1 == s->name - 1 ? p2 : NULL;
}
-/*-
- *-----------------------------------------------------------------------
- * SuffSuffIsSuffixP --
- * Predicate form of SuffSuffIsSuffix. Passed as the callback function
- * to Lst_Find.
- *
- * Results:
- * 0 if the suffix is the one desired, non-zero if not.
- *
- * Side Effects:
- * None.
- *
- *-----------------------------------------------------------------------
- */
-static int
-SuffSuffIsSuffixP(const void *s, const void *sd)
+/* Predicate form of SuffSuffGetSuffix, for Lst_Find. */
+static Boolean
+SuffSuffIsSuffix(const void *s, const void *sd)
{
- return !SuffSuffIsSuffix(s, sd);
+ return SuffSuffGetSuffix(s, sd) != NULL;
}
-/*-
- *-----------------------------------------------------------------------
- * SuffSuffHasNameP --
- * Callback procedure for finding a suffix based on its name. Used by
- * Suff_GetPath.
- *
- * Input:
- * s Suffix to check
- * sd Desired name
- *
- * Results:
- * 0 if the suffix is of the given name. non-zero otherwise.
- *
- * Side Effects:
- * None
- *-----------------------------------------------------------------------
- */
-static int
-SuffSuffHasNameP(const void *s, const void *sname)
+/* See if the suffix has the desired name. */
+static Boolean
+SuffSuffHasName(const void *s, const void *desiredName)
{
- return strcmp(sname, ((const Suff *)s)->name);
+ return strcmp(((const Suff *)s)->name, desiredName) == 0;
}
-/*-
- *-----------------------------------------------------------------------
- * SuffSuffIsPrefix --
- * See if the suffix described by s is a prefix of the string. Care
- * must be taken when using this to search for transformations and
- * what-not, since there could well be two suffixes, one of which
- * is a prefix of the other...
- *
- * Input:
- * s suffix to compare
- * str string to examine
- *
- * Results:
- * 0 if s is a prefix of str. non-zero otherwise
- *
- * Side Effects:
- * None
- *-----------------------------------------------------------------------
- */
-static int
+/* See if the suffix name is a prefix of the string. Care must be taken when
+ * using this to search for transformations and what-not, since there could
+ * well be two suffixes, one of which is a prefix of the other... */
+static Boolean
SuffSuffIsPrefix(const void *s, const void *str)
{
- return SuffStrIsPrefix(((const Suff *)s)->name, str) == NULL;
+ return SuffStrIsPrefix(((const Suff *)s)->name, str) != NULL;
}
-/*-
- *-----------------------------------------------------------------------
- * SuffGNHasNameP --
- * See if the graph node has the desired name
- *
- * Input:
- * gn current node we're looking at
- * name name we're looking for
- *
- * Results:
- * 0 if it does. non-zero if it doesn't
- *
- * Side Effects:
- * None
- *-----------------------------------------------------------------------
- */
-static int
-SuffGNHasNameP(const void *gn, const void *name)
+/* See if the graph node has the desired name. */
+static Boolean
+SuffGNHasName(const void *gn, const void *desiredName)
{
- return strcmp(name, ((const GNode *)gn)->name);
+ return strcmp(((const GNode *)gn)->name, desiredName) == 0;
}
- /*********** Maintenance Functions ************/
+ /*********** Maintenance Functions ************/
static void
SuffUnRef(void *lp, void *sp)
{
Lst l = (Lst) lp;
- LstNode ln = Lst_Member(l, sp);
+ LstNode ln = Lst_FindDatum(l, sp);
if (ln != NULL) {
Lst_Remove(l, ln);
((Suff *)sp)->refCount--;
}
}
-/*-
- *-----------------------------------------------------------------------
- * SuffFree --
- * Free up all memory associated with the given suffix structure.
- *
- * Results:
- * none
- *
- * Side Effects:
- * the suffix entry is detroyed
- *-----------------------------------------------------------------------
- */
+/* Free up all memory associated with the given suffix structure. */
static void
SuffFree(void *sp)
{
- Suff *s = (Suff *)sp;
+ Suff *s = (Suff *)sp;
if (s == suffNull)
suffNull = NULL;
@@ -441,28 +367,16 @@ SuffFree(void *sp)
s->refCount);
#endif
- Lst_Destroy(s->ref, NULL);
- Lst_Destroy(s->children, NULL);
- Lst_Destroy(s->parents, NULL);
+ Lst_Free(s->ref);
+ Lst_Free(s->children);
+ Lst_Free(s->parents);
Lst_Destroy(s->searchPath, Dir_Destroy);
free(s->name);
free(s);
}
-/*-
- *-----------------------------------------------------------------------
- * SuffRemove --
- * Remove the suffix into the list
- *
- * Results:
- * None
- *
- * Side Effects:
- * The reference count for the suffix is decremented and the
- * suffix is possibly freed
- *-----------------------------------------------------------------------
- */
+/* Remove the suffix from the list, and free if it is otherwise unused. */
static void
SuffRemove(Lst l, Suff *s)
{
@@ -472,23 +386,12 @@ SuffRemove(Lst l, Suff *s)
SuffFree(s);
}
}
-
-/*-
- *-----------------------------------------------------------------------
- * SuffInsert --
- * Insert the suffix into the list keeping the list ordered by suffix
- * numbers.
+
+/* Insert the suffix into the list keeping the list ordered by suffix numbers.
*
* Input:
* l the list where in s should be inserted
* s the suffix to insert
- *
- * Results:
- * None
- *
- * Side Effects:
- * The reference count of the suffix is incremented
- *-----------------------------------------------------------------------
*/
static void
SuffInsert(Lst l, Suff *s)
@@ -496,100 +399,83 @@ SuffInsert(Lst l, Suff *s)
LstNode ln; /* current element in l we're examining */
Suff *s2 = NULL; /* the suffix descriptor in this element */
- if (Lst_Open(l) == FAILURE) {
- return;
- }
+ Lst_Open(l);
while ((ln = Lst_Next(l)) != NULL) {
- s2 = (Suff *)Lst_Datum(ln);
+ s2 = LstNode_Datum(ln);
if (s2->sNum >= s->sNum) {
break;
}
}
-
Lst_Close(l);
- if (DEBUG(SUFF)) {
- fprintf(debug_file, "inserting %s(%d)...", s->name, s->sNum);
- }
+
+ SUFF_DEBUG2("inserting %s(%d)...", s->name, s->sNum);
+
if (ln == NULL) {
- if (DEBUG(SUFF)) {
- fprintf(debug_file, "at end of list\n");
- }
- (void)Lst_AtEnd(l, s);
+ SUFF_DEBUG0("at end of list\n");
+ Lst_Append(l, s);
s->refCount++;
- (void)Lst_AtEnd(s->ref, l);
+ Lst_Append(s->ref, l);
} else if (s2->sNum != s->sNum) {
- if (DEBUG(SUFF)) {
- fprintf(debug_file, "before %s(%d)\n", s2->name, s2->sNum);
- }
- (void)Lst_InsertBefore(l, ln, s);
+ SUFF_DEBUG2("before %s(%d)\n", s2->name, s2->sNum);
+ Lst_InsertBefore(l, ln, s);
s->refCount++;
- (void)Lst_AtEnd(s->ref, l);
- } else if (DEBUG(SUFF)) {
- fprintf(debug_file, "already there\n");
+ Lst_Append(s->ref, l);
+ } else {
+ SUFF_DEBUG0("already there\n");
}
}
-/*-
- *-----------------------------------------------------------------------
- * Suff_ClearSuffixes --
- * This is gross. Nuke the list of suffixes but keep all transformation
- * rules around. The transformation graph is destroyed in this process,
- * but we leave the list of rules so when a new graph is formed the rules
- * will remain.
- * This function is called from the parse module when a
- * .SUFFIXES:\n line is encountered.
- *
- * Results:
- * none
- *
- * Side Effects:
- * the sufflist and its graph nodes are destroyed
- *-----------------------------------------------------------------------
- */
+static Suff *
+SuffNew(const char *name)
+{
+ Suff *s = bmake_malloc(sizeof(Suff));
+
+ s->name = bmake_strdup(name);
+ s->nameLen = strlen(s->name);
+ s->searchPath = Lst_Init();
+ s->children = Lst_Init();
+ s->parents = Lst_Init();
+ s->ref = Lst_Init();
+ s->sNum = sNum++;
+ s->flags = 0;
+ s->refCount = 1;
+
+ return s;
+}
+
+/* This is gross. Nuke the list of suffixes but keep all transformation
+ * rules around. The transformation graph is destroyed in this process, but
+ * we leave the list of rules so when a new graph is formed the rules will
+ * remain. This function is called from the parse module when a .SUFFIXES:\n
+ * line is encountered. */
void
Suff_ClearSuffixes(void)
{
#ifdef CLEANUP
- Lst_Concat(suffClean, sufflist, LST_CONCLINK);
+ Lst_MoveAll(suffClean, sufflist);
#endif
- sufflist = Lst_Init(FALSE);
+ sufflist = Lst_Init();
sNum = 0;
if (suffNull)
SuffFree(suffNull);
- emptySuff = suffNull = bmake_malloc(sizeof(Suff));
+ emptySuff = suffNull = SuffNew("");
- suffNull->name = bmake_strdup("");
- suffNull->nameLen = 0;
- suffNull->searchPath = Lst_Init(FALSE);
Dir_Concat(suffNull->searchPath, dirSearchPath);
- suffNull->children = Lst_Init(FALSE);
- suffNull->parents = Lst_Init(FALSE);
- suffNull->ref = Lst_Init(FALSE);
- suffNull->sNum = sNum++;
suffNull->flags = SUFF_NULL;
- suffNull->refCount = 1;
}
-/*-
- *-----------------------------------------------------------------------
- * SuffParseTransform --
- * Parse a transformation string to find its two component suffixes.
+/* 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.
+ * out_src Place to store source of trans.
+ * out_targ Place to store target of trans.
*
* Results:
- * TRUE if the string is a valid transformation and FALSE otherwise.
- *
- * Side Effects:
- * The passed pointers are overwritten.
- *
- *-----------------------------------------------------------------------
+ * TRUE if the string is a valid transformation, FALSE otherwise.
*/
static Boolean
-SuffParseTransform(char *str, Suff **srcPtr, Suff **targPtr)
+SuffParseTransform(char *str, Suff **out_src, Suff **out_targ)
{
LstNode srcLn; /* element in suffix list of trans source*/
Suff *src; /* Source of transformation */
@@ -611,10 +497,10 @@ SuffParseTransform(char *str, Suff **srcPtr, Suff **targPtr)
*/
for (;;) {
if (srcLn == NULL) {
- srcLn = Lst_Find(sufflist, str, SuffSuffIsPrefix);
+ srcLn = Lst_Find(sufflist, SuffSuffIsPrefix, str);
} else {
- srcLn = Lst_FindFrom(sufflist, Lst_Succ(srcLn), str,
- SuffSuffIsPrefix);
+ srcLn = Lst_FindFrom(sufflist, LstNode_Next(srcLn),
+ SuffSuffIsPrefix, str);
}
if (srcLn == NULL) {
/*
@@ -630,45 +516,30 @@ SuffParseTransform(char *str, Suff **srcPtr, Suff **targPtr)
*
* XXX: Use emptySuff over suffNull?
*/
- *srcPtr = single;
- *targPtr = suffNull;
+ *out_src = single;
+ *out_targ = suffNull;
return TRUE;
}
return FALSE;
}
- src = (Suff *)Lst_Datum(srcLn);
+ src = LstNode_Datum(srcLn);
str2 = str + src->nameLen;
if (*str2 == '\0') {
single = src;
singleLn = srcLn;
} else {
- targLn = Lst_Find(sufflist, str2, SuffSuffHasNameP);
+ targLn = Lst_Find(sufflist, SuffSuffHasName, str2);
if (targLn != NULL) {
- *srcPtr = src;
- *targPtr = (Suff *)Lst_Datum(targLn);
+ *out_src = src;
+ *out_targ = LstNode_Datum(targLn);
return TRUE;
}
}
}
}
-/*-
- *-----------------------------------------------------------------------
- * Suff_IsTransform --
- * Return TRUE if the given string is a transformation rule
- *
- *
- * Input:
- * str string to check
- *
- * Results:
- * TRUE if the string is a concatenation of two known suffixes.
- * FALSE otherwise
- *
- * Side Effects:
- * None
- *-----------------------------------------------------------------------
- */
+/* Return TRUE if the given string is a transformation rule, that is, a
+ * concatenation of two known suffixes. */
Boolean
Suff_IsTransform(char *str)
{
@@ -677,39 +548,35 @@ Suff_IsTransform(char *str)
return SuffParseTransform(str, &src, &targ);
}
-/*-
- *-----------------------------------------------------------------------
- * Suff_AddTransform --
- * Add the transformation rule described by the line to the
- * list of rules and place the transformation itself in the graph
+/* Add the transformation rule described by the line to the list of rules
+ * and place the transformation itself in the graph.
*
+ * The node is placed on the end of the transforms Lst and links are made
+ * between the two suffixes mentioned in the target name.
+
* Input:
* line name of transformation to add
*
* Results:
* The node created for the transformation in the transforms list
- *
- * Side Effects:
- * The node is placed on the end of the transforms Lst and links are
- * made between the two suffixes mentioned in the target name
- *-----------------------------------------------------------------------
*/
GNode *
Suff_AddTransform(char *line)
{
GNode *gn; /* GNode of transformation rule */
Suff *s, /* source suffix */
- *t; /* target suffix */
+ *t; /* target suffix */
LstNode ln; /* Node for existing transformation */
+ Boolean ok;
- ln = Lst_Find(transforms, line, SuffGNHasNameP);
+ ln = Lst_Find(transforms, SuffGNHasName, line);
if (ln == NULL) {
/*
* Make a new graph node for the transformation. It will be filled in
* by the Parse module.
*/
gn = Targ_NewGN(line);
- (void)Lst_AtEnd(transforms, gn);
+ Lst_Append(transforms, gn);
} else {
/*
* New specification for transformation rule. Just nuke the old list
@@ -717,58 +584,50 @@ Suff_AddTransform(char *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, NULL);
- Lst_Destroy(gn->children, NULL);
- gn->commands = Lst_Init(FALSE);
- gn->children = Lst_Init(FALSE);
+ gn = LstNode_Datum(ln);
+ Lst_Free(gn->commands);
+ Lst_Free(gn->children);
+ gn->commands = Lst_Init();
+ gn->children = Lst_Init();
}
gn->type = OP_TRANSFORM;
- (void)SuffParseTransform(line, &s, &t);
+ ok = SuffParseTransform(line, &s, &t);
+ assert(ok);
+ (void)ok;
/*
* link the two together in the proper relationship and order
*/
- if (DEBUG(SUFF)) {
- fprintf(debug_file, "defining transformation from `%s' to `%s'\n",
+ SUFF_DEBUG2("defining transformation from `%s' to `%s'\n",
s->name, t->name);
- }
SuffInsert(t->children, s);
SuffInsert(s->parents, t);
return gn;
}
-/*-
- *-----------------------------------------------------------------------
- * Suff_EndTransform --
- * Handle the finish of a transformation definition, removing the
- * transformation from the graph if it has neither commands nor
- * sources. This is a callback procedure for the Parse module via
- * Lst_ForEach
+/* Handle the finish of a transformation definition, removing the
+ * transformation from the graph if it has neither commands nor sources.
+ * This is a callback procedure for the Parse module via Lst_ForEach.
+ *
+ * If the node has no commands or children, the children and parents lists
+ * of the affected suffixes are altered.
*
* Input:
* gnp Node for transformation
- * dummy Node for transformation
*
* Results:
- * === 0
- *
- * Side Effects:
- * If the node has no commands or children, the children and parents
- * lists of the affected suffixes are altered.
- *
- *-----------------------------------------------------------------------
+ * 0, so that Lst_ForEach continues
*/
int
Suff_EndTransform(void *gnp, void *dummy MAKE_ATTR_UNUSED)
{
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_DOUBLEDEP) && !Lst_IsEmpty(gn->cohorts))
+ gn = LstNode_Datum(Lst_Last(gn->cohorts));
if ((gn->type & OP_TRANSFORM) && Lst_IsEmpty(gn->commands) &&
Lst_IsEmpty(gn->children))
{
@@ -781,10 +640,8 @@ Suff_EndTransform(void *gnp, void *dummy MAKE_ATTR_UNUSED)
if (SuffParseTransform(gn->name, &s, &t)) {
Lst p;
- if (DEBUG(SUFF)) {
- fprintf(debug_file, "deleting transformation from `%s' to `%s'\n",
- s->name, t->name);
- }
+ SUFF_DEBUG2("deleting transformation from `%s' to `%s'\n",
+ s->name, t->name);
/*
* Store s->parents because s could be deleted in SuffRemove
@@ -806,35 +663,28 @@ Suff_EndTransform(void *gnp, void *dummy MAKE_ATTR_UNUSED)
*/
SuffRemove(p, t);
}
- } else if ((gn->type & OP_TRANSFORM) && DEBUG(SUFF)) {
- fprintf(debug_file, "transformation %s complete\n", gn->name);
+ } else if (gn->type & OP_TRANSFORM) {
+ SUFF_DEBUG1("transformation %s complete\n", gn->name);
}
return 0;
}
-/*-
- *-----------------------------------------------------------------------
- * SuffRebuildGraph --
- * Called from Suff_AddSuffix via Lst_ForEach to search through the
- * list of existing transformation rules and rebuild the transformation
- * graph when it has been destroyed by Suff_ClearSuffixes. If the
- * given rule is a transformation involving this suffix and another,
- * existing suffix, the proper relationship is established between
- * the two.
+/* Called from Suff_AddSuffix via Lst_ForEach to search through the list of
+ * existing transformation rules and rebuild the transformation graph when
+ * it has been destroyed by Suff_ClearSuffixes. If the given rule is a
+ * transformation involving this suffix and another, existing suffix, the
+ * proper relationship is established between the two.
+ *
+ * The appropriate links will be made between this suffix and others if
+ * transformation rules exist for it.
*
* Input:
* transformp Transformation to test
* sp Suffix to rebuild
*
* Results:
- * Always 0.
- *
- * Side Effects:
- * The appropriate links will be made between this suffix and
- * others if transformation rules exist for it.
- *
- *-----------------------------------------------------------------------
+ * 0, so that Lst_ForEach continues
*/
static int
SuffRebuildGraph(void *transformp, void *sp)
@@ -844,20 +694,20 @@ SuffRebuildGraph(void *transformp, void *sp)
char *cp;
LstNode ln;
Suff *s2;
- SuffixCmpData sd;
+ SuffSuffGetSuffixArgs sd;
/*
* First see if it is a transformation from this suffix.
*/
cp = UNCONST(SuffStrIsPrefix(s->name, transform->name));
if (cp != NULL) {
- ln = Lst_Find(sufflist, cp, SuffSuffHasNameP);
+ ln = Lst_Find(sufflist, SuffSuffHasName, cp);
if (ln != NULL) {
/*
* Found target. Link in and return, since it can't be anything
* else.
*/
- s2 = (Suff *)Lst_Datum(ln);
+ s2 = LstNode_Datum(ln);
SuffInsert(s2->children, s);
SuffInsert(s->parents, s2);
return 0;
@@ -869,13 +719,13 @@ SuffRebuildGraph(void *transformp, void *sp)
*/
sd.len = strlen(transform->name);
sd.ename = transform->name + sd.len;
- cp = SuffSuffIsSuffix(s, &sd);
+ cp = SuffSuffGetSuffix(s, &sd);
if (cp != NULL) {
/*
* Null-terminate the source suffix in order to find it.
*/
cp[1] = '\0';
- ln = Lst_Find(sufflist, transform->name, SuffSuffHasNameP);
+ ln = Lst_Find(sufflist, SuffSuffHasName, transform->name);
/*
* Replace the start of the target suffix
*/
@@ -884,7 +734,7 @@ SuffRebuildGraph(void *transformp, void *sp)
/*
* Found it -- establish the proper relationship
*/
- s2 = (Suff *)Lst_Datum(ln);
+ s2 = LstNode_Datum(ln);
SuffInsert(s->children, s2);
SuffInsert(s2->parents, s);
}
@@ -892,22 +742,16 @@ SuffRebuildGraph(void *transformp, void *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.
+/* 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.
+ *
+ * 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.
*
* 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)
@@ -923,7 +767,7 @@ SuffScanTargets(void *targetp, void *gsp)
return 1;
}
- if ((unsigned int)target->type == OP_TRANSFORM)
+ if (target->type == OP_TRANSFORM)
return 0;
if ((ptr = strstr(target->name, gs->s->name)) == NULL ||
@@ -936,63 +780,43 @@ SuffScanTargets(void *targetp, void *gsp)
*gs->gn = NULL;
Targ_SetMain(NULL);
}
- Lst_Destroy(target->children, NULL);
- target->children = Lst_Init(FALSE);
+ Lst_Free(target->children);
+ target->children = Lst_Init();
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);
- }
+ SUFF_DEBUG2("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...
+/* Add the suffix 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
+ * 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.
*
- * 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.
- *-----------------------------------------------------------------------
+ * Input:
+ * name the name of the suffix to add
*/
void
-Suff_AddSuffix(char *str, GNode **gn)
+Suff_AddSuffix(const char *name, GNode **gn)
{
Suff *s; /* new suffix descriptor */
LstNode ln;
GNodeSuff gs;
- ln = Lst_Find(sufflist, str, SuffSuffHasNameP);
+ ln = Lst_Find(sufflist, SuffSuffHasName, name);
if (ln == NULL) {
- s = bmake_malloc(sizeof(Suff));
-
- s->name = bmake_strdup(str);
- s->nameLen = strlen(s->name);
- s->searchPath = Lst_Init(FALSE);
- s->children = Lst_Init(FALSE);
- s->parents = Lst_Init(FALSE);
- s->ref = Lst_Init(FALSE);
- s->sNum = sNum++;
- s->flags = 0;
- s->refCount = 1;
-
- (void)Lst_AtEnd(sufflist, s);
+ s = SuffNew(name);
+
+ Lst_Append(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
@@ -1011,51 +835,30 @@ Suff_AddSuffix(char *str, GNode **gn)
}
}
-/*-
- *-----------------------------------------------------------------------
- * Suff_GetPath --
- * Return the search path for the given suffix, if it's defined.
- *
- * Results:
- * The searchPath for the desired suffix or NULL if the suffix isn't
- * defined.
- *
- * Side Effects:
- * None
- *-----------------------------------------------------------------------
- */
+/* Return the search path for the given suffix, or NULL. */
Lst
Suff_GetPath(char *sname)
{
LstNode ln;
Suff *s;
- ln = Lst_Find(sufflist, sname, SuffSuffHasNameP);
+ ln = Lst_Find(sufflist, SuffSuffHasName, sname);
if (ln == NULL) {
return NULL;
} else {
- s = (Suff *)Lst_Datum(ln);
+ s = LstNode_Datum(ln);
return s->searchPath;
}
}
-/*-
- *-----------------------------------------------------------------------
- * Suff_DoPaths --
- * Extend the search paths for all suffixes to include the default
- * search path.
+/* Extend the search paths for all suffixes to include the default search
+ * path.
*
- * Results:
- * None.
- *
- * Side Effects:
- * The searchPath field of all the suffixes is extended by the
- * directories in dirSearchPath. If paths were specified for the
- * ".h" suffix, the directories are stuffed into a global variable
- * called ".INCLUDES" with each directory preceded by a -I. The same
- * is done for the ".a" suffix, except the variable is called
- * ".LIBS" and the flag is -L.
- *-----------------------------------------------------------------------
+ * The searchPath field of all the suffixes is extended by the directories
+ * in dirSearchPath. If paths were specified for the ".h" suffix, the
+ * directories are stuffed into a global variable called ".INCLUDES" with
+ * each directory preceded by a -I. The same is done for the ".a" suffix,
+ * except the variable is called ".LIBS" and the flag is -L.
*/
void
Suff_DoPaths(void)
@@ -1066,16 +869,14 @@ Suff_DoPaths(void)
Lst inIncludes; /* Cumulative .INCLUDES path */
Lst inLibs; /* Cumulative .LIBS path */
- if (Lst_Open(sufflist) == FAILURE) {
- return;
- }
- inIncludes = Lst_Init(FALSE);
- inLibs = Lst_Init(FALSE);
+ inIncludes = Lst_Init();
+ inLibs = Lst_Init();
+ Lst_Open(sufflist);
while ((ln = Lst_Next(sufflist)) != NULL) {
- s = (Suff *)Lst_Datum(ln);
- if (!Lst_IsEmpty (s->searchPath)) {
+ s = LstNode_Datum(ln);
+ if (!Lst_IsEmpty(s->searchPath)) {
#ifdef INCLUDES
if (s->flags & SUFF_INCLUDE) {
Dir_Concat(inIncludes, s->searchPath);
@@ -1089,9 +890,10 @@ Suff_DoPaths(void)
Dir_Concat(s->searchPath, dirSearchPath);
} else {
Lst_Destroy(s->searchPath, Dir_Destroy);
- s->searchPath = Lst_Duplicate(dirSearchPath, Dir_CopyDir);
+ s->searchPath = Lst_Copy(dirSearchPath, Dir_CopyDir);
}
}
+ Lst_Close(sufflist);
Var_Set(".INCLUDES", ptr = Dir_MakeFlags("-I", inIncludes), VAR_GLOBAL);
free(ptr);
@@ -1100,27 +902,15 @@ Suff_DoPaths(void)
Lst_Destroy(inIncludes, Dir_Destroy);
Lst_Destroy(inLibs, Dir_Destroy);
-
- Lst_Close(sufflist);
}
-/*-
- *-----------------------------------------------------------------------
- * Suff_AddInclude --
- * Add the given suffix as a type of file which gets included.
- * Called from the parse module when a .INCLUDES line is parsed.
- * The suffix must have already been defined.
+/* Add the given suffix as a type of file which gets included.
+ * Called from the parse module when a .INCLUDES line is parsed.
+ * The suffix must have already been defined.
+ * The SUFF_INCLUDE bit is set in the suffix's flags field.
*
* Input:
* sname Name of the suffix to mark
- *
- * Results:
- * None.
- *
- * Side Effects:
- * The SUFF_INCLUDE bit is set in the suffix's flags field
- *
- *-----------------------------------------------------------------------
*/
void
Suff_AddInclude(char *sname)
@@ -1128,64 +918,46 @@ Suff_AddInclude(char *sname)
LstNode ln;
Suff *s;
- ln = Lst_Find(sufflist, sname, SuffSuffHasNameP);
+ ln = Lst_Find(sufflist, SuffSuffHasName, sname);
if (ln != NULL) {
- s = (Suff *)Lst_Datum(ln);
+ s = LstNode_Datum(ln);
s->flags |= SUFF_INCLUDE;
}
}
-/*-
- *-----------------------------------------------------------------------
- * Suff_AddLib --
- * Add the given suffix as a type of file which is a library.
- * Called from the parse module when parsing a .LIBS line. The
- * suffix must have been defined via .SUFFIXES before this is
- * called.
+/* Add the given suffix as a type of file which is a library.
+ * Called from the parse module when parsing a .LIBS line.
+ * The suffix must have been defined via .SUFFIXES before this is called.
+ * The SUFF_LIBRARY bit is set in the suffix's flags field.
*
* Input:
* sname Name of the suffix to mark
- *
- * Results:
- * None.
- *
- * Side Effects:
- * The SUFF_LIBRARY bit is set in the suffix's flags field
- *
- *-----------------------------------------------------------------------
*/
void
-Suff_AddLib(char *sname)
+Suff_AddLib(const char *sname)
{
LstNode ln;
Suff *s;
- ln = Lst_Find(sufflist, sname, SuffSuffHasNameP);
+ ln = Lst_Find(sufflist, SuffSuffHasName, sname);
if (ln != NULL) {
- s = (Suff *)Lst_Datum(ln);
+ s = LstNode_Datum(ln);
s->flags |= SUFF_LIBRARY;
}
}
- /********** Implicit Source Search Functions *********/
+ /********** Implicit Source Search Functions *********/
-/*-
- *-----------------------------------------------------------------------
- * SuffAddSrc --
- * Add a suffix as a Src structure to the given list with its parent
- * being the given Src structure. If the suffix is the null suffix,
- * the prefix is used unaltered as the file name in the Src structure.
+/* Add a suffix as a Src structure to the given list with its parent
+ * being the given Src structure. If the suffix is the null suffix,
+ * the prefix is used unaltered as the file name in the Src structure.
*
* Input:
* sp suffix for which to create a Src structure
* lsp list and parent for the new Src
*
* Results:
- * always returns 0
- *
- * Side Effects:
- * A Src structure is created and tacked onto the end of the list
- *-----------------------------------------------------------------------
+ * 0, so that Lst_ForEach continues
*/
static int
SuffAddSrc(void *sp, void *lsp)
@@ -1212,17 +984,17 @@ SuffAddSrc(void *sp, void *lsp)
s->refCount++;
s2->children = 0;
targ->children += 1;
- (void)Lst_AtEnd(ls->l, s2);
+ Lst_Append(ls->l, s2);
#ifdef DEBUG_SRC
- s2->cp = Lst_Init(FALSE);
- Lst_AtEnd(targ->cp, s2);
+ s2->cp = Lst_Init();
+ Lst_Append(targ->cp, s2);
fprintf(debug_file, "1 add %p %p to %p:", targ, s2, ls->l);
Lst_ForEach(ls->l, PrintAddr, NULL);
fprintf(debug_file, "\n");
#endif
}
s2 = bmake_malloc(sizeof(Src));
- s2->file = str_concat(targ->pref, s->name, 0);
+ s2->file = str_concat2(targ->pref, s->name);
s2->pref = targ->pref;
s2->parent = targ;
s2->node = NULL;
@@ -1230,10 +1002,10 @@ SuffAddSrc(void *sp, void *lsp)
s->refCount++;
s2->children = 0;
targ->children += 1;
- (void)Lst_AtEnd(ls->l, s2);
+ Lst_Append(ls->l, s2);
#ifdef DEBUG_SRC
- s2->cp = Lst_Init(FALSE);
- Lst_AtEnd(targ->cp, s2);
+ s2->cp = Lst_Init();
+ Lst_Append(targ->cp, s2);
fprintf(debug_file, "2 add %p %p to %p:", targ, s2, ls->l);
Lst_ForEach(ls->l, PrintAddr, NULL);
fprintf(debug_file, "\n");
@@ -1242,21 +1014,11 @@ SuffAddSrc(void *sp, void *lsp)
return 0;
}
-/*-
- *-----------------------------------------------------------------------
- * SuffAddLevel --
- * Add all the children of targ as Src structures to the given list
+/* Add all the children of targ as Src structures to the given list.
*
* Input:
* l list to which to add the new level
* targ Src structure to use as the parent
- *
- * Results:
- * None
- *
- * Side Effects:
- * Lots of structures are created and added to the list
- *-----------------------------------------------------------------------
*/
static void
SuffAddLevel(Lst l, Src *targ)
@@ -1269,44 +1031,31 @@ SuffAddLevel(Lst l, Src *targ)
Lst_ForEach(targ->suff->children, SuffAddSrc, &ls);
}
-/*-
- *----------------------------------------------------------------------
- * SuffRemoveSrc --
- * Free all src structures in list that don't have a reference count
- *
- * Results:
- * Ture if an src was removed
- *
- * Side Effects:
- * The memory is free'd.
- *----------------------------------------------------------------------
- */
-static int
+/* Free the first Src in the list that doesn't have a reference count.
+ * Return whether a Src was removed. */
+static Boolean
SuffRemoveSrc(Lst l)
{
LstNode ln;
Src *s;
- int t = 0;
- if (Lst_Open(l) == FAILURE) {
- return 0;
- }
+ Lst_Open(l);
+
#ifdef DEBUG_SRC
fprintf(debug_file, "cleaning %lx: ", (unsigned long) l);
Lst_ForEach(l, PrintAddr, NULL);
fprintf(debug_file, "\n");
#endif
-
while ((ln = Lst_Next(l)) != NULL) {
- s = (Src *)Lst_Datum(ln);
+ s = LstNode_Datum(ln);
if (s->children == 0) {
free(s->file);
if (!s->parent)
free(s->pref);
else {
#ifdef DEBUG_SRC
- LstNode ln2 = Lst_Member(s->parent->cp, s);
+ LstNode ln2 = Lst_FindDatum(s->parent->cp, s);
if (ln2 != NULL)
Lst_Remove(s->parent->cp, ln2);
#endif
@@ -1314,11 +1063,10 @@ SuffRemoveSrc(Lst l)
}
#ifdef DEBUG_SRC
fprintf(debug_file, "free: [l=%p] p=%p %d\n", l, s, s->children);
- Lst_Destroy(s->cp, NULL);
+ Lst_Free(s->cp);
#endif
Lst_Remove(l, ln);
free(s);
- t |= 1;
Lst_Close(l);
return TRUE;
}
@@ -1333,23 +1081,16 @@ SuffRemoveSrc(Lst l)
Lst_Close(l);
- return t;
+ return FALSE;
}
-/*-
- *-----------------------------------------------------------------------
- * SuffFindThem --
- * Find the first existing file/target in the list srcs
+/* Find the first existing file/target in the list srcs.
*
* Input:
* srcs list of Src structures to search through
*
* Results:
- * The lowest structure in the chain of transformations
- *
- * Side Effects:
- * None
- *-----------------------------------------------------------------------
+ * The lowest structure in the chain of transformations, or NULL.
*/
static Src *
SuffFindThem(Lst srcs, Lst slst)
@@ -1360,12 +1101,10 @@ SuffFindThem(Lst srcs, Lst slst)
rs = NULL;
- while (!Lst_IsEmpty (srcs)) {
- s = (Src *)Lst_DeQueue(srcs);
+ while (!Lst_IsEmpty(srcs)) {
+ s = Lst_Dequeue(srcs);
- if (DEBUG(SUFF)) {
- fprintf(debug_file, "\ttrying %s...", s->file);
- }
+ SUFF_DEBUG1("\ttrying %s...", s->file);
/*
* A file is considered to exist if either a node exists in the
@@ -1388,51 +1127,41 @@ SuffFindThem(Lst srcs, Lst slst)
break;
}
- if (DEBUG(SUFF)) {
- fprintf(debug_file, "not there\n");
- }
+ SUFF_DEBUG0("not there\n");
SuffAddLevel(srcs, s);
- Lst_AtEnd(slst, s);
+ Lst_Append(slst, s);
}
- if (DEBUG(SUFF) && rs) {
- fprintf(debug_file, "got it\n");
+ if (rs) {
+ SUFF_DEBUG0("got it\n");
}
return rs;
}
-/*-
- *-----------------------------------------------------------------------
- * SuffFindCmds --
- * See if any of the children of the target in the Src structure is
- * one from which the target can be transformed. If there is one,
- * a Src structure is put together for it and returned.
+/* See if any of the children of the target in the Src structure is one from
+ * which the target can be transformed. If there is one, a Src structure is
+ * put together for it and returned.
*
* Input:
- * targ Src structure to play with
+ * targ Src to play with
*
* Results:
- * The Src structure of the "winning" child, or NULL if no such beast.
- *
- * Side Effects:
- * A Src structure may be allocated.
- *
- *-----------------------------------------------------------------------
+ * The Src of the "winning" child, or NULL.
*/
static Src *
SuffFindCmds(Src *targ, Lst slst)
{
LstNode ln; /* General-purpose list node */
GNode *t, /* Target GNode */
- *s; /* Source GNode */
+ *s; /* Source GNode */
int prefLen;/* The length of the defined prefix */
Suff *suff; /* Suffix on matching beastie */
Src *ret; /* Return value */
char *cp;
t = targ->node;
- (void)Lst_Open(t->children);
+ Lst_Open(t->children);
prefLen = strlen(targ->pref);
for (;;) {
@@ -1441,7 +1170,7 @@ SuffFindCmds(Src *targ, Lst slst)
Lst_Close(t->children);
return NULL;
}
- s = (GNode *)Lst_Datum(ln);
+ s = LstNode_Datum(ln);
if (s->type & OP_OPTIONAL && Lst_IsEmpty(t->commands)) {
/*
@@ -1466,7 +1195,7 @@ SuffFindCmds(Src *targ, Lst slst)
* The node matches the prefix ok, see if it has a known
* suffix.
*/
- ln = Lst_Find(sufflist, &cp[prefLen], SuffSuffHasNameP);
+ ln = Lst_Find(sufflist, SuffSuffHasName, &cp[prefLen]);
if (ln == NULL)
continue;
/*
@@ -1475,9 +1204,11 @@ SuffFindCmds(Src *targ, Lst slst)
*
* XXX: Handle multi-stage transformations here, too.
*/
- suff = (Suff *)Lst_Datum(ln);
+ suff = LstNode_Datum(ln);
- if (Lst_Member(suff->parents, targ->suff) != NULL)
+ /* XXX: Can targ->suff be NULL here? */
+ if (targ->suff != NULL &&
+ Lst_FindDatum(suff->parents, targ->suff) != NULL)
break;
}
@@ -1497,41 +1228,30 @@ SuffFindCmds(Src *targ, Lst slst)
ret->children = 0;
targ->children += 1;
#ifdef DEBUG_SRC
- ret->cp = Lst_Init(FALSE);
+ ret->cp = Lst_Init();
fprintf(debug_file, "3 add %p %p\n", targ, ret);
- Lst_AtEnd(targ->cp, ret);
+ Lst_Append(targ->cp, ret);
#endif
- Lst_AtEnd(slst, ret);
- if (DEBUG(SUFF)) {
- fprintf(debug_file, "\tusing existing source %s\n", s->name);
- }
+ Lst_Append(slst, ret);
+ SUFF_DEBUG1("\tusing existing source %s\n", s->name);
+ Lst_Close(t->children);
return ret;
}
-/*-
- *-----------------------------------------------------------------------
- * SuffExpandChildren --
- * Expand the names of any children of a given node that contain
- * variable invocations or file wildcards into actual targets.
+/* Expand the names of any children of a given node that contain variable
+ * invocations or file wildcards into actual targets.
+ *
+ * The expanded node is removed from the parent's list of children, and the
+ * parent's unmade counter is decremented, but other nodes may be added.
*
* Input:
* cln Child to examine
* pgn Parent node being processed
- *
- * Results:
- * === 0 (continue)
- *
- * Side Effects:
- * The expanded node is removed from the parent's list of children,
- * and the parent's unmade counter is decremented, but other nodes
- * may be added.
- *
- *-----------------------------------------------------------------------
*/
static void
SuffExpandChildren(LstNode cln, GNode *pgn)
{
- GNode *cgn = (GNode *)Lst_Datum(cln);
+ GNode *cgn = LstNode_Datum(cln);
GNode *gn; /* New source 8) */
char *cp; /* Expanded value */
@@ -1554,13 +1274,11 @@ SuffExpandChildren(LstNode cln, GNode *pgn)
return;
}
- if (DEBUG(SUFF)) {
- fprintf(debug_file, "Expanding \"%s\"...", cgn->name);
- }
- cp = Var_Subst(NULL, cgn->name, pgn, VARF_UNDEFERR|VARF_WANTRES);
+ SUFF_DEBUG1("Expanding \"%s\"...", cgn->name);
+ cp = Var_Subst(cgn->name, pgn, VARE_UNDEFERR|VARE_WANTRES);
- if (cp != NULL) {
- Lst members = Lst_Init(FALSE);
+ {
+ Lst members = Lst_Init();
if (cgn->type & OP_ARCHV) {
/*
@@ -1592,7 +1310,7 @@ SuffExpandChildren(LstNode cln, GNode *pgn)
*/
*cp++ = '\0';
gn = Targ_FindNode(start, TARG_CREATE);
- (void)Lst_AtEnd(members, gn);
+ Lst_Append(members, gn);
while (*cp == ' ' || *cp == '\t') {
cp++;
}
@@ -1606,12 +1324,12 @@ SuffExpandChildren(LstNode cln, GNode *pgn)
* Start of a variable spec -- contact variable module
* to find the end so we can skip over it.
*/
- char *junk;
+ const char *junk;
int len;
void *freeIt;
- junk = Var_Parse(cp, pgn, VARF_UNDEFERR|VARF_WANTRES,
- &len, &freeIt);
+ junk = Var_Parse(cp, pgn, VARE_UNDEFERR|VARE_WANTRES,
+ &len, &freeIt);
if (junk != var_Error) {
cp += len - 1;
}
@@ -1630,7 +1348,7 @@ SuffExpandChildren(LstNode cln, GNode *pgn)
* Stuff left over -- add it to the list too
*/
gn = Targ_FindNode(start, TARG_CREATE);
- (void)Lst_AtEnd(members, gn);
+ Lst_Append(members, gn);
}
/*
* Point cp back at the beginning again so the variable value
@@ -1643,28 +1361,25 @@ SuffExpandChildren(LstNode cln, GNode *pgn)
* Add all elements of the members list to the parent node.
*/
while(!Lst_IsEmpty(members)) {
- gn = (GNode *)Lst_DeQueue(members);
+ gn = Lst_Dequeue(members);
- if (DEBUG(SUFF)) {
- fprintf(debug_file, "%s...", gn->name);
- }
+ SUFF_DEBUG1("%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);
+ Lst_InsertBefore(pgn->children, cln, gn);
+ Lst_Append(gn->parents, pgn);
pgn->unmade++;
/* Expand wildcards on new node */
- SuffExpandWildcards(Lst_Prev(cln), pgn);
+ SuffExpandWildcards(LstNode_Prev(cln), pgn);
}
- Lst_Destroy(members, NULL);
+ Lst_Free(members);
/*
* Free the result
*/
free(cp);
}
- if (DEBUG(SUFF)) {
- fprintf(debug_file, "\n");
- }
+
+ SUFF_DEBUG0("\n");
/*
* Now the source is expanded, remove it from the list of children to
@@ -1672,13 +1387,13 @@ SuffExpandChildren(LstNode cln, GNode *pgn)
*/
pgn->unmade--;
Lst_Remove(pgn->children, cln);
- Lst_Remove(cgn->parents, Lst_Member(cgn->parents, pgn));
+ Lst_Remove(cgn->parents, Lst_FindDatum(cgn->parents, pgn));
}
static void
SuffExpandWildcards(LstNode cln, GNode *pgn)
{
- GNode *cgn = (GNode *)Lst_Datum(cln);
+ GNode *cgn = LstNode_Datum(cln);
GNode *gn; /* New source 8) */
char *cp; /* Expanded value */
Lst explist; /* List of expansions */
@@ -1689,34 +1404,27 @@ SuffExpandWildcards(LstNode cln, GNode *pgn)
/*
* Expand the word along the chosen path
*/
- explist = Lst_Init(FALSE);
+ explist = Lst_Init();
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);
+ cp = Lst_Dequeue(explist);
- if (DEBUG(SUFF)) {
- fprintf(debug_file, "%s...", cp);
- }
+ SUFF_DEBUG1("%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);
+ Lst_InsertBefore(pgn->children, cln, gn);
+ Lst_Append(gn->parents, pgn);
pgn->unmade++;
}
- /*
- * Nuke what's left of the list
- */
- Lst_Destroy(explist, NULL);
+ Lst_Free(explist);
- if (DEBUG(SUFF)) {
- fprintf(debug_file, "\n");
- }
+ SUFF_DEBUG0("\n");
/*
* Now the source is expanded, remove it from the list of children to
@@ -1724,28 +1432,19 @@ SuffExpandWildcards(LstNode cln, GNode *pgn)
*/
pgn->unmade--;
Lst_Remove(pgn->children, cln);
- Lst_Remove(cgn->parents, Lst_Member(cgn->parents, pgn));
+ Lst_Remove(cgn->parents, Lst_FindDatum(cgn->parents, pgn));
}
-/*-
- *-----------------------------------------------------------------------
- * Suff_FindPath --
- * Find a path along which to expand the node.
+/* 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.
+ * 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)
@@ -1753,24 +1452,20 @@ Suff_FindPath(GNode* gn)
Suff *suff = gn->suffix;
if (suff == NULL) {
- SuffixCmpData sd; /* Search string data */
+ SuffSuffGetSuffixArgs sd; /* Search string data */
LstNode ln;
sd.len = strlen(gn->name);
sd.ename = gn->name + sd.len;
- ln = Lst_Find(sufflist, &sd, SuffSuffIsSuffixP);
+ ln = Lst_Find(sufflist, SuffSuffIsSuffix, &sd);
- if (DEBUG(SUFF)) {
- fprintf(debug_file, "Wildcard expanding \"%s\"...", gn->name);
- }
+ SUFF_DEBUG1("Wildcard expanding \"%s\"...", gn->name);
if (ln != NULL)
- suff = (Suff *)Lst_Datum(ln);
+ suff = LstNode_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);
- }
+ SUFF_DEBUG1("suffix is \"%s\"...", suff->name);
return suff->searchPath;
} else {
/*
@@ -1780,11 +1475,8 @@ Suff_FindPath(GNode* gn)
}
}
-/*-
- *-----------------------------------------------------------------------
- * SuffApplyTransform --
- * Apply a transformation rule, given the source and target nodes
- * and suffixes.
+/* Apply a transformation rule, given the source and target nodes and
+ * suffixes.
*
* Input:
* tGn Target node
@@ -1801,8 +1493,6 @@ Suff_FindPath(GNode* gn)
* All attributes but OP_DEPMASK and OP_TRANSFORM are applied
* to the target. The target also inherits all the sources for
* the transformation rule.
- *
- *-----------------------------------------------------------------------
*/
static Boolean
SuffApplyTransform(GNode *tGn, GNode *sGn, Suff *t, Suff *s)
@@ -1814,15 +1504,15 @@ SuffApplyTransform(GNode *tGn, GNode *sGn, Suff *t, Suff *s)
/*
* Form the proper links between the target and source.
*/
- (void)Lst_AtEnd(tGn->children, sGn);
- (void)Lst_AtEnd(sGn->parents, tGn);
+ Lst_Append(tGn->children, sGn);
+ Lst_Append(sGn->parents, tGn);
tGn->unmade += 1;
/*
* Locate the transformation rule itself
*/
- tname = str_concat(s->name, t->name, 0);
- ln = Lst_Find(transforms, tname, SuffGNHasNameP);
+ tname = str_concat2(s->name, t->name);
+ ln = Lst_Find(transforms, SuffGNHasName, tname);
free(tname);
if (ln == NULL) {
@@ -1834,11 +1524,9 @@ SuffApplyTransform(GNode *tGn, GNode *sGn, Suff *t, Suff *s)
return FALSE;
}
- gn = (GNode *)Lst_Datum(ln);
+ gn = LstNode_Datum(ln);
- if (DEBUG(SUFF)) {
- fprintf(debug_file, "\tapplying %s -> %s to \"%s\"\n", s->name, t->name, tGn->name);
- }
+ SUFF_DEBUG3("\tapplying %s -> %s to \"%s\"\n", s->name, t->name, tGn->name);
/*
* Record last child for expansion purposes
@@ -1853,8 +1541,8 @@ SuffApplyTransform(GNode *tGn, GNode *sGn, Suff *t, Suff *s)
/*
* Deal with wildcards and variables in any acquired sources
*/
- for (ln = Lst_Succ(ln); ln != NULL; ln = nln) {
- nln = Lst_Succ(ln);
+ for (ln = ln != NULL ? LstNode_Next(ln) : NULL; ln != NULL; ln = nln) {
+ nln = LstNode_Next(ln);
SuffExpandChildren(ln, tGn);
}
@@ -1862,27 +1550,19 @@ SuffApplyTransform(GNode *tGn, GNode *sGn, Suff *t, Suff *s)
* Keep track of another parent to which this beast is transformed so
* the .IMPSRC variable can be set correctly for the parent.
*/
- (void)Lst_AtEnd(sGn->iParents, tGn);
+ Lst_Append(sGn->implicitParents, tGn);
return TRUE;
}
-/*-
- *-----------------------------------------------------------------------
- * SuffFindArchiveDeps --
- * Locate dependencies for an OP_ARCHV node.
+/* Locate dependencies for an OP_ARCHV node.
*
* Input:
* gn Node for which to locate dependencies
*
- * Results:
- * None
- *
* Side Effects:
* Same as Suff_FindDeps
- *
- *-----------------------------------------------------------------------
*/
static void
SuffFindArchiveDeps(GNode *gn, Lst slst)
@@ -1890,13 +1570,7 @@ SuffFindArchiveDeps(GNode *gn, Lst slst)
char *eoarch; /* End of archive portion */
char *eoname; /* End of member portion */
GNode *mem; /* Node for member */
- static const char *copy[] = {
- /* Variables to be copied from the member node */
- TARGET, /* Must be first */
- PREFIX, /* Must be second */
- };
LstNode ln, nln; /* Next suffix node to check */
- int i; /* Index into copy and vals */
Suff *ms; /* Suffix descriptor for member */
char *name; /* Start of member's name */
@@ -1932,18 +1606,19 @@ SuffFindArchiveDeps(GNode *gn, Lst slst)
/*
* Create the link between the two nodes right off
*/
- (void)Lst_AtEnd(gn->children, mem);
- (void)Lst_AtEnd(mem->parents, gn);
+ Lst_Append(gn->children, mem);
+ Lst_Append(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);
- free(p1);
-
+ {
+ char *freeIt;
+ Var_Set(PREFIX, Var_Value(PREFIX, mem, &freeIt), gn);
+ bmake_free(freeIt);
+ Var_Set(TARGET, Var_Value(TARGET, mem, &freeIt), gn);
+ bmake_free(freeIt);
}
ms = mem->suffix;
@@ -1951,9 +1626,7 @@ SuffFindArchiveDeps(GNode *gn, Lst slst)
/*
* Didn't know what it was -- use .NULL suffix if not in make mode
*/
- if (DEBUG(SUFF)) {
- fprintf(debug_file, "using null suffix\n");
- }
+ SUFF_DEBUG0("using null suffix\n");
ms = suffNull;
}
@@ -1974,7 +1647,7 @@ SuffFindArchiveDeps(GNode *gn, Lst slst)
* that still contain variables or wildcards in their names.
*/
for (ln = Lst_First(gn->children); ln != NULL; ln = nln) {
- nln = Lst_Succ(ln);
+ nln = LstNode_Next(ln);
SuffExpandChildren(ln, gn);
}
@@ -1985,24 +1658,23 @@ SuffFindArchiveDeps(GNode *gn, Lst slst)
* through the entire list, we just look at suffixes to which the
* member's suffix may be transformed...
*/
- SuffixCmpData sd; /* Search string data */
+ SuffSuffGetSuffixArgs sd; /* Search string data */
/*
* Use first matching suffix...
*/
sd.len = eoarch - gn->name;
sd.ename = eoarch;
- ln = Lst_Find(ms->parents, &sd, SuffSuffIsSuffixP);
+ ln = Lst_Find(ms->parents, SuffSuffIsSuffix, &sd);
if (ln != NULL) {
/*
* Got one -- apply it
*/
- if (!SuffApplyTransform(gn, mem, (Suff *)Lst_Datum(ln), ms) &&
- DEBUG(SUFF))
- {
- fprintf(debug_file, "\tNo transformation from %s -> %s\n",
- ms->name, ((Suff *)Lst_Datum(ln))->name);
+ Suff *suff = LstNode_Datum(ln);
+ if (!SuffApplyTransform(gn, mem, suff, ms)) {
+ SUFF_DEBUG2("\tNo transformation from %s -> %s\n",
+ ms->name, suff->name);
}
}
}
@@ -2030,21 +1702,13 @@ SuffFindArchiveDeps(GNode *gn, Lst slst)
mem->type |= OP_MEMBER | OP_JOIN | OP_MADE;
}
-/*-
- *-----------------------------------------------------------------------
- * SuffFindNormalDeps --
- * Locate implicit dependencies for regular targets.
+/* Locate implicit dependencies for regular targets.
*
* Input:
* gn Node for which to find sources
*
- * Results:
- * None.
- *
* Side Effects:
- * Same as Suff_FindDeps...
- *
- *-----------------------------------------------------------------------
+ * Same as Suff_FindDeps
*/
static void
SuffFindNormalDeps(GNode *gn, Lst slst)
@@ -2060,7 +1724,7 @@ SuffFindNormalDeps(GNode *gn, Lst slst)
Src *src; /* General Src pointer */
char *pref; /* Prefix to use */
Src *targ; /* General Src target pointer */
- SuffixCmpData sd; /* Search string data */
+ SuffSuffGetSuffixArgs sd; /* Search string data */
sd.len = strlen(gn->name);
@@ -2072,8 +1736,8 @@ SuffFindNormalDeps(GNode *gn, Lst slst)
* Begin at the beginning...
*/
ln = Lst_First(sufflist);
- srcs = Lst_Init(FALSE);
- targs = Lst_Init(FALSE);
+ srcs = Lst_Init();
+ targs = Lst_Init();
/*
* We're caught in a catch-22 here. On the one hand, we want to use any
@@ -2102,34 +1766,27 @@ SuffFindNormalDeps(GNode *gn, Lst slst)
/*
* Look for next possible suffix...
*/
- ln = Lst_FindFrom(sufflist, ln, &sd, SuffSuffIsSuffixP);
+ ln = Lst_FindFrom(sufflist, ln, SuffSuffIsSuffix, &sd);
if (ln != NULL) {
- int prefLen; /* Length of the prefix */
+ const char *eopref;
/*
* Allocate a Src structure to which things can be transformed
*/
targ = bmake_malloc(sizeof(Src));
targ->file = bmake_strdup(gn->name);
- targ->suff = (Suff *)Lst_Datum(ln);
+ targ->suff = LstNode_Datum(ln);
targ->suff->refCount++;
targ->node = gn;
targ->parent = NULL;
targ->children = 0;
#ifdef DEBUG_SRC
- targ->cp = Lst_Init(FALSE);
+ targ->cp = Lst_Init();
#endif
- /*
- * Allocate room for the prefix, whose end is found by
- * subtracting the length of the suffix from
- * the end of the name.
- */
- prefLen = (eoname - targ->suff->nameLen) - sopref;
- targ->pref = bmake_malloc(prefLen + 1);
- memcpy(targ->pref, sopref, prefLen);
- targ->pref[prefLen] = '\0';
+ eopref = eoname - targ->suff->nameLen;
+ targ->pref = bmake_strsedup(sopref, eopref);
/*
* Add nodes from which the target can be made
@@ -2139,12 +1796,12 @@ SuffFindNormalDeps(GNode *gn, Lst slst)
/*
* Record the target so we can nuke it
*/
- (void)Lst_AtEnd(targs, targ);
+ Lst_Append(targs, targ);
/*
* Search from this suffix's successor...
*/
- ln = Lst_Succ(ln);
+ ln = LstNode_Next(ln);
}
}
@@ -2152,9 +1809,8 @@ SuffFindNormalDeps(GNode *gn, Lst slst)
* Handle target of unknown suffix...
*/
if (Lst_IsEmpty(targs) && suffNull != NULL) {
- if (DEBUG(SUFF)) {
- fprintf(debug_file, "\tNo known suffix on %s. Using .NULL suffix\n", gn->name);
- }
+ SUFF_DEBUG1("\tNo known suffix on %s. Using .NULL suffix\n",
+ gn->name);
targ = bmake_malloc(sizeof(Src));
targ->file = bmake_strdup(gn->name);
@@ -2165,7 +1821,7 @@ SuffFindNormalDeps(GNode *gn, Lst slst)
targ->children = 0;
targ->pref = bmake_strdup(sopref);
#ifdef DEBUG_SRC
- targ->cp = Lst_Init(FALSE);
+ targ->cp = Lst_Init();
#endif
/*
@@ -2177,14 +1833,12 @@ SuffFindNormalDeps(GNode *gn, Lst slst)
if (Lst_IsEmpty(gn->commands))
SuffAddLevel(srcs, targ);
else {
- if (DEBUG(SUFF))
- fprintf(debug_file, "not ");
+ SUFF_DEBUG0("not ");
}
- if (DEBUG(SUFF))
- fprintf(debug_file, "adding suffix rules\n");
+ SUFF_DEBUG0("adding suffix rules\n");
- (void)Lst_AtEnd(targs, targ);
+ Lst_Append(targs, targ);
}
/*
@@ -2199,7 +1853,7 @@ SuffFindNormalDeps(GNode *gn, Lst slst)
* for setting the local variables.
*/
if (!Lst_IsEmpty(targs)) {
- targ = (Src *)Lst_Datum(Lst_First(targs));
+ targ = LstNode_Datum(Lst_First(targs));
} else {
targ = NULL;
}
@@ -2223,14 +1877,12 @@ SuffFindNormalDeps(GNode *gn, Lst slst)
* that still contain variables or wildcards in their names.
*/
for (ln = Lst_First(gn->children); ln != NULL; ln = nln) {
- nln = Lst_Succ(ln);
+ nln = LstNode_Next(ln);
SuffExpandChildren(ln, gn);
}
if (targ == NULL) {
- if (DEBUG(SUFF)) {
- fprintf(debug_file, "\tNo valid suffix on %s\n", gn->name);
- }
+ SUFF_DEBUG1("\tNo valid suffix on %s\n", gn->name);
sfnd_abort:
/*
@@ -2314,8 +1966,8 @@ sfnd_abort:
* up to, but not including, the parent node.
*/
while (bottom && bottom->parent != NULL) {
- if (Lst_Member(slst, bottom) == NULL) {
- Lst_AtEnd(slst, bottom);
+ if (Lst_FindDatum(slst, bottom) == NULL) {
+ Lst_Append(slst, bottom);
}
bottom = bottom->parent;
}
@@ -2390,45 +2042,31 @@ sfnd_abort:
*/
sfnd_return:
if (bottom)
- if (Lst_Member(slst, bottom) == NULL)
- Lst_AtEnd(slst, bottom);
+ if (Lst_FindDatum(slst, bottom) == NULL)
+ Lst_Append(slst, bottom);
while (SuffRemoveSrc(srcs) || SuffRemoveSrc(targs))
continue;
- Lst_Concat(slst, srcs, LST_CONCLINK);
- Lst_Concat(slst, targs, LST_CONCLINK);
+ Lst_MoveAll(slst, srcs);
+ Lst_MoveAll(slst, targs);
}
-/*-
- *-----------------------------------------------------------------------
- * Suff_FindDeps --
- * Find implicit sources for the target described by the graph node
- * gn
- *
- * Results:
- * Nothing.
+/* Find implicit sources for the target described by the graph node.
*
- * Side Effects:
- * Nodes are added to the graph below the passed-in node. The nodes
- * are marked to have their IMPSRC variable filled in. The
- * PREFIX variable is set for the given node and all its
- * implied children.
- *
- * Notes:
- * The path found by this target is the shortest path in the
- * transformation graph, which may pass through non-existent targets,
- * to an existing target. The search continues on all paths from the
- * root suffix until a file is found. I.e. if there's a path
- * .o -> .c -> .l -> .l,v from the root and the .l,v file exists but
- * the .c and .l files don't, the search will branch out in
- * all directions from .o and again from all the nodes on the
- * next level until the .l,v node is encountered.
+ * Nodes are added to the graph below the passed-in node. The nodes are
+ * marked to have their IMPSRC variable filled in. The PREFIX variable is set
+ * for the given node and all its implied children.
*
- *-----------------------------------------------------------------------
+ * The path found by this target is the shortest path in the transformation
+ * graph, which may pass through non-existent targets, to an existing target.
+ * The search continues on all paths from the root suffix until a file is
+ * found. I.e. if there's a path .o -> .c -> .l -> .l,v from the root and the
+ * .l,v file exists but the .c and .l files don't, the search will branch out
+ * in all directions from .o and again from all the nodes on the next level
+ * until the .l,v node is encountered.
*/
-
void
Suff_FindDeps(GNode *gn)
{
@@ -2438,32 +2076,20 @@ Suff_FindDeps(GNode *gn)
continue;
}
-
-/*
- * Input:
- * gn node we're dealing with
- *
- */
static void
SuffFindDeps(GNode *gn, Lst slst)
{
- if (gn->type & OP_DEPS_FOUND) {
- /*
- * If dependencies already found, no need to do it again...
- */
+ if (gn->type & OP_DEPS_FOUND)
return;
- } else {
- gn->type |= OP_DEPS_FOUND;
- }
+ gn->type |= OP_DEPS_FOUND;
+
/*
* Make sure we have these set, may get revised below.
*/
Var_Set(TARGET, gn->path ? gn->path : gn->name, gn);
Var_Set(PREFIX, gn->name, gn);
- if (DEBUG(SUFF)) {
- fprintf(debug_file, "SuffFindDeps (%s)\n", gn->name);
- }
+ SUFF_DEBUG1("SuffFindDeps (%s)\n", gn->name);
if (gn->type & OP_ARCHV) {
SuffFindArchiveDeps(gn, slst);
@@ -2479,11 +2105,11 @@ SuffFindDeps(GNode *gn, Lst slst)
LstNode ln;
Suff *s;
- ln = Lst_Find(sufflist, LIBSUFF, SuffSuffHasNameP);
+ ln = Lst_Find(sufflist, SuffSuffHasName, LIBSUFF);
if (gn->suffix)
gn->suffix->refCount--;
if (ln != NULL) {
- gn->suffix = s = (Suff *)Lst_Datum(ln);
+ gn->suffix = s = LstNode_Datum(ln);
gn->suffix->refCount++;
Arch_FindLib(gn, s->searchPath);
} else {
@@ -2501,25 +2127,13 @@ SuffFindDeps(GNode *gn, Lst slst)
}
}
-/*-
- *-----------------------------------------------------------------------
- * Suff_SetNull --
- * Define which suffix is the null suffix.
+/* Define which suffix is the null suffix.
+ *
+ * Need to handle the changing of the null suffix gracefully so the old
+ * transformation rules don't just go away.
*
* Input:
* name Name of null suffix
- *
- * Results:
- * None.
- *
- * Side Effects:
- * 'suffNull' is altered.
- *
- * Notes:
- * Need to handle the changing of the null suffix gracefully so the
- * old transformation rules don't just go away.
- *
- *-----------------------------------------------------------------------
*/
void
Suff_SetNull(char *name)
@@ -2527,9 +2141,9 @@ Suff_SetNull(char *name)
Suff *s;
LstNode ln;
- ln = Lst_Find(sufflist, name, SuffSuffHasNameP);
+ ln = Lst_Find(sufflist, SuffSuffHasName, name);
if (ln != NULL) {
- s = (Suff *)Lst_Datum(ln);
+ s = LstNode_Datum(ln);
if (suffNull != NULL) {
suffNull->flags &= ~SUFF_NULL;
}
@@ -2544,26 +2158,16 @@ Suff_SetNull(char *name)
}
}
-/*-
- *-----------------------------------------------------------------------
- * Suff_Init --
- * Initialize suffixes module
- *
- * Results:
- * None
- *
- * Side Effects:
- * Many
- *-----------------------------------------------------------------------
- */
+/* Initialize the suffixes module. */
void
Suff_Init(void)
{
#ifdef CLEANUP
- suffClean = Lst_Init(FALSE);
+ suffClean = Lst_Init();
+ sufflist = Lst_Init();
#endif
- srclist = Lst_Init(FALSE);
- transforms = Lst_Init(FALSE);
+ srclist = Lst_Init();
+ transforms = Lst_Init();
/*
* Create null suffix for single-suffix rules (POSIX). The thing doesn't
@@ -2574,19 +2178,7 @@ Suff_Init(void)
}
-/*-
- *----------------------------------------------------------------------
- * Suff_End --
- * Cleanup the this module
- *
- * Results:
- * None
- *
- * Side Effects:
- * The memory is free'd.
- *----------------------------------------------------------------------
- */
-
+/* Clean up the suffixes module. */
void
Suff_End(void)
{
@@ -2595,8 +2187,8 @@ Suff_End(void)
Lst_Destroy(suffClean, SuffFree);
if (suffNull)
SuffFree(suffNull);
- Lst_Destroy(srclist, NULL);
- Lst_Destroy(transforms, NULL);
+ Lst_Free(srclist);
+ Lst_Free(transforms);
#endif
}
@@ -2614,30 +2206,15 @@ static int
SuffPrintSuff(void *sp, void *dummy MAKE_ATTR_UNUSED)
{
Suff *s = (Suff *)sp;
- int flags;
- int flag;
fprintf(debug_file, "# `%s' [%d] ", s->name, s->refCount);
- flags = s->flags;
- if (flags) {
- fputs(" (", debug_file);
- while (flags) {
- flag = 1 << (ffs(flags) - 1);
- flags &= ~flag;
- switch (flag) {
- case SUFF_NULL:
- fprintf(debug_file, "NULL");
- break;
- case SUFF_INCLUDE:
- fprintf(debug_file, "INCLUDE");
- break;
- case SUFF_LIBRARY:
- fprintf(debug_file, "LIBRARY");
- break;
- }
- fputc(flags ? '|' : ')', debug_file);
- }
+ if (s->flags != 0) {
+ char flags_buf[SuffFlags_ToStringSize];
+
+ fprintf(debug_file, " (%s)",
+ Enum_FlagsToString(flags_buf, sizeof flags_buf,
+ s->flags, SuffFlags_ToStringSpecs));
}
fputc('\n', debug_file);
fprintf(debug_file, "#\tTo: ");
diff --git a/contrib/bmake/targ.c b/contrib/bmake/targ.c
index 15dc01f0e08d..c98abbed5705 100644
--- a/contrib/bmake/targ.c
+++ b/contrib/bmake/targ.c
@@ -1,4 +1,4 @@
-/* $NetBSD: targ.c,v 1.63 2020/07/03 08:02:55 rillig Exp $ */
+/* $NetBSD: targ.c,v 1.81 2020/09/01 20:54:00 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -69,14 +69,14 @@
*/
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: targ.c,v 1.63 2020/07/03 08:02:55 rillig Exp $";
+static char rcsid[] = "$NetBSD: targ.c,v 1.81 2020/09/01 20:54:00 rillig Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)targ.c 8.2 (Berkeley) 3/19/94";
#else
-__RCSID("$NetBSD: targ.c,v 1.63 2020/07/03 08:02:55 rillig Exp $");
+__RCSID("$NetBSD: targ.c,v 1.81 2020/09/01 20:54:00 rillig Exp $");
#endif
#endif /* not lint */
#endif
@@ -84,8 +84,7 @@ __RCSID("$NetBSD: targ.c,v 1.63 2020/07/03 08:02:55 rillig Exp $");
/*-
* 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.
+ * kept in two structures: a Lst and a hash table.
*
* Interface:
* Targ_Init Initialization procedure.
@@ -133,7 +132,6 @@ __RCSID("$NetBSD: targ.c,v 1.63 2020/07/03 08:02:55 rillig Exp $");
#include <time.h>
#include "make.h"
-#include "hash.h"
#include "dir.h"
static Lst allTargets; /* the list of all targets found so far */
@@ -142,91 +140,49 @@ 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);
+ allTargets = Lst_Init();
+ Hash_InitTable(&targets, 191);
}
-/*-
- *-----------------------------------------------------------------------
- * Targ_End --
- * Finalize this module
- *
- * Results:
- * None
- *
- * Side Effects:
- * All lists and gnodes are cleared
- *-----------------------------------------------------------------------
- */
void
Targ_End(void)
{
+ Targ_Stats();
#ifdef CLEANUP
- Lst_Destroy(allTargets, NULL);
- if (allGNs)
+ Lst_Free(allTargets);
+ if (allGNs != NULL)
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
- *-----------------------------------------------------------------------
- */
+void
+Targ_Stats(void)
+{
+ Hash_DebugStats(&targets, "targets");
+}
+
+/* Return the list of all targets. */
Lst
Targ_List(void)
{
return allTargets;
}
-/*-
- *-----------------------------------------------------------------------
- * Targ_NewGN --
- * Create and initialize a new graph node
+/* Create and initialize a new graph node. The gnode is added to the list of
+ * all gnodes.
*
* 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.
- *-----------------------------------------------------------------------
+ * name the name of the node, such as "clean", "src.c"
*/
GNode *
Targ_NewGN(const char *name)
@@ -237,11 +193,7 @@ Targ_NewGN(const char *name)
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->type = name[0] == '-' && name[1] == 'l' ? OP_LIB : 0;
gn->unmade = 0;
gn->unmade_cohorts = 0;
gn->cohort_num[0] = 0;
@@ -251,82 +203,65 @@ Targ_NewGN(const char *name)
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);
+ gn->implicitParents = Lst_Init();
+ gn->cohorts = Lst_Init();
+ gn->parents = Lst_Init();
+ gn->children = Lst_Init();
+ gn->order_pred = Lst_Init();
+ gn->order_succ = Lst_Init();
Hash_InitTable(&gn->context, 0);
- gn->commands = Lst_Init(FALSE);
+ gn->commands = Lst_Init();
gn->suffix = NULL;
- gn->lineno = 0;
gn->fname = NULL;
+ gn->lineno = 0;
#ifdef CLEANUP
if (allGNs == NULL)
- allGNs = Lst_Init(FALSE);
- Lst_AtEnd(allGNs, gn);
+ allGNs = Lst_Init();
+ Lst_Append(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);
free(gn->uname);
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);
+ Lst_Free(gn->implicitParents);
+ Lst_Free(gn->cohorts);
+ Lst_Free(gn->parents);
+ Lst_Free(gn->children);
+ Lst_Free(gn->order_succ);
+ Lst_Free(gn->order_pred);
Hash_DeleteTable(&gn->context);
- Lst_Destroy(gn->commands, NULL);
+ Lst_Free(gn->commands);
+
+ /* XXX: does gn->suffix need to be freed? It is reference-counted. */
+ /* gn->fname points to name allocated when file was opened, don't free */
+
free(gn);
}
#endif
-/*-
- *-----------------------------------------------------------------------
- * Targ_FindNode --
- * Find a node in the list using the given name for matching
+/* Find a node in the list using the given name for matching.
+ * If the node is created, it is added to the .ALLTARGETS list.
*
* Input:
* name the name to find
- * flags flags governing events when target not
- * found
+ * flags flags governing events when target not found
*
* Results:
- * The node in the list if it was. If it wasn't, return NULL of
+ * The node in the list if it was. If it wasn't, return NULL if
* 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)
@@ -353,16 +288,16 @@ Targ_FindNode(const char *name, int flags)
if (!(flags & TARG_NOHASH))
Hash_SetValue(he, gn);
Var_Append(".ALLTARGETS", name, VAR_GLOBAL);
- (void)Lst_AtEnd(allTargets, gn);
+ Lst_Append(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
+/* Make a complete list of GNodes from the given list of names.
+ * 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.
*
* Input:
* name list of names to find
@@ -371,12 +306,6 @@ Targ_FindNode(const char *name, int flags)
* 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)
@@ -386,21 +315,19 @@ Targ_FindList(Lst names, int flags)
GNode *gn; /* node in tLn */
char *name;
- nodes = Lst_Init(FALSE);
+ nodes = Lst_Init();
- if (Lst_Open(names) == FAILURE) {
- return nodes;
- }
+ Lst_Open(names);
while ((ln = Lst_Next(names)) != NULL) {
- name = (char *)Lst_Datum(ln);
+ name = LstNode_Datum(ln);
gn = Targ_FindNode(name, flags);
if (gn != NULL) {
/*
- * Note: Lst_AtEnd must come before the Lst_Concat so the nodes
+ * Note: Lst_Append 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);
+ Lst_Append(nodes, gn);
} else if (flags == TARG_NOCREATE) {
Error("\"%s\" -- target unknown.", name);
}
@@ -409,100 +336,33 @@ Targ_FindList(Lst names, int flags)
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
- *-----------------------------------------------------------------------
- */
+/* Return true if should ignore errors when creating gn. */
Boolean
Targ_Ignore(GNode *gn)
{
- if (ignoreErrors || gn->type & OP_IGNORE) {
- return TRUE;
- } else {
- return FALSE;
- }
+ return ignoreErrors || gn->type & OP_IGNORE;
}
-/*-
- *-----------------------------------------------------------------------
- * 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
- *-----------------------------------------------------------------------
- */
+/* Return true if be silent when creating gn. */
Boolean
Targ_Silent(GNode *gn)
{
- if (beSilent || gn->type & OP_SILENT) {
- return TRUE;
- } else {
- return FALSE;
- }
+ return beSilent || gn->type & OP_SILENT;
}
-/*-
- *-----------------------------------------------------------------------
- * 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
- *-----------------------------------------------------------------------
- */
+/* See if the given target is precious. */
Boolean
Targ_Precious(GNode *gn)
{
- if (allPrecious || (gn->type & (OP_PRECIOUS|OP_DOUBLEDEP))) {
- return TRUE;
- } else {
- return FALSE;
- }
+ return allPrecious || gn->type & (OP_PRECIOUS | OP_DOUBLEDEP);
}
/******************* 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.
- *-----------------------------------------------------------------------
- */
+
+/* Set our idea of the main target we'll be creating. Used for debugging
+ * output. */
void
Targ_SetMain(GNode *gn)
{
@@ -527,20 +387,8 @@ Targ_PrintCmd(void *cmd, void *dummy MAKE_ATTR_UNUSED)
return 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.
- *
- *-----------------------------------------------------------------------
- */
+/* Format a modification time in some reasonable way and return it.
+ * The time is placed in a static area, so it is overwritten with each call. */
char *
Targ_FmtTime(time_t tm)
{
@@ -552,18 +400,7 @@ Targ_FmtTime(time_t tm)
return buf;
}
-/*-
- *-----------------------------------------------------------------------
- * Targ_PrintType --
- * Print out a type field giving only those attributes the user can
- * set.
- *
- * Results:
- *
- * Side Effects:
- *
- *-----------------------------------------------------------------------
- */
+/* Print out a type field giving only those attributes the user can set. */
void
Targ_PrintType(int type)
{
@@ -600,7 +437,7 @@ Targ_PrintType(int type)
}
static const char *
-made_name(enum enum_made made)
+made_name(GNodeMade made)
{
switch (made) {
case UNMADE: return "unmade";
@@ -615,20 +452,15 @@ made_name(enum enum_made made)
}
}
-/*-
- *-----------------------------------------------------------------------
- * TargPrintNode --
- * print the contents of a node
- *-----------------------------------------------------------------------
- */
+/* 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);
+ fprintf(debug_file, "# %s%s", gn->name, gn->cohort_num);
+ GNode_FprintDetails(debug_file, ", ", gn, "\n");
if (gn->flags == 0)
return 0;
@@ -655,26 +487,26 @@ Targ_PrintNode(void *gnp, void *passp)
fprintf(debug_file, "# unmade\n");
}
}
- if (!Lst_IsEmpty (gn->iParents)) {
+ if (!Lst_IsEmpty(gn->implicitParents)) {
fprintf(debug_file, "# implicit parents: ");
- Lst_ForEach(gn->iParents, TargPrintName, NULL);
+ Lst_ForEach(gn->implicitParents, TargPrintName, NULL);
fprintf(debug_file, "\n");
}
} else {
if (gn->unmade)
fprintf(debug_file, "# %d unmade children\n", gn->unmade);
}
- if (!Lst_IsEmpty (gn->parents)) {
+ 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)) {
+ 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)) {
+ if (!Lst_IsEmpty(gn->order_succ)) {
fprintf(debug_file, "# order_succ: ");
Lst_ForEach(gn->order_succ, TargPrintName, NULL);
fprintf(debug_file, "\n");
@@ -701,19 +533,8 @@ Targ_PrintNode(void *gnp, void *passp)
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
- *
- *-----------------------------------------------------------------------
- */
+/* Print only those targets that are just a source.
+ * The name of each file is printed, preceded by #\t. */
static int
TargPrintOnlySrc(void *gnp, void *dummy MAKE_ATTR_UNUSED)
{
@@ -729,21 +550,10 @@ TargPrintOnlySrc(void *gnp, void *dummy MAKE_ATTR_UNUSED)
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
- *-----------------------------------------------------------------------
+/* Input:
+ * pass 1 => before processing
+ * 2 => after processing
+ * 3 => after processing, an error occurred
*/
void
Targ_PrintGraph(int pass)
@@ -763,84 +573,26 @@ Targ_PrintGraph(int pass)
Suff_PrintAll();
}
-/*-
- *-----------------------------------------------------------------------
- * TargPropagateNode --
- * Propagate information from a single node to related nodes if
- * appropriate.
+/* Propagate some type information to cohort nodes (those from the ::
+ * dependency operator).
*
- * Input:
- * gnp The node that we are processing.
- *
- * Results:
- * Always returns 0, for the benefit of Lst_ForEach().
- *
- * Side Effects:
- * Information is propagated from this node to cohort or child
- * nodes.
- *
- * If the node was defined with "::", then TargPropagateCohort()
- * will be called for each cohort node.
- *
- * If the node has recursive predecessors, then
- * TargPropagateRecpred() will be called for each recursive
- * predecessor.
- *-----------------------------------------------------------------------
- */
-static int
-TargPropagateNode(void *gnp, void *junk MAKE_ATTR_UNUSED)
+ * Should be called after the makefiles are parsed but before any action is
+ * taken. */
+void
+Targ_Propagate(void)
{
- GNode *gn = (GNode *)gnp;
+ LstNode pn, cn;
- if (gn->type & OP_DOUBLEDEP)
- Lst_ForEach(gn->cohorts, TargPropagateCohort, gnp);
- return 0;
-}
+ for (pn = Lst_First(allTargets); pn != NULL; pn = LstNode_Next(pn)) {
+ GNode *pgn = LstNode_Datum(pn);
-/*-
- *-----------------------------------------------------------------------
- * 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;
+ if (!(pgn->type & OP_DOUBLEDEP))
+ continue;
- cgn->type |= pgn->type & ~OP_OPMASK;
- return 0;
-}
+ for (cn = Lst_First(pgn->cohorts); cn != NULL; cn = LstNode_Next(cn)) {
+ GNode *cgn = LstNode_Datum(cn);
-/*-
- *-----------------------------------------------------------------------
- * 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);
+ cgn->type |= pgn->type & ~OP_OPMASK;
+ }
+ }
}
diff --git a/contrib/bmake/trace.c b/contrib/bmake/trace.c
index 3ef210f02a8c..0611318dbf07 100644
--- a/contrib/bmake/trace.c
+++ b/contrib/bmake/trace.c
@@ -1,4 +1,4 @@
-/* $NetBSD: trace.c,v 1.12 2020/07/03 08:13:23 rillig Exp $ */
+/* $NetBSD: trace.c,v 1.15 2020/08/03 20:26:09 rillig Exp $ */
/*-
* Copyright (c) 2000 The NetBSD Foundation, Inc.
@@ -31,11 +31,11 @@
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: trace.c,v 1.12 2020/07/03 08:13:23 rillig Exp $";
+static char rcsid[] = "$NetBSD: trace.c,v 1.15 2020/08/03 20:26:09 rillig Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
-__RCSID("$NetBSD: trace.c,v 1.12 2020/07/03 08:13:23 rillig Exp $");
+__RCSID("$NetBSD: trace.c,v 1.15 2020/08/03 20:26:09 rillig Exp $");
#endif /* not lint */
#endif
@@ -63,7 +63,7 @@ __RCSID("$NetBSD: trace.c,v 1.12 2020/07/03 08:13:23 rillig Exp $");
static FILE *trfile;
static pid_t trpid;
-char *trwd;
+const char *trwd;
static const char *evname[] = {
"BEG",
@@ -77,10 +77,10 @@ static const char *evname[] = {
void
Trace_Init(const char *pathname)
{
- char *p1;
if (pathname != NULL) {
+ char *dontFreeIt;
trpid = getpid();
- trwd = Var_Value(".CURDIR", VAR_GLOBAL, &p1);
+ trwd = Var_Value(".CURDIR", VAR_GLOBAL, &dontFreeIt);
trfile = fopen(pathname, "a");
}
diff --git a/contrib/bmake/unit-tests/Makefile b/contrib/bmake/unit-tests/Makefile
index c9cc13d769db..1566b177087a 100644
--- a/contrib/bmake/unit-tests/Makefile
+++ b/contrib/bmake/unit-tests/Makefile
@@ -1,6 +1,6 @@
-# $Id: Makefile,v 1.60 2020/07/10 00:48:32 sjg Exp $
+# $Id: Makefile,v 1.92 2020/09/02 18:39:29 sjg Exp $
#
-# $NetBSD: Makefile,v 1.63 2020/07/09 22:40:14 sjg Exp $
+# $NetBSD: Makefile,v 1.130 2020/09/02 05:33:57 rillig Exp $
#
# Unit tests for make(1)
#
@@ -25,68 +25,364 @@
# named makefile (*.mk), with its own set of expected results (*.exp),
# and it should be added to the TESTS list.
#
-# Any added files must also be added to src/distrib/sets/lists/tests/mi.
-# Makefiles that are not added to TESTS must be ignored in
-# src/tests/usr.bin/make/t_make.sh (example: include-sub).
+# A few *.mk files are helper files for other tests (such as include-sub.mk)
+# and are thus not added to TESTS. Such files must be ignored in
+# src/tests/usr.bin/make/t_make.sh.
#
# Each test is in a sub-makefile.
# Keep the list sorted.
+# Any test that is commented out must be ignored in
+# src/tests/usr.bin/make/t_make.sh as well.
+TESTS+= # archive # broken on FreeBSD, enabled in t_make.sh
+TESTS+= archive-suffix
+TESTS+= cmd-interrupt
+TESTS+= cmdline
TESTS+= comment
+TESTS+= cond-cmp-numeric
+TESTS+= cond-cmp-numeric-eq
+TESTS+= cond-cmp-numeric-ge
+TESTS+= cond-cmp-numeric-gt
+TESTS+= cond-cmp-numeric-le
+TESTS+= cond-cmp-numeric-lt
+TESTS+= cond-cmp-numeric-ne
+TESTS+= cond-cmp-string
+TESTS+= cond-func
+TESTS+= cond-func-commands
+TESTS+= cond-func-defined
+TESTS+= cond-func-empty
+TESTS+= cond-func-exists
+TESTS+= cond-func-make
+TESTS+= cond-func-target
TESTS+= cond-late
+TESTS+= cond-op
+TESTS+= cond-op-and
+TESTS+= cond-op-not
+TESTS+= cond-op-or
+TESTS+= cond-op-parentheses
TESTS+= cond-short
+TESTS+= cond-token-number
+TESTS+= cond-token-plain
+TESTS+= cond-token-string
+TESTS+= cond-token-var
TESTS+= cond1
TESTS+= cond2
+TESTS+= counter
+TESTS+= dep
+TESTS+= dep-colon
+TESTS+= dep-double-colon
+TESTS+= dep-exclam
+TESTS+= dep-none
+TESTS+= dep-var
+TESTS+= dep-wildcards
+TESTS+= depsrc
+TESTS+= depsrc-exec
+TESTS+= depsrc-ignore
+TESTS+= depsrc-made
+TESTS+= depsrc-make
+TESTS+= depsrc-meta
+TESTS+= depsrc-nometa
+TESTS+= depsrc-nometa_cmp
+TESTS+= depsrc-nopath
+TESTS+= depsrc-notmain
+TESTS+= depsrc-optional
+TESTS+= depsrc-phony
+TESTS+= depsrc-precious
+TESTS+= depsrc-recursive
+TESTS+= depsrc-silent
+TESTS+= depsrc-use
+TESTS+= depsrc-usebefore
+TESTS+= depsrc-usebefore-double-colon
+TESTS+= depsrc-wait
+TESTS+= deptgt
+TESTS+= deptgt-begin
+TESTS+= deptgt-default
+TESTS+= deptgt-delete_on_error
+TESTS+= deptgt-end
+TESTS+= deptgt-error
+TESTS+= deptgt-ignore
+TESTS+= deptgt-interrupt
+TESTS+= deptgt-main
+TESTS+= deptgt-makeflags
+TESTS+= deptgt-no_parallel
+TESTS+= deptgt-nopath
+TESTS+= deptgt-notparallel
+TESTS+= deptgt-objdir
+TESTS+= deptgt-order
+TESTS+= deptgt-path
+TESTS+= deptgt-path-suffix
+TESTS+= deptgt-phony
+TESTS+= deptgt-precious
+TESTS+= deptgt-shell
+TESTS+= deptgt-silent
+TESTS+= deptgt-stale
+TESTS+= deptgt-suffixes
+TESTS+= dir
+TESTS+= dir-expand-path
+TESTS+= directive
+TESTS+= directive-elif
+TESTS+= directive-elifdef
+TESTS+= directive-elifmake
+TESTS+= directive-elifndef
+TESTS+= directive-elifnmake
+TESTS+= directive-else
+TESTS+= directive-endif
+TESTS+= directive-error
+TESTS+= directive-export
+TESTS+= directive-export-env
+TESTS+= directive-export-literal
+TESTS+= directive-for
+TESTS+= directive-for-generating-endif
+TESTS+= directive-if
+TESTS+= directive-ifdef
+TESTS+= directive-ifmake
+TESTS+= directive-ifndef
+TESTS+= directive-ifnmake
+TESTS+= directive-info
+TESTS+= directive-undef
+TESTS+= directive-unexport
+TESTS+= directive-unexport-env
+TESTS+= directive-warning
+TESTS+= directives
TESTS+= dollar
TESTS+= doterror
TESTS+= dotwait
+TESTS+= envfirst
TESTS+= error
TESTS+= # escape # broken by reverting POSIX changes
TESTS+= export
TESTS+= export-all
TESTS+= export-env
+TESTS+= export-variants
TESTS+= forloop
TESTS+= forsubst
TESTS+= hash
-TESTS+= # impsrc # broken by reverting POSIX changes
+TESTS+= impsrc
TESTS+= include-main
+TESTS+= lint
+TESTS+= make-exported
TESTS+= misc
TESTS+= moderrs
TESTS+= modmatch
TESTS+= modmisc
-TESTS+= modorder
TESTS+= modts
TESTS+= modword
+TESTS+= opt
+TESTS+= opt-backwards
+TESTS+= opt-chdir
+TESTS+= opt-debug
+TESTS+= opt-debug-g1
+TESTS+= opt-define
+TESTS+= opt-env
+TESTS+= opt-file
+TESTS+= opt-ignore
+TESTS+= opt-include-dir
+TESTS+= opt-jobs
+TESTS+= opt-jobs-internal
+TESTS+= opt-keep-going
+TESTS+= opt-m-include-dir
+TESTS+= opt-no-action
+TESTS+= opt-no-action-at-all
+TESTS+= opt-query
+TESTS+= opt-raw
+TESTS+= opt-silent
+TESTS+= opt-touch
+TESTS+= opt-tracefile
+TESTS+= opt-var-expanded
+TESTS+= opt-var-literal
+TESTS+= opt-warnings-as-errors
+TESTS+= opt-where-am-i
+TESTS+= opt-x-reduce-exported
TESTS+= order
-TESTS+= # phony-end # broken by reverting POSIX changes
+TESTS+= phony-end
TESTS+= posix
TESTS+= # posix1 # broken by reverting POSIX changes
TESTS+= qequals
-TESTS+= # suffixes # broken by reverting POSIX changes
+TESTS+= recursive
+TESTS+= sh
+TESTS+= sh-dots
+TESTS+= sh-jobs
+TESTS+= sh-jobs-error
+TESTS+= sh-leading-at
+TESTS+= sh-leading-hyphen
+TESTS+= sh-leading-plus
+TESTS+= sh-meta-chars
+TESTS+= sh-multi-line
+TESTS+= sh-single-line
+TESTS+= # suffixes # runs into an endless loop (try -dA)
TESTS+= sunshcmd
TESTS+= sysv
TESTS+= ternary
TESTS+= unexport
TESTS+= unexport-env
+TESTS+= use-inference
+TESTS+= var-class
+TESTS+= var-class-cmdline
+TESTS+= var-class-env
+TESTS+= var-class-global
+TESTS+= var-class-local
+TESTS+= var-class-local-legacy
+TESTS+= var-op
+TESTS+= var-op-append
+TESTS+= var-op-assign
+TESTS+= var-op-default
+TESTS+= var-op-expand
+TESTS+= var-op-shell
TESTS+= varcmd
+TESTS+= vardebug
+TESTS+= varfind
TESTS+= varmisc
+TESTS+= varmod
+TESTS+= varmod-assign
+TESTS+= varmod-defined
TESTS+= varmod-edge
+TESTS+= varmod-exclam-shell
+TESTS+= varmod-extension
+TESTS+= varmod-gmtime
+TESTS+= varmod-hash
+TESTS+= varmod-head
+TESTS+= varmod-ifelse
+TESTS+= varmod-l-name-to-value
+TESTS+= varmod-localtime
+TESTS+= varmod-loop
+TESTS+= varmod-match
+TESTS+= varmod-match-escape
+TESTS+= varmod-no-match
+TESTS+= varmod-order
+TESTS+= varmod-order-reverse
+TESTS+= varmod-order-shuffle
+TESTS+= varmod-path
+TESTS+= varmod-quote
+TESTS+= varmod-quote-dollar
+TESTS+= varmod-range
+TESTS+= varmod-remember
+TESTS+= varmod-root
+TESTS+= varmod-select-words
+TESTS+= varmod-shell
+TESTS+= varmod-subst
+TESTS+= varmod-subst-regex
+TESTS+= varmod-sysv
+TESTS+= varmod-tail
+TESTS+= varmod-to-abs
+TESTS+= varmod-to-lower
+TESTS+= varmod-to-many-words
+TESTS+= varmod-to-one-word
+TESTS+= varmod-to-separator
+TESTS+= varmod-to-upper
+TESTS+= varmod-undefined
+TESTS+= varmod-unique
+TESTS+= varname
+TESTS+= varname-dollar
+TESTS+= varname-dot-alltargets
+TESTS+= varname-dot-curdir
+TESTS+= varname-dot-includes
+TESTS+= varname-dot-includedfromdir
+TESTS+= varname-dot-includedfromfile
+TESTS+= varname-dot-libs
+TESTS+= varname-dot-make-dependfile
+TESTS+= varname-dot-make-expand_variables
+TESTS+= varname-dot-make-exported
+TESTS+= varname-dot-make-jobs
+TESTS+= varname-dot-make-jobs-prefix
+TESTS+= varname-dot-make-level
+TESTS+= varname-dot-make-makefile_preference
+TESTS+= varname-dot-make-makefiles
+TESTS+= varname-dot-make-meta-bailiwick
+TESTS+= varname-dot-make-meta-created
+TESTS+= varname-dot-make-meta-files
+TESTS+= varname-dot-make-meta-ignore_filter
+TESTS+= varname-dot-make-meta-ignore_paths
+TESTS+= varname-dot-make-meta-ignore_patterns
+TESTS+= varname-dot-make-meta-prefix
+TESTS+= varname-dot-make-mode
+TESTS+= varname-dot-make-path_filemon
+TESTS+= varname-dot-make-pid
+TESTS+= varname-dot-make-ppid
+TESTS+= varname-dot-make-save_dollars
+TESTS+= varname-dot-makeoverrides
+TESTS+= varname-dot-newline
+TESTS+= varname-dot-objdir
+TESTS+= varname-dot-parsedir
+TESTS+= varname-dot-parsefile
+TESTS+= varname-dot-path
+TESTS+= varname-dot-shell
+TESTS+= varname-dot-targets
+TESTS+= varname-empty
+TESTS+= varname-make
+TESTS+= varname-make_print_var_on_error
+TESTS+= varname-makeflags
+TESTS+= varname-pwd
+TESTS+= varname-vpath
+TESTS+= varparse-dynamic
TESTS+= varquote
TESTS+= varshell
-# Override make flags for certain tests; default is -k.
+# Additional environment variables for some of the tests.
+# The base environment is -i PATH="$PATH".
+ENV.envfirst= FROM_ENV=value-from-env
+ENV.varmisc= FROM_ENV=env
+ENV.varmisc+= FROM_ENV_BEFORE=env
+ENV.varmisc+= FROM_ENV_AFTER=env
+
+# Override make flags for some of the tests; default is -k.
+# If possible, write ".MAKEFLAGS: -dv" in the test .mk file instead of
+# settings FLAGS.test=-dv here, since that is closer to the test code.
+FLAGS.archive= -dA
+FLAGS.counter= -dv
+FLAGS.directive-ifmake= first second
FLAGS.doterror= # none
+FLAGS.envfirst= -e
+FLAGS.export= # none
+FLAGS.lint= -dL -k
+FLAGS.opt-debug-g1= -dg1
+FLAGS.opt-ignore= -i
+FLAGS.opt-keep-going= -k
+FLAGS.opt-no-action= -n
+FLAGS.opt-query= -q
+FLAGS.opt-var-expanded= -v VAR -v VALUE
+FLAGS.opt-var-literal= -V VAR -V VALUE
+FLAGS.opt-warnings-as-errors= -W
FLAGS.order= -j1
+FLAGS.recursive= -dL
+FLAGS.sh-leading-plus= -n
+FLAGS.vardebug= -k -dv FROM_CMDLINE=
+FLAGS.varmod-match-escape= -dv
+FLAGS.varname-dot-shell= -dpv
+FLAGS.varname-empty= -dv '$${:U}=cmdline-u' '=cmline-plain'
# Some tests need extra post-processing.
-SED_CMDS.modmisc+= -e 's,\(substitution error:\).*,\1 (details omitted),'
-SED_CMDS.varshell+= -e 's,^[a-z]*sh: ,,'
+SED_CMDS.opt-debug-g1= -e 's,${.CURDIR},CURDIR,'
+SED_CMDS.opt-debug-g1+= -e '/Global Variables:/,/Suffixes:/d'
+SED_CMDS.sh-dots= -e 's,^.*\.\.\.:.*,<normalized: ...: not found>,'
+SED_CMDS.varmod-subst-regex+= \
+ -e 's,\(Regex compilation error:\).*,\1 (details omitted),'
+SED_CMDS.varmod-edge+= -e 's, line [0-9]*:, line omitted:,'
+SED_CMDS.varshell+= -e 's,^${.SHELL:T}: ,,'
SED_CMDS.varshell+= -e '/command/s,No such.*,not found,'
+SED_CMDS.varname-dot-shell= -e 's, = /.*, = (details omitted),'
+SED_CMDS.varname-dot-shell+= -e 's,"/[^"]*","(details omitted)",'
+SED_CMDS.varname-dot-shell+= -e 's,\[/[^]]*\],[(details omitted)],'
+
+# Some tests need an additional round of postprocessing.
+POSTPROC.counter= ${TOOL_SED} -n -e '/:RELEVANT = yes/,/:RELEVANT = no/p'
+POSTPROC.deptgt-suffixes= \
+ ${TOOL_SED} -n -e '/^\#\*\*\* Suffixes/,/^\#\*/p'
+POSTPROC.vardebug= ${TOOL_SED} -n -e '/:RELEVANT = yes/,/:RELEVANT = no/p'
+POSTPROC.varmod-match-escape= ${TOOL_SED} -n -e '/^Pattern/p'
+POSTPROC.varname-dot-shell= \
+ awk '/\.SHELL/ || /^ParseReadLine/'
+POSTPROC.varname-empty= ${TOOL_SED} -n -e '/^Var_Set/p' -e '/^out:/p'
+
+# Some tests reuse other tests, which makes them unnecessarily fragile.
+export-all.rawout: export.mk
+unexport.rawout: export.mk
+unexport-env.rawout: export.mk
# End of the configuration section.
.MAIN: all
+.-include "Makefile.inc"
.-include "Makefile.config"
UNIT_TESTS:= ${.PARSEDIR}
@@ -96,16 +392,14 @@ OUTFILES= ${TESTS:=.out}
all: ${OUTFILES}
-CLEANFILES+= *.rawout *.out *.status *.tmp *.core *.tmp
+CLEANFILES= *.rawout *.out *.status *.tmp *.core *.tmp
CLEANFILES+= obj*.[och] lib*.a # posix1.mk
CLEANFILES+= issue* .[ab]* # suffixes.mk
-CLEANRECURSIVE+= dir dummy # posix1.mk
+CLEANDIRS= dir dummy # posix1.mk
clean:
rm -f ${CLEANFILES}
-.if !empty(CLEANRECURSIVE)
- rm -rf ${CLEANRECURSIVE}
-.endif
+ rm -rf ${CLEANDIRS}
TEST_MAKE?= ${.MAKE}
TOOL_SED?= sed
@@ -120,13 +414,24 @@ LANG= C
.export LANG LC_ALL
.endif
+.if ${.MAKE.MODE:Unormal:Mmeta} != ""
+# we don't need the noise
+_MKMSG_TEST= :
+.endif
+
# the tests are actually done with sub-makes.
.SUFFIXES: .mk .rawout .out
.mk.rawout:
- @echo ${TEST_MAKE} ${FLAGS.${.TARGET:R}:U-k} -f ${.IMPSRC}
- -@cd ${.OBJDIR} && \
- { ${TEST_MAKE} ${FLAGS.${.TARGET:R}:U-k} -f ${.IMPSRC} \
- 2>&1 ; echo $$? >${.TARGET:R}.status ; } > ${.TARGET}.tmp
+ @${_MKMSG_TEST:Uecho '# test '} ${.PREFIX}
+ @set -eu; \
+ cd ${.OBJDIR}; \
+ env -i PATH="$$PATH" ${ENV.${.TARGET:R}} \
+ ${TEST_MAKE} \
+ -r -C ${.CURDIR} -f ${.IMPSRC} \
+ ${FLAGS.${.TARGET:R}:U-k} \
+ > ${.TARGET}.tmp 2>&1 \
+ && status=$$? || status=$$?; \
+ echo $$status > ${.TARGET:R}.status
@mv ${.TARGET}.tmp ${.TARGET}
# Post-process the test output so that the results can be compared.
@@ -141,11 +446,12 @@ _SED_CMDS+= -e 's,${.CURDIR:S,.,\\.,g}/,,g'
_SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g'
.rawout.out:
- @echo postprocess ${.TARGET}
@${TOOL_SED} ${_SED_CMDS} ${SED_CMDS.${.TARGET:R}} \
- < ${.IMPSRC} > ${.TARGET}.tmp
- @echo "exit status `cat ${.TARGET:R}.status`" >> ${.TARGET}.tmp
- @mv ${.TARGET}.tmp ${.TARGET}
+ < ${.IMPSRC} > ${.TARGET}.tmp1
+ @${POSTPROC.${.TARGET:R}:Ucat} < ${.TARGET}.tmp1 > ${.TARGET}.tmp2
+ @rm ${.TARGET}.tmp1
+ @echo "exit status `cat ${.TARGET:R}.status`" >> ${.TARGET}.tmp2
+ @mv ${.TARGET}.tmp2 ${.TARGET}
# Compare all output files
test: ${OUTFILES} .PHONY
@@ -168,7 +474,7 @@ accept:
done
.if exists(${TEST_MAKE})
-${TESTS:=.rawout}: ${TEST_MAKE}
+${TESTS:=.rawout}: ${TEST_MAKE} ${.PARSEDIR}/Makefile
.endif
.-include <obj.mk>
diff --git a/contrib/bmake/unit-tests/archive-suffix.exp b/contrib/bmake/unit-tests/archive-suffix.exp
new file mode 100755
index 000000000000..05f2ac6624c4
--- /dev/null
+++ b/contrib/bmake/unit-tests/archive-suffix.exp
@@ -0,0 +1,2 @@
+`all' is up to date.
+exit status 0
diff --git a/contrib/bmake/unit-tests/archive-suffix.mk b/contrib/bmake/unit-tests/archive-suffix.mk
new file mode 100755
index 000000000000..9f7fa219c667
--- /dev/null
+++ b/contrib/bmake/unit-tests/archive-suffix.mk
@@ -0,0 +1,23 @@
+# $NetBSD: archive-suffix.mk,v 1.1 2020/08/29 14:47:26 rillig Exp $
+#
+# Between 2020-08-23 and 2020-08-30, the below code produced an assertion
+# failure in Var_Set_with_flags, triggered by Compat_Make, when setting the
+# .IMPSRC of an archive node to its .TARGET.
+#
+# The code assumed that the .TARGET variable of every node would be set, but
+# but that is not guaranteed.
+#
+# Between 2016-03-15 and 2016-03-16 the behavior of the below code changed.
+# Until 2016-03-15, it remade the target, starting with 2016-03-16 it says
+# "`all' is up to date".
+
+.SUFFIXES:
+.SUFFIXES: .c .o
+
+all: lib.a(obj1.o)
+
+.c.o:
+ : making $@
+
+obj1.c:
+ : $@
diff --git a/contrib/bmake/unit-tests/archive.exp b/contrib/bmake/unit-tests/archive.exp
new file mode 100644
index 000000000000..a42b4f39e173
--- /dev/null
+++ b/contrib/bmake/unit-tests/archive.exp
@@ -0,0 +1,13 @@
+rm -f libprog.a
+ar cru libprog.a archive.mk modmisc.mk varmisc.mk
+ranlib libprog.a
+ar t libprog.a
+archive.mk
+modmisc.mk
+varmisc.mk
+list-archive-wildcard: archive.mk
+list-archive-wildcard: ternary.mk
+depend-on-existing-member
+`depend-on-nonexistent-member' is up to date.
+rm -f libprog.a
+exit status 0
diff --git a/contrib/bmake/unit-tests/archive.mk b/contrib/bmake/unit-tests/archive.mk
new file mode 100644
index 000000000000..c3b7e919eab2
--- /dev/null
+++ b/contrib/bmake/unit-tests/archive.mk
@@ -0,0 +1,45 @@
+# $NetBSD: archive.mk,v 1.5 2020/08/23 17:51:24 rillig Exp $
+#
+# Very basic demonstration of handling archives, based on the description
+# in PSD.doc/tutorial.ms.
+
+ARCHIVE= libprog.${EXT.a}
+FILES= archive.${EXT.mk} modmisc.${EXT.mk} varmisc.mk
+
+EXT.a= a
+EXT.mk= mk
+
+MAKE_CMD= ${.MAKE} -f ${MAKEFILE}
+RUN?= @set -eu;
+
+all:
+ ${RUN} ${MAKE_CMD} remove-archive
+ ${RUN} ${MAKE_CMD} create-archive
+ ${RUN} ${MAKE_CMD} list-archive
+ ${RUN} ${MAKE_CMD} list-archive-wildcard
+ ${RUN} ${MAKE_CMD} depend-on-existing-member
+ ${RUN} ${MAKE_CMD} depend-on-nonexistent-member
+ ${RUN} ${MAKE_CMD} remove-archive
+
+create-archive: ${ARCHIVE}
+${ARCHIVE}: ${ARCHIVE}(${FILES})
+ ar cru ${.TARGET} ${.OODATE}
+ ranlib ${.TARGET}
+
+list-archive: ${ARCHIVE}
+ ar t ${.ALLSRC}
+
+# XXX: I had expected that this dependency would select all *.mk files from
+# the archive. Instead, the globbing is done in the current directory.
+# To prevent an overly long file list, the pattern is restricted to [at]*.mk.
+list-archive-wildcard: ${ARCHIVE}([at]*.mk)
+ ${RUN} printf '%s\n' ${.ALLSRC:O:@member@${.TARGET:Q}': '${member:Q}@}
+
+depend-on-existing-member: ${ARCHIVE}(archive.mk)
+ ${RUN} echo $@
+
+depend-on-nonexistent-member: ${ARCHIVE}(nonexistent.mk)
+ ${RUN} echo $@
+
+remove-archive:
+ rm -f ${ARCHIVE}
diff --git a/contrib/bmake/unit-tests/cmd-interrupt.exp b/contrib/bmake/unit-tests/cmd-interrupt.exp
new file mode 100755
index 000000000000..91f4439e7bea
--- /dev/null
+++ b/contrib/bmake/unit-tests/cmd-interrupt.exp
@@ -0,0 +1,9 @@
+> cmd-interrupt-ordinary
+make: *** cmd-interrupt-ordinary removed
+interrupt-ordinary: ok
+> cmd-interrupt-phony
+make: *** cmd-interrupt-phony removed
+interrupt-phony: ok
+> cmd-interrupt-precious
+interrupt-precious: ok
+exit status 0
diff --git a/contrib/bmake/unit-tests/cmd-interrupt.mk b/contrib/bmake/unit-tests/cmd-interrupt.mk
new file mode 100755
index 000000000000..033f3307bd2e
--- /dev/null
+++ b/contrib/bmake/unit-tests/cmd-interrupt.mk
@@ -0,0 +1,50 @@
+# $NetBSD: cmd-interrupt.mk,v 1.2 2020/08/28 18:16:22 rillig Exp $
+#
+# Tests for interrupting a command.
+#
+# If a command is interrupted (usually by the user, here by itself), the
+# target is removed. This is to avoid having an unfinished target that
+# would be newer than all of its sources and would therefore not be
+# tried again in the next run.
+#
+# This happens for ordinary targets as well as for .PHONY targets, even
+# though the .PHONY targets usually do not correspond to a file.
+#
+# To protect the target from being removed, the target has to be marked with
+# the special source .PRECIOUS. These targets need to ensure for themselves
+# that interrupting them does not leave an inconsistent state behind.
+#
+# See also:
+# CompatDeleteTarget
+
+all: clean-before interrupt-ordinary interrupt-phony interrupt-precious clean-after
+
+clean-before clean-after: .PHONY
+ @rm -f cmd-interrupt-ordinary cmd-interrupt-phony cmd-interrupt-precious
+
+interrupt-ordinary: .PHONY
+ @${.MAKE} ${MAKEFLAGS} -f ${MAKEFILE} cmd-interrupt-ordinary || true
+ # The ././ is necessary to work around the file cache.
+ @echo ${.TARGET}: ${exists(././cmd-interrupt-ordinary) :? error : ok }
+
+interrupt-phony: .PHONY
+ @${.MAKE} ${MAKEFLAGS} -f ${MAKEFILE} cmd-interrupt-phony || true
+ # The ././ is necessary to work around the file cache.
+ @echo ${.TARGET}: ${exists(././cmd-interrupt-phony) :? error : ok }
+
+interrupt-precious: .PRECIOUS
+ @${.MAKE} ${MAKEFLAGS} -f ${MAKEFILE} cmd-interrupt-precious || true
+ # The ././ is necessary to work around the file cache.
+ @echo ${.TARGET}: ${exists(././cmd-interrupt-precious) :? ok : error }
+
+cmd-interrupt-ordinary:
+ > ${.TARGET}
+ @kill -INT ${.MAKE.PID}
+
+cmd-interrupt-phony: .PHONY
+ > ${.TARGET}
+ @kill -INT ${.MAKE.PID}
+
+cmd-interrupt-precious: .PRECIOUS
+ > ${.TARGET}
+ @kill -INT ${.MAKE.PID}
diff --git a/contrib/bmake/unit-tests/cmdline.exp b/contrib/bmake/unit-tests/cmdline.exp
new file mode 100644
index 000000000000..8e981ba5248c
--- /dev/null
+++ b/contrib/bmake/unit-tests/cmdline.exp
@@ -0,0 +1,5 @@
+makeobjdir-direct:
+show-objdir: /tmp/6a8899d2-d227-4b55-9b6b-f3c8eeb83fd5
+makeobjdir-indirect:
+show-objdir: /tmp/a7b41170-53f8-4cc2-bc5c-e4c3dd93ec45/
+exit status 0
diff --git a/contrib/bmake/unit-tests/cmdline.mk b/contrib/bmake/unit-tests/cmdline.mk
new file mode 100644
index 000000000000..c12c31220cb5
--- /dev/null
+++ b/contrib/bmake/unit-tests/cmdline.mk
@@ -0,0 +1,37 @@
+# $NetBSD: cmdline.mk,v 1.1 2020/07/28 22:44:44 rillig Exp $
+#
+# Tests for command line parsing and related special variables.
+
+RUN?= @set -eu;
+TMPBASE?= /tmp
+SUB1= a7b41170-53f8-4cc2-bc5c-e4c3dd93ec45 # just a random UUID
+SUB2= 6a8899d2-d227-4b55-9b6b-f3c8eeb83fd5 # just a random UUID
+MAKE_CMD= env TMPBASE=${TMPBASE}/${SUB1} ${.MAKE} -f ${MAKEFILE} -r
+DIR2= ${TMPBASE}/${SUB2}
+DIR12= ${TMPBASE}/${SUB1}/${SUB2}
+
+all: prepare-dirs
+all: makeobjdir-direct makeobjdir-indirect
+
+prepare-dirs:
+ ${RUN} rm -rf ${DIR2} ${DIR12}
+ ${RUN} mkdir -p ${DIR2} ${DIR12}
+
+# The .OBJDIR can be set via the MAKEOBJDIR command line variable.
+# It must be a command line variable; an environment variable would not work.
+makeobjdir-direct:
+ @echo $@:
+ ${RUN} ${MAKE_CMD} MAKEOBJDIR=${DIR2} show-objdir
+
+# The .OBJDIR can be set via the MAKEOBJDIR command line variable,
+# and that variable could even contain the usual modifiers.
+# Since the .OBJDIR=MAKEOBJDIR assignment happens very early,
+# the SUB2 variable in the modifier is not defined yet and is therefore empty.
+# The SUB1 in the resulting path comes from the environment variable TMPBASE,
+# see MAKE_CMD.
+makeobjdir-indirect:
+ @echo $@:
+ ${RUN} ${MAKE_CMD} MAKEOBJDIR='$${TMPBASE}/$${SUB2}' show-objdir
+
+show-objdir:
+ @echo $@: ${.OBJDIR:Q}
diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric-eq.exp b/contrib/bmake/unit-tests/cond-cmp-numeric-eq.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-cmp-numeric-eq.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric-eq.mk b/contrib/bmake/unit-tests/cond-cmp-numeric-eq.mk
new file mode 100755
index 000000000000..02b95ae593cb
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-cmp-numeric-eq.mk
@@ -0,0 +1,53 @@
+# $NetBSD: cond-cmp-numeric-eq.mk,v 1.1 2020/08/23 13:50:17 rillig Exp $
+#
+# Tests for numeric comparisons with the == operator in .if conditions.
+
+# This comparison yields the same result, whether numeric or character-based.
+.if 1 == 1
+.else
+.error
+.endif
+
+# This comparison yields the same result, whether numeric or character-based.
+.if 1 == 2
+.error
+.endif
+
+.if 2 == 1
+.error
+.endif
+
+# Scientific notation is supported, as per strtod.
+.if 2e7 == 2000e4
+.else
+.error
+.endif
+
+.if 2000e4 == 2e7
+.else
+.error
+.endif
+
+# Trailing zeroes after the decimal point are irrelevant for the numeric
+# value.
+.if 3.30000 == 3.3
+.else
+.error
+.endif
+
+.if 3.3 == 3.30000
+.else
+.error
+.endif
+
+# As of 2020-08-23, numeric comparison is implemented as parsing both sides
+# as double, and then performing a normal comparison. The range of double is
+# typically 16 or 17 significant digits, therefore these two numbers seem to
+# be equal.
+.if 1.000000000000000001 == 1.000000000000000002
+.else
+.error
+.endif
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric-ge.exp b/contrib/bmake/unit-tests/cond-cmp-numeric-ge.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-cmp-numeric-ge.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric-ge.mk b/contrib/bmake/unit-tests/cond-cmp-numeric-ge.mk
new file mode 100755
index 000000000000..510b80e14942
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-cmp-numeric-ge.mk
@@ -0,0 +1,75 @@
+# $NetBSD: cond-cmp-numeric-ge.mk,v 1.1 2020/08/23 13:50:17 rillig Exp $
+#
+# Tests for numeric comparisons with the >= operator in .if conditions.
+
+# When both sides are equal, the >= operator always yields true.
+.if 1 >= 1
+.else
+.error
+.endif
+
+# This comparison yields the same result, whether numeric or character-based.
+.if 1 >= 2
+.error
+.endif
+
+.if 2 >= 1
+.else
+.error
+.endif
+
+# If this comparison were character-based instead of numerical, the
+# 5 would be >= 14 since its first digit is greater.
+.if 5 >= 14
+.error
+.endif
+
+.if 14 >= 5
+.else
+.error
+.endif
+
+# Scientific notation is supported, as per strtod.
+.if 2e7 >= 1e8
+.error
+.endif
+
+.if 1e8 >= 2e7
+.else
+.error
+.endif
+
+# Floating pointer numbers can be compared as well.
+# This might be tempting to use for version numbers, but there are a few pitfalls.
+.if 3.141 >= 111.222
+.error
+.endif
+
+.if 111.222 >= 3.141
+.else
+.error
+.endif
+
+# When parsed as a version number, 3.30 is greater than 3.7.
+# Since make parses numbers as plain numbers, that leads to wrong results.
+# Numeric comparisons are not suited for comparing version number.
+.if 3.30 >= 3.7
+.error
+.endif
+
+.if 3.7 >= 3.30
+.else
+.error
+.endif
+
+# As of 2020-08-23, numeric comparison is implemented as parsing both sides
+# as double, and then performing a normal comparison. The range of double is
+# typically 16 or 17 significant digits, therefore these two numbers seem to
+# be equal.
+.if 1.000000000000000001 >= 1.000000000000000002
+.else
+.error
+.endif
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric-gt.exp b/contrib/bmake/unit-tests/cond-cmp-numeric-gt.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-cmp-numeric-gt.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric-gt.mk b/contrib/bmake/unit-tests/cond-cmp-numeric-gt.mk
new file mode 100755
index 000000000000..24ac1eb8531b
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-cmp-numeric-gt.mk
@@ -0,0 +1,73 @@
+# $NetBSD: cond-cmp-numeric-gt.mk,v 1.1 2020/08/23 13:50:17 rillig Exp $
+#
+# Tests for numeric comparisons with the > operator in .if conditions.
+
+# When both sides are equal, the > operator always yields false.
+.if 1 > 1
+.error
+.endif
+
+# This comparison yields the same result, whether numeric or character-based.
+.if 1 > 2
+.error
+.endif
+
+.if 2 > 1
+.else
+.error
+.endif
+
+# If this comparison were character-based instead of numerical, the
+# 5 would be > 14 since its first digit is greater.
+.if 5 > 14
+.error
+.endif
+
+.if 14 > 5
+.else
+.error
+.endif
+
+# Scientific notation is supported, as per strtod.
+.if 2e7 > 1e8
+.error
+.endif
+
+.if 1e8 > 2e7
+.else
+.error
+.endif
+
+# Floating pointer numbers can be compared as well.
+# This might be tempting to use for version numbers, but there are a few pitfalls.
+.if 3.141 > 111.222
+.error
+.endif
+
+.if 111.222 > 3.141
+.else
+.error
+.endif
+
+# When parsed as a version number, 3.30 is greater than 3.7.
+# Since make parses numbers as plain numbers, that leads to wrong results.
+# Numeric comparisons are not suited for comparing version number.
+.if 3.30 > 3.7
+.error
+.endif
+
+.if 3.7 > 3.30
+.else
+.error
+.endif
+
+# As of 2020-08-23, numeric comparison is implemented as parsing both sides
+# as double, and then performing a normal comparison. The range of double is
+# typically 16 or 17 significant digits, therefore these two numbers seem to
+# be equal.
+.if 1.000000000000000001 > 1.000000000000000002
+.error
+.endif
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric-le.exp b/contrib/bmake/unit-tests/cond-cmp-numeric-le.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-cmp-numeric-le.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric-le.mk b/contrib/bmake/unit-tests/cond-cmp-numeric-le.mk
new file mode 100755
index 000000000000..2e4f5e9e694b
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-cmp-numeric-le.mk
@@ -0,0 +1,75 @@
+# $NetBSD: cond-cmp-numeric-le.mk,v 1.1 2020/08/23 13:50:17 rillig Exp $
+#
+# Tests for numeric comparisons with the <= operator in .if conditions.
+
+# When both sides are equal, the <= operator always yields true.
+.if 1 <= 1
+.else
+.error
+.endif
+
+# This comparison yields the same result, whether numeric or character-based.
+.if 1 <= 2
+.else
+.error
+.endif
+
+.if 2 <= 1
+.error
+.endif
+
+# If this comparison were character-based instead of numerical, the
+# 5 would be >= 14 since its first digit is greater.
+.if 5 <= 14
+.else
+.error
+.endif
+
+.if 14 <= 5
+.error
+.endif
+
+# Scientific notation is supported, as per strtod.
+.if 2e7 <= 1e8
+.else
+.error
+.endif
+
+.if 1e8 <= 2e7
+.error
+.endif
+
+# Floating pointer numbers can be compared as well.
+# This might be tempting to use for version numbers, but there are a few pitfalls.
+.if 3.141 <= 111.222
+.else
+.error
+.endif
+
+.if 111.222 <= 3.141
+.error
+.endif
+
+# When parsed as a version number, 3.30 is greater than 3.7.
+# Since make parses numbers as plain numbers, that leads to wrong results.
+# Numeric comparisons are not suited for comparing version number.
+.if 3.30 <= 3.7
+.else
+.error
+.endif
+
+.if 3.7 <= 3.30
+.error
+.endif
+
+# As of 2020-08-23, numeric comparison is implemented as parsing both sides
+# as double, and then performing a normal comparison. The range of double is
+# typically 16 or 17 significant digits, therefore these two numbers seem to
+# be equal.
+.if 1.000000000000000001 <= 1.000000000000000002
+.else
+.error
+.endif
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric-lt.exp b/contrib/bmake/unit-tests/cond-cmp-numeric-lt.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-cmp-numeric-lt.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric-lt.mk b/contrib/bmake/unit-tests/cond-cmp-numeric-lt.mk
new file mode 100755
index 000000000000..a5fcceddff4b
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-cmp-numeric-lt.mk
@@ -0,0 +1,73 @@
+# $NetBSD: cond-cmp-numeric-lt.mk,v 1.1 2020/08/23 13:50:17 rillig Exp $
+#
+# Tests for numeric comparisons with the < operator in .if conditions.
+
+# When both sides are equal, the < operator always yields false.
+.if 1 < 1
+.error
+.endif
+
+# This comparison yields the same result, whether numeric or character-based.
+.if 1 < 2
+.else
+.error
+.endif
+
+.if 2 < 1
+.error
+.endif
+
+# If this comparison were character-based instead of numerical, the
+# 5 would be > 14 since its first digit is greater.
+.if 5 < 14
+.else
+.error
+.endif
+
+.if 14 < 5
+.error
+.endif
+
+# Scientific notation is supported, as per strtod.
+.if 2e7 < 1e8
+.else
+.error
+.endif
+
+.if 1e8 < 2e7
+.error
+.endif
+
+# Floating pointer numbers can be compared as well.
+# This might be tempting to use for version numbers, but there are a few pitfalls.
+.if 3.141 < 111.222
+.else
+.error
+.endif
+
+.if 111.222 < 3.141
+.error
+.endif
+
+# When parsed as a version number, 3.30 is greater than 3.7.
+# Since make parses numbers as plain numbers, that leads to wrong results.
+# Numeric comparisons are not suited for comparing version number.
+.if 3.30 < 3.7
+.else
+.error
+.endif
+
+.if 3.7 < 3.30
+.error
+.endif
+
+# As of 2020-08-23, numeric comparison is implemented as parsing both sides
+# as double, and then performing a normal comparison. The range of double is
+# typically 16 or 17 significant digits, therefore these two numbers seem to
+# be equal.
+.if 1.000000000000000001 < 1.000000000000000002
+.error
+.endif
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric-ne.exp b/contrib/bmake/unit-tests/cond-cmp-numeric-ne.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-cmp-numeric-ne.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric-ne.mk b/contrib/bmake/unit-tests/cond-cmp-numeric-ne.mk
new file mode 100755
index 000000000000..6f858584d139
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-cmp-numeric-ne.mk
@@ -0,0 +1,49 @@
+# $NetBSD: cond-cmp-numeric-ne.mk,v 1.1 2020/08/23 13:50:17 rillig Exp $
+#
+# Tests for numeric comparisons with the != operator in .if conditions.
+
+# When both sides are equal, the != operator always yields false.
+.if 1 != 1
+.error
+.endif
+
+# This comparison yields the same result, whether numeric or character-based.
+.if 1 != 2
+.else
+.error
+.endif
+
+.if 2 != 1
+.else
+.error
+.endif
+
+# Scientific notation is supported, as per strtod.
+.if 2e7 != 2000e4
+.error
+.endif
+
+.if 2000e4 != 2e7
+.error
+.endif
+
+# Trailing zeroes after the decimal point are irrelevant for the numeric
+# value.
+.if 3.30000 != 3.3
+.error
+.endif
+
+.if 3.3 != 3.30000
+.error
+.endif
+
+# As of 2020-08-23, numeric comparison is implemented as parsing both sides
+# as double, and then performing a normal comparison. The range of double is
+# typically 16 or 17 significant digits, therefore these two numbers seem to
+# be equal.
+.if 1.000000000000000001 != 1.000000000000000002
+.error
+.endif
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric.exp b/contrib/bmake/unit-tests/cond-cmp-numeric.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-cmp-numeric.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric.mk b/contrib/bmake/unit-tests/cond-cmp-numeric.mk
new file mode 100644
index 000000000000..409636c3c3ca
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-cmp-numeric.mk
@@ -0,0 +1,8 @@
+# $NetBSD: cond-cmp-numeric.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for numeric comparisons in .if conditions.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/cond-cmp-string.exp b/contrib/bmake/unit-tests/cond-cmp-string.exp
new file mode 100644
index 000000000000..03dcb0416898
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-cmp-string.exp
@@ -0,0 +1,5 @@
+make: "cond-cmp-string.mk" line 18: Malformed conditional (str != str)
+make: "cond-cmp-string.mk" line 37: Malformed conditional ("string" != "str""ing")
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/cond-cmp-string.mk b/contrib/bmake/unit-tests/cond-cmp-string.mk
new file mode 100644
index 000000000000..67d86b61e88d
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-cmp-string.mk
@@ -0,0 +1,39 @@
+# $NetBSD: cond-cmp-string.mk,v 1.3 2020/08/20 18:43:19 rillig Exp $
+#
+# Tests for string comparisons in .if conditions.
+
+# This is a simple comparison of string literals.
+# Nothing surprising here.
+.if "str" != "str"
+.error
+.endif
+
+# The right-hand side of the comparison may be written without quotes.
+.if "str" != str
+.error
+.endif
+
+# The left-hand side of the comparison must be enclosed in quotes.
+# This one is not enclosed in quotes and thus generates an error message.
+.if str != str
+.error
+.endif
+
+# The left-hand side of the comparison requires a defined variable.
+# The variable named "" is not defined, but applying the :U modifier to it
+# makes it "kind of defined" (see VAR_KEEP). Therefore it is ok here.
+.if ${:Ustr} != "str"
+.error
+.endif
+
+# Any character in a string literal may be escaped using a backslash.
+# This means that "\n" does not mean a newline but a simple "n".
+.if "string" != "\s\t\r\i\n\g"
+.error
+.endif
+
+# It is not possible to concatenate two string literals to form a single
+# string.
+.if "string" != "str""ing"
+.error
+.endif
diff --git a/contrib/bmake/unit-tests/cond-func-commands.exp b/contrib/bmake/unit-tests/cond-func-commands.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-func-commands.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/cond-func-commands.mk b/contrib/bmake/unit-tests/cond-func-commands.mk
new file mode 100644
index 000000000000..4a098169048c
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-func-commands.mk
@@ -0,0 +1,36 @@
+# $NetBSD: cond-func-commands.mk,v 1.3 2020/08/23 14:07:20 rillig Exp $
+#
+# Tests for the commands() function in .if conditions.
+
+.MAIN: all
+
+# The target "target" does not exist yet, therefore it cannot have commands.
+.if commands(target)
+.error
+.endif
+
+target:
+
+# Now the target exists, but it still has no commands.
+.if commands(target)
+.error
+.endif
+
+target:
+ # not a command
+
+# Even after the comment, the target still has no commands.
+.if commands(target)
+.error
+.endif
+
+target:
+ @:;
+
+# Finally the target has commands.
+.if !commands(target)
+.error
+.endif
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/cond-func-defined.exp b/contrib/bmake/unit-tests/cond-func-defined.exp
new file mode 100644
index 000000000000..70c6342a02c3
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-func-defined.exp
@@ -0,0 +1,5 @@
+make: "cond-func-defined.mk" line 23: warning: Missing closing parenthesis for defined()
+make: "cond-func-defined.mk" line 23: Malformed conditional (!defined(A B))
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/cond-func-defined.mk b/contrib/bmake/unit-tests/cond-func-defined.mk
new file mode 100644
index 000000000000..dce1399183aa
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-func-defined.mk
@@ -0,0 +1,33 @@
+# $NetBSD: cond-func-defined.mk,v 1.3 2020/08/20 17:23:43 rillig Exp $
+#
+# Tests for the defined() function in .if conditions.
+
+DEF= defined
+${:UA B}= variable name with spaces
+
+.if !defined(DEF)
+.error
+.endif
+
+# Horizontal whitespace after the opening parenthesis is ignored.
+.if !defined( DEF)
+.error
+.endif
+
+# Horizontal whitespace before the closing parenthesis is ignored.
+.if !defined(DEF )
+.error
+.endif
+
+# The argument of a function must not directly contain whitespace.
+.if !defined(A B)
+.error
+.endif
+
+# If necessary, the whitespace can be generated by a variable expression.
+.if !defined(${:UA B})
+.error
+.endif
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/cond-func-empty.exp b/contrib/bmake/unit-tests/cond-func-empty.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-func-empty.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/cond-func-empty.mk b/contrib/bmake/unit-tests/cond-func-empty.mk
new file mode 100644
index 000000000000..737403f94525
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-func-empty.mk
@@ -0,0 +1,8 @@
+# $NetBSD: cond-func-empty.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the empty() function in .if conditions.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/cond-func-exists.exp b/contrib/bmake/unit-tests/cond-func-exists.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-func-exists.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/cond-func-exists.mk b/contrib/bmake/unit-tests/cond-func-exists.mk
new file mode 100644
index 000000000000..6386f21fdc7b
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-func-exists.mk
@@ -0,0 +1,42 @@
+# $NetBSD: cond-func-exists.mk,v 1.4 2020/08/28 12:59:36 rillig Exp $
+#
+# Tests for the exists() function in .if conditions.
+
+.if !exists(.)
+.error
+.endif
+
+# The argument to the function must not be enclosed in quotes.
+# Neither double quotes nor single quotes are allowed.
+.if exists(".")
+.error
+.endif
+
+.if exists('.')
+.error
+.endif
+
+# The only way to escape characters that would otherwise influence the parser
+# is to enclose them in a variable expression. For function arguments,
+# neither the backslash nor the dollar sign act as escape character.
+.if exists(\.)
+.error
+.endif
+
+.if !exists(${:U.})
+.error
+.endif
+
+# The argument to the function can have several variable expressions.
+# See cond-func.mk for the characters that cannot be used directly.
+.if !exists(${.PARSEDIR}/${.PARSEFILE})
+.error
+.endif
+
+# Whitespace is trimmed on both sides of the function argument.
+.if !exists( . )
+.error
+.endif
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/cond-func-make.exp b/contrib/bmake/unit-tests/cond-func-make.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-func-make.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/cond-func-make.mk b/contrib/bmake/unit-tests/cond-func-make.mk
new file mode 100644
index 000000000000..baa0d37da726
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-func-make.mk
@@ -0,0 +1,8 @@
+# $NetBSD: cond-func-make.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the make() function in .if conditions.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/cond-func-target.exp b/contrib/bmake/unit-tests/cond-func-target.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-func-target.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/cond-func-target.mk b/contrib/bmake/unit-tests/cond-func-target.mk
new file mode 100644
index 000000000000..c36bcfe0a5f8
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-func-target.mk
@@ -0,0 +1,38 @@
+# $NetBSD: cond-func-target.mk,v 1.3 2020/08/23 14:07:20 rillig Exp $
+#
+# Tests for the target() function in .if conditions.
+
+.MAIN: all
+
+# The target "target" does not exist yet.
+.if target(target)
+.error
+.endif
+
+target:
+
+# The target exists, even though it does not have any commands.
+.if !target(target)
+.error
+.endif
+
+target:
+ # not a command
+
+# Adding a comment to an existing target does not change whether the target
+# is defined or not.
+.if !target(target)
+.error
+.endif
+
+target:
+ @:;
+
+# Adding a command to an existing target does not change whether the target
+# is defined or not.
+.if !target(target)
+.error
+.endif
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/cond-func.exp b/contrib/bmake/unit-tests/cond-func.exp
new file mode 100644
index 000000000000..0069ed75726c
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-func.exp
@@ -0,0 +1,9 @@
+make: "cond-func.mk" line 29: warning: Missing closing parenthesis for defined()
+make: "cond-func.mk" line 29: Malformed conditional (!defined(A B))
+make: "cond-func.mk" line 44: warning: Missing closing parenthesis for defined()
+make: "cond-func.mk" line 44: Malformed conditional (!defined(A&B))
+make: "cond-func.mk" line 47: warning: Missing closing parenthesis for defined()
+make: "cond-func.mk" line 47: Malformed conditional (!defined(A|B))
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/cond-func.mk b/contrib/bmake/unit-tests/cond-func.mk
new file mode 100644
index 000000000000..304735241e7f
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-func.mk
@@ -0,0 +1,63 @@
+# $NetBSD: cond-func.mk,v 1.1 2020/08/20 17:45:47 rillig Exp $
+#
+# Tests for those parts of the functions in .if conditions that are common
+# among several functions.
+#
+# The below test uses the function defined(...) since it has no side-effects,
+# the other functions (except empty(...)) would work equally well.
+
+DEF= defined
+${:UA B}= variable name with spaces
+${:UVAR(value)}= variable name with parentheses
+${:UVAR{value}}= variable name with braces
+
+.if !defined(DEF)
+.error
+.endif
+
+# Horizontal whitespace after the opening parenthesis is ignored.
+.if !defined( DEF)
+.error
+.endif
+
+# Horizontal whitespace before the closing parenthesis is ignored.
+.if !defined(DEF )
+.error
+.endif
+
+# The argument of a function must not directly contain whitespace.
+.if !defined(A B)
+.error
+.endif
+
+# If necessary, the whitespace can be generated by a variable expression.
+.if !defined(${:UA B})
+.error
+.endif
+
+# Characters that could be mistaken for operators must not appear directly
+# in a function argument. As with whitespace, these can be generated
+# indirectly.
+#
+# It's not entirely clear why these characters are forbidden.
+# The most plausible reason seems to be typo detection.
+.if !defined(A&B)
+.error
+.endif
+.if !defined(A|B)
+.error
+.endif
+
+# Even parentheses may appear in variable names.
+# They must be balanced though.
+.if !defined(VAR(value))
+.error
+.endif
+
+# Braces do not have any special meaning when parsing arguments.
+.if !defined(VAR{value})
+.error
+.endif
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/cond-late.exp b/contrib/bmake/unit-tests/cond-late.exp
index 3717b088c646..46c4aa2f4230 100644
--- a/contrib/bmake/unit-tests/cond-late.exp
+++ b/contrib/bmake/unit-tests/cond-late.exp
@@ -1,3 +1,4 @@
+make: Bad conditional expression ` != "no"' in != "no"?:
yes
no
exit status 0
diff --git a/contrib/bmake/unit-tests/cond-late.mk b/contrib/bmake/unit-tests/cond-late.mk
index 2eb02b42b920..397f5febd480 100644
--- a/contrib/bmake/unit-tests/cond-late.mk
+++ b/contrib/bmake/unit-tests/cond-late.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-late.mk,v 1.1 2020/04/29 23:15:21 rillig Exp $
+# $NetBSD: cond-late.mk,v 1.2 2020/07/25 20:37:46 rillig Exp $
#
# Using the :? modifier, variable expressions can contain conditional
# expressions that are evaluated late. Any variables appearing in these
@@ -15,9 +15,15 @@
# and then expand the variables, the output would change from the
# current "yes no" to "yes yes", since both variables are non-empty.
+all: cond-literal
+
COND.true= "yes" == "yes"
COND.false= "yes" != "yes"
-all:
+cond-literal:
@echo ${ ${COND.true} :?yes:no}
@echo ${ ${COND.false} :?yes:no}
+
+VAR+= ${${UNDEF} != "no":?:}
+.if empty(VAR:Mpattern)
+.endif
diff --git a/contrib/bmake/unit-tests/cond-op-and.exp b/contrib/bmake/unit-tests/cond-op-and.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-op-and.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/cond-op-and.mk b/contrib/bmake/unit-tests/cond-op-and.mk
new file mode 100644
index 000000000000..a204a227c4f0
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-op-and.mk
@@ -0,0 +1,27 @@
+# $NetBSD: cond-op-and.mk,v 1.3 2020/08/28 14:48:37 rillig Exp $
+#
+# Tests for the && operator in .if conditions.
+
+.if 0 && 0
+.error
+.endif
+
+.if 1 && 0
+.error
+.endif
+
+.if 0 && 1
+.error
+.endif
+
+.if !(1 && 1)
+.error
+.endif
+
+# The right-hand side is not evaluated since the left-hand side is already
+# false.
+.if 0 && ${UNDEF}
+.endif
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/cond-op-not.exp b/contrib/bmake/unit-tests/cond-op-not.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-op-not.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/cond-op-not.mk b/contrib/bmake/unit-tests/cond-op-not.mk
new file mode 100644
index 000000000000..ad0a0939eecf
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-op-not.mk
@@ -0,0 +1,22 @@
+# $NetBSD: cond-op-not.mk,v 1.3 2020/08/28 14:48:37 rillig Exp $
+#
+# Tests for the ! operator in .if conditions.
+
+# The exclamation mark negates its operand.
+.if !1
+.error
+.endif
+
+# Exclamation marks can be chained.
+# This doesn't happen in practice though.
+.if !!!1
+.error
+.endif
+
+# The ! binds more tightly than the &&.
+.if !!0 && 1
+.error
+.endif
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/cond-op-or.exp b/contrib/bmake/unit-tests/cond-op-or.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-op-or.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/cond-op-or.mk b/contrib/bmake/unit-tests/cond-op-or.mk
new file mode 100644
index 000000000000..696b9dd23062
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-op-or.mk
@@ -0,0 +1,27 @@
+# $NetBSD: cond-op-or.mk,v 1.3 2020/08/28 14:48:37 rillig Exp $
+#
+# Tests for the || operator in .if conditions.
+
+.if 0 || 0
+.error
+.endif
+
+.if !(1 || 0)
+.error
+.endif
+
+.if !(0 || 1)
+.error
+.endif
+
+.if !(1 || 1)
+.error
+.endif
+
+# The right-hand side is not evaluated since the left-hand side is already
+# true.
+.if 1 || ${UNDEF}
+.endif
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/cond-op-parentheses.exp b/contrib/bmake/unit-tests/cond-op-parentheses.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-op-parentheses.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/cond-op-parentheses.mk b/contrib/bmake/unit-tests/cond-op-parentheses.mk
new file mode 100644
index 000000000000..6c48d83dd2be
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-op-parentheses.mk
@@ -0,0 +1,8 @@
+# $NetBSD: cond-op-parentheses.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for parentheses in .if conditions.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/cond-op.exp b/contrib/bmake/unit-tests/cond-op.exp
new file mode 100644
index 000000000000..4caec4df659b
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-op.exp
@@ -0,0 +1,5 @@
+make: "cond-op.mk" line 45: Malformed conditional ("!word" == !word)
+make: "cond-op.mk" line 57: Parsing continues until here.
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/cond-op.mk b/contrib/bmake/unit-tests/cond-op.mk
new file mode 100644
index 000000000000..a22e4181e8fe
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-op.mk
@@ -0,0 +1,60 @@
+# $NetBSD: cond-op.mk,v 1.4 2020/08/28 14:07:51 rillig Exp $
+#
+# Tests for operators like &&, ||, ! in .if conditions.
+#
+# See also:
+# cond-op-and.mk
+# cond-op-not.mk
+# cond-op-or.mk
+# cond-op-parentheses.mk
+
+# In make, && binds more tightly than ||, like in C.
+# If make had the same precedence for both && and ||, the result would be
+# different.
+# If || were to bind more tightly than &&, the result would be different
+# as well.
+.if !(1 || 1 && 0)
+.error
+.endif
+
+# If make were to interpret the && and || operators like the shell, the
+# implicit binding would be this:
+.if (1 || 1) && 0
+.error
+.endif
+
+# The precedence of the ! operator is different from C though. It has a
+# lower precedence than the comparison operators.
+.if !"word" == "word"
+.error
+.endif
+
+# This is how the above condition is actually interpreted.
+.if !("word" == "word")
+.error
+.endif
+
+# TODO: Demonstrate that the precedence of the ! and == operators actually
+# makes a difference. There is a simple example for sure, I just cannot
+# wrap my head around it.
+
+# This condition is malformed because the '!' on the right-hand side must not
+# appear unquoted. If any, it must be enclosed in quotes.
+# In any case, it is not interpreted as a negation of an unquoted string.
+# See CondGetString.
+.if "!word" == !word
+.error
+.endif
+
+# Surprisingly, the ampersand and pipe are allowed in bare strings.
+# That's another opportunity for writing confusing code.
+# See CondGetString, which only has '!' in the list of stop characters.
+.if "a&&b||c" != a&&b||c
+.error
+.endif
+
+# Just in case that parsing should ever stop on the first error.
+.info Parsing continues until here.
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/cond-short.mk b/contrib/bmake/unit-tests/cond-short.mk
index 41d76768a404..ae441d80f7d9 100644
--- a/contrib/bmake/unit-tests/cond-short.mk
+++ b/contrib/bmake/unit-tests/cond-short.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-short.mk,v 1.7 2020/07/09 22:34:09 sjg Exp $
+# $NetBSD: cond-short.mk,v 1.9 2020/08/19 22:47:09 rillig Exp $
#
# Demonstrates that in conditions, the right-hand side of an && or ||
# is only evaluated if it can actually influence the result.
@@ -66,6 +66,19 @@ VAR= # empty again, for the following tests
.if 0 && !empty(:U${:!echo "unexpected exclam modifier" 1>&2 !})
.endif
+# Irrelevant assignment modifiers are skipped as well.
+.if 0 && ${1 2 3:L:@i@${FIRST::?=$i}@}
+.endif
+.if 0 && ${1 2 3:L:@i@${LAST::=$i}@}
+.endif
+.if 0 && ${1 2 3:L:@i@${APPENDED::+=$i}@}
+.endif
+.if 0 && ${echo.1 echo.2 echo.3:L:@i@${RAN::!=${i:C,.*,&; & 1>\&2,:S,., ,g}}@}
+.endif
+.if defined(FIRST) || defined(LAST) || defined(APPENDED) || defined(RAN)
+.warning first=${FIRST} last=${LAST} appended=${APPENDED} ran=${RAN}
+.endif
+
# The || operator.
.if 1 || ${echo "unexpected or" 1>&2 :L:sh}
@@ -110,6 +123,7 @@ x=Ok
x=Fail
.endif
x!= echo 'defined(V42) && ${V42} > 0: $x' >&2; echo
+
# this one throws both String comparison operator and
# Malformed conditional with cond.c 1.78
# indirect iV2 would expand to "" and treated as 0
@@ -119,6 +133,7 @@ x=Fail
x=Ok
.endif
x!= echo 'defined(V66) && ( "${iV2}" < ${V42} ): $x' >&2; echo
+
# next two thow String comparison operator with cond.c 1.78
# indirect iV1 would expand to 42
.if 1 || ${iV1} < ${V42}
@@ -127,12 +142,14 @@ x=Ok
x=Fail
.endif
x!= echo '1 || ${iV1} < ${V42}: $x' >&2; echo
+
.if 1 || ${iV2:U2} < ${V42}
x=Ok
.else
x=Fail
.endif
x!= echo '1 || ${iV2:U2} < ${V42}: $x' >&2; echo
+
# the same expressions are fine when the lhs is expanded
# ${iV1} expands to 42
.if 0 || ${iV1} <= ${V42}
@@ -141,6 +158,7 @@ x=Ok
x=Fail
.endif
x!= echo '0 || ${iV1} <= ${V42}: $x' >&2; echo
+
# ${iV2:U2} expands to 2
.if 0 || ${iV2:U2} < ${V42}
x=Ok
diff --git a/contrib/bmake/unit-tests/cond-token-number.exp b/contrib/bmake/unit-tests/cond-token-number.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-token-number.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/cond-token-number.mk b/contrib/bmake/unit-tests/cond-token-number.mk
new file mode 100644
index 000000000000..30e7e84e81bb
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-token-number.mk
@@ -0,0 +1,8 @@
+# $NetBSD: cond-token-number.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for number tokens in .if conditions.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/cond-token-plain.exp b/contrib/bmake/unit-tests/cond-token-plain.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-token-plain.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/cond-token-plain.mk b/contrib/bmake/unit-tests/cond-token-plain.mk
new file mode 100644
index 000000000000..fc13245382f1
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-token-plain.mk
@@ -0,0 +1,9 @@
+# $NetBSD: cond-token-plain.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for plain tokens (that is, string literals without quotes)
+# in .if conditions.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/cond-token-string.exp b/contrib/bmake/unit-tests/cond-token-string.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-token-string.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/cond-token-string.mk b/contrib/bmake/unit-tests/cond-token-string.mk
new file mode 100644
index 000000000000..1a8019754824
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-token-string.mk
@@ -0,0 +1,8 @@
+# $NetBSD: cond-token-string.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for quoted and unquoted string literals in .if conditions.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/cond-token-var.exp b/contrib/bmake/unit-tests/cond-token-var.exp
new file mode 100644
index 000000000000..eb71a43c55f3
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-token-var.exp
@@ -0,0 +1,7 @@
+make: "cond-token-var.mk" line 9: ok
+make: "cond-token-var.mk" line 15: Malformed conditional (${UNDEF} == ${DEF})
+make: "cond-token-var.mk" line 20: Malformed conditional (${DEF} == ${UNDEF})
+make: "cond-token-var.mk" line 29: Malformed conditional (${UNDEF})
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/cond-token-var.mk b/contrib/bmake/unit-tests/cond-token-var.mk
new file mode 100644
index 000000000000..5d5bbf2c7fc4
--- /dev/null
+++ b/contrib/bmake/unit-tests/cond-token-var.mk
@@ -0,0 +1,34 @@
+# $NetBSD: cond-token-var.mk,v 1.3 2020/08/20 19:43:42 rillig Exp $
+#
+# Tests for variables in .if conditions.
+
+DEF= defined
+
+# A defined variable may appear on either side of the comparison.
+.if ${DEF} == ${DEF}
+.info ok
+.else
+.error
+.endif
+
+# A variable that appears on the left-hand side must be defined.
+.if ${UNDEF} == ${DEF}
+.error
+.endif
+
+# A variable that appears on the right-hand side must be defined.
+.if ${DEF} == ${UNDEF}
+.error
+.endif
+
+# A defined variable may appear as an expression of its own.
+.if ${DEF}
+.endif
+
+# An undefined variable generates a warning.
+.if ${UNDEF}
+.endif
+
+# The :U modifier turns an undefined variable into an ordinary expression.
+.if ${UNDEF:U}
+.endif
diff --git a/contrib/bmake/unit-tests/counter.exp b/contrib/bmake/unit-tests/counter.exp
new file mode 100644
index 000000000000..c00b3e57056d
--- /dev/null
+++ b/contrib/bmake/unit-tests/counter.exp
@@ -0,0 +1,88 @@
+Global:RELEVANT = yes (load-time part)
+Global:COUNTER =
+Global:NEXT = ${COUNTER::=${COUNTER} a}${COUNTER:[#]}
+Global:A =
+Var_Parse: ${NEXT} with VARE_WANTRES|VARE_ASSIGN
+Var_Parse: ${COUNTER::=${COUNTER} a}${COUNTER:[#]} with VARE_WANTRES|VARE_ASSIGN
+Applying ${COUNTER::...} to "" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none)
+Var_Parse: ${COUNTER} a}${COUNTER:[#]} with VARE_WANTRES
+Modifier part: " a"
+Global:COUNTER = a
+Result of ${COUNTER::=${COUNTER} a} is "" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none)
+Var_Parse: ${COUNTER} a}${COUNTER:[#]} with VARE_WANTRES|VARE_ASSIGN
+Var_Parse: ${COUNTER:[#]} with VARE_WANTRES|VARE_ASSIGN
+Applying ${COUNTER:[...} to " a" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none)
+Modifier part: "#"
+Result of ${COUNTER:[#]} is "1" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none)
+Global:A = ${COUNTER::= a a}1
+Global:B =
+Var_Parse: ${NEXT} with VARE_WANTRES|VARE_ASSIGN
+Var_Parse: ${COUNTER::=${COUNTER} a}${COUNTER:[#]} with VARE_WANTRES|VARE_ASSIGN
+Applying ${COUNTER::...} to " a" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none)
+Var_Parse: ${COUNTER} a}${COUNTER:[#]} with VARE_WANTRES
+Modifier part: " a a"
+Global:COUNTER = a a
+Result of ${COUNTER::=${COUNTER} a} is "" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none)
+Var_Parse: ${COUNTER} a}${COUNTER:[#]} with VARE_WANTRES|VARE_ASSIGN
+Var_Parse: ${COUNTER:[#]} with VARE_WANTRES|VARE_ASSIGN
+Applying ${COUNTER:[...} to " a a" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none)
+Modifier part: "#"
+Result of ${COUNTER:[#]} is "2" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none)
+Global:B = ${COUNTER::= a a a}2
+Global:C =
+Var_Parse: ${NEXT} with VARE_WANTRES|VARE_ASSIGN
+Var_Parse: ${COUNTER::=${COUNTER} a}${COUNTER:[#]} with VARE_WANTRES|VARE_ASSIGN
+Applying ${COUNTER::...} to " a a" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none)
+Var_Parse: ${COUNTER} a}${COUNTER:[#]} with VARE_WANTRES
+Modifier part: " a a a"
+Global:COUNTER = a a a
+Result of ${COUNTER::=${COUNTER} a} is "" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none)
+Var_Parse: ${COUNTER} a}${COUNTER:[#]} with VARE_WANTRES|VARE_ASSIGN
+Var_Parse: ${COUNTER:[#]} with VARE_WANTRES|VARE_ASSIGN
+Applying ${COUNTER:[...} to " a a a" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none)
+Modifier part: "#"
+Result of ${COUNTER:[#]} is "3" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none)
+Global:C = ${COUNTER::= a a a a}3
+Global:RELEVANT = no
+Global:RELEVANT = yes (run-time part)
+Result of ${RELEVANT::=yes (run-time part)} is "" (eflags = VARE_WANTRES, vflags = none)
+Var_Parse: ${A:Q} B=${B:Q} C=${C:Q} COUNTER=${COUNTER:[#]:Q} with VARE_WANTRES
+Var_Parse: ${COUNTER::= a a}1 with VARE_WANTRES
+Applying ${COUNTER::...} to " a a a" (eflags = VARE_WANTRES, vflags = none)
+Modifier part: " a a"
+Global:COUNTER = a a
+Result of ${COUNTER::= a a} is "" (eflags = VARE_WANTRES, vflags = none)
+Applying ${A:Q} to "1" (eflags = VARE_WANTRES, vflags = none)
+QuoteMeta: [1]
+Result of ${A:Q} is "1" (eflags = VARE_WANTRES, vflags = none)
+Var_Parse: ${B:Q} C=${C:Q} COUNTER=${COUNTER:[#]:Q} with VARE_WANTRES
+Var_Parse: ${COUNTER::= a a a}2 with VARE_WANTRES
+Applying ${COUNTER::...} to " a a" (eflags = VARE_WANTRES, vflags = none)
+Modifier part: " a a a"
+Global:COUNTER = a a a
+Result of ${COUNTER::= a a a} is "" (eflags = VARE_WANTRES, vflags = none)
+Applying ${B:Q} to "2" (eflags = VARE_WANTRES, vflags = none)
+QuoteMeta: [2]
+Result of ${B:Q} is "2" (eflags = VARE_WANTRES, vflags = none)
+Var_Parse: ${C:Q} COUNTER=${COUNTER:[#]:Q} with VARE_WANTRES
+Var_Parse: ${COUNTER::= a a a a}3 with VARE_WANTRES
+Applying ${COUNTER::...} to " a a a" (eflags = VARE_WANTRES, vflags = none)
+Modifier part: " a a a a"
+Global:COUNTER = a a a a
+Result of ${COUNTER::= a a a a} is "" (eflags = VARE_WANTRES, vflags = none)
+Applying ${C:Q} to "3" (eflags = VARE_WANTRES, vflags = none)
+QuoteMeta: [3]
+Result of ${C:Q} is "3" (eflags = VARE_WANTRES, vflags = none)
+Var_Parse: ${COUNTER:[#]:Q} with VARE_WANTRES
+Applying ${COUNTER:[...} to " a a a a" (eflags = VARE_WANTRES, vflags = none)
+Modifier part: "#"
+Result of ${COUNTER:[#]} is "4" (eflags = VARE_WANTRES, vflags = none)
+Applying ${COUNTER:Q} to "4" (eflags = VARE_WANTRES, vflags = none)
+QuoteMeta: [4]
+Result of ${COUNTER:Q} is "4" (eflags = VARE_WANTRES, vflags = none)
+A=1 B=2 C=3 COUNTER=4
+Var_Parse: ${RELEVANT::=no} with VARE_WANTRES
+Applying ${RELEVANT::...} to "yes (run-time part)" (eflags = VARE_WANTRES, vflags = none)
+Modifier part: "no"
+Global:RELEVANT = no
+exit status 0
diff --git a/contrib/bmake/unit-tests/counter.mk b/contrib/bmake/unit-tests/counter.mk
new file mode 100644
index 000000000000..23e7dcc0e8d2
--- /dev/null
+++ b/contrib/bmake/unit-tests/counter.mk
@@ -0,0 +1,31 @@
+# $NetBSD: counter.mk,v 1.1 2020/08/02 14:53:02 rillig Exp $
+#
+# Demonstrates that it is not easily possible to let make count
+# the number of times a variable is actually accessed.
+#
+# As of 2020-08-02, the counter ends up at having 4 words, even
+# though the NEXT variable is only accessed 3 times. This is
+# surprising.
+#
+# A hint to this surprising behavior is that the variables don't
+# get fully expanded. For example, A does not simply contain the
+# value "1" but an additional unexpanded ${COUNTER:...} before it.
+
+RELEVANT= yes (load-time part) # just to filter the output
+
+COUNTER= # zero
+
+NEXT= ${COUNTER::=${COUNTER} a}${COUNTER:[#]}
+
+# This variable is first set to empty and then expanded.
+# See parse.c, function Parse_DoVar, keyword "!Var_Exists".
+A:= ${NEXT}
+B:= ${NEXT}
+C:= ${NEXT}
+
+RELEVANT= no
+
+all:
+ @: ${RELEVANT::=yes (run-time part)}
+ @echo A=${A:Q} B=${B:Q} C=${C:Q} COUNTER=${COUNTER:[#]:Q}
+ @: ${RELEVANT::=no}
diff --git a/contrib/bmake/unit-tests/dep-colon.exp b/contrib/bmake/unit-tests/dep-colon.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/dep-colon.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/dep-colon.mk b/contrib/bmake/unit-tests/dep-colon.mk
new file mode 100644
index 000000000000..7355c59ae557
--- /dev/null
+++ b/contrib/bmake/unit-tests/dep-colon.mk
@@ -0,0 +1,8 @@
+# $NetBSD: dep-colon.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the : operator in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/dep-double-colon.exp b/contrib/bmake/unit-tests/dep-double-colon.exp
new file mode 100644
index 000000000000..5528abbab32d
--- /dev/null
+++ b/contrib/bmake/unit-tests/dep-double-colon.exp
@@ -0,0 +1,5 @@
+command 1a
+command 1b
+command 2a
+command 2b
+exit status 0
diff --git a/contrib/bmake/unit-tests/dep-double-colon.mk b/contrib/bmake/unit-tests/dep-double-colon.mk
new file mode 100644
index 000000000000..de4cd9bc9e33
--- /dev/null
+++ b/contrib/bmake/unit-tests/dep-double-colon.mk
@@ -0,0 +1,11 @@
+# $NetBSD: dep-double-colon.mk,v 1.3 2020/08/22 12:42:32 rillig Exp $
+#
+# Tests for the :: operator in dependency declarations.
+
+all::
+ @echo 'command 1a'
+ @echo 'command 1b'
+
+all::
+ @echo 'command 2a'
+ @echo 'command 2b'
diff --git a/contrib/bmake/unit-tests/dep-exclam.exp b/contrib/bmake/unit-tests/dep-exclam.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/dep-exclam.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/dep-exclam.mk b/contrib/bmake/unit-tests/dep-exclam.mk
new file mode 100644
index 000000000000..2779a66ea6e3
--- /dev/null
+++ b/contrib/bmake/unit-tests/dep-exclam.mk
@@ -0,0 +1,8 @@
+# $NetBSD: dep-exclam.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the ! operator in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/dep-none.exp b/contrib/bmake/unit-tests/dep-none.exp
new file mode 100755
index 000000000000..0d299fcca8d3
--- /dev/null
+++ b/contrib/bmake/unit-tests/dep-none.exp
@@ -0,0 +1,4 @@
+make: no target to make.
+
+make: stopped in unit-tests
+exit status 2
diff --git a/contrib/bmake/unit-tests/dep-none.mk b/contrib/bmake/unit-tests/dep-none.mk
new file mode 100755
index 000000000000..74b50aa29051
--- /dev/null
+++ b/contrib/bmake/unit-tests/dep-none.mk
@@ -0,0 +1,3 @@
+# $NetBSD: dep-none.mk,v 1.1 2020/08/22 12:51:11 rillig Exp $
+#
+# Test for a Makefile that declares no target at all.
diff --git a/contrib/bmake/unit-tests/dep-var.exp b/contrib/bmake/unit-tests/dep-var.exp
new file mode 100755
index 000000000000..819a939ed70a
--- /dev/null
+++ b/contrib/bmake/unit-tests/dep-var.exp
@@ -0,0 +1,2 @@
+def2
+exit status 0
diff --git a/contrib/bmake/unit-tests/dep-var.mk b/contrib/bmake/unit-tests/dep-var.mk
new file mode 100755
index 000000000000..b0ce4b2c53ae
--- /dev/null
+++ b/contrib/bmake/unit-tests/dep-var.mk
@@ -0,0 +1,33 @@
+# $NetBSD: dep-var.mk,v 1.1 2020/08/22 16:51:26 rillig Exp $
+#
+# Tests for variable references in dependency declarations.
+#
+# Uh oh, this feels so strange that probably nobody uses it. But it seems to
+# be the only way to reach the lower half of SuffExpandChildren.
+
+# XXX: The -dv log says:
+# Var_Parse: ${UNDEF1} with VARE_UNDEFERR|VARE_WANTRES
+# but no error message is generated for this line.
+# The variable expression ${UNDEF1} simply expands to an empty string.
+all: ${UNDEF1}
+
+# Using a double dollar in order to circumvent immediate variable expansion
+# feels like unintended behavior. At least the manual page says nothing at
+# all about defined or undefined variables in dependency lines.
+#
+# At the point where the expression ${DEF2} is expanded, the variable DEF2
+# is defined, so everything's fine.
+all: $${DEF2}
+
+# This variable is not defined at all.
+# XXX: The -dv log says:
+# Var_Parse: ${UNDEF3} with VARE_UNDEFERR|VARE_WANTRES
+# but no error message is generated for this line, just like for UNDEF1.
+# The variable expression ${UNDEF3} simply expands to an empty string.
+all: $${UNDEF3}
+
+UNDEF1= undef1
+DEF2= def2
+
+undef1 def2:
+ @echo ${.TARGET}
diff --git a/contrib/bmake/unit-tests/dep-wildcards.exp b/contrib/bmake/unit-tests/dep-wildcards.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/dep-wildcards.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/dep-wildcards.mk b/contrib/bmake/unit-tests/dep-wildcards.mk
new file mode 100644
index 000000000000..c557efab3ebc
--- /dev/null
+++ b/contrib/bmake/unit-tests/dep-wildcards.mk
@@ -0,0 +1,8 @@
+# $NetBSD: dep-wildcards.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for wildcards such as *.c in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/dep.exp b/contrib/bmake/unit-tests/dep.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/dep.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/dep.mk b/contrib/bmake/unit-tests/dep.mk
new file mode 100644
index 000000000000..b2463dfc6458
--- /dev/null
+++ b/contrib/bmake/unit-tests/dep.mk
@@ -0,0 +1,8 @@
+# $NetBSD: dep.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for dependency declarations, such as "target: sources".
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/depsrc-exec.exp b/contrib/bmake/unit-tests/depsrc-exec.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-exec.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/depsrc-exec.mk b/contrib/bmake/unit-tests/depsrc-exec.mk
new file mode 100644
index 000000000000..a71284d33b3c
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-exec.mk
@@ -0,0 +1,8 @@
+# $NetBSD: depsrc-exec.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special source .EXEC in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/depsrc-ignore.exp b/contrib/bmake/unit-tests/depsrc-ignore.exp
new file mode 100644
index 000000000000..162f10ddc17b
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-ignore.exp
@@ -0,0 +1,11 @@
+ignore-errors begin
+false ignore-errors
+ignore-errors end
+all begin
+*** Error code 1 (ignored)
+false all
+*** Error code 1 (continuing)
+
+Stop.
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/depsrc-ignore.mk b/contrib/bmake/unit-tests/depsrc-ignore.mk
new file mode 100644
index 000000000000..1be3eabe8806
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-ignore.mk
@@ -0,0 +1,67 @@
+# $NetBSD: depsrc-ignore.mk,v 1.4 2020/08/29 16:13:27 rillig Exp $
+#
+# Tests for the special source .IGNORE in dependency declarations,
+# which ignores any command failures for that target.
+#
+# Even though ignore-errors fails, the all target is still made.
+# Since the all target is not marked with .IGNORE, it stops at the
+# first failing command.
+#
+# XXX: The ordering of the messages in the output is confusing.
+# The "ignored" comes much too late to be related to the "false
+# ignore-errors". This is due to stdout being buffered.
+#
+# The "continuing" message comes from the -k option. If there had been
+# other targets independent of "all", these would be built as well.
+#
+# Enabling the debugging option -de changes the order in which the messages
+# appear. Now the "ignored" message is issued in the correct position.
+# The explanation for the output reordering is that the output is buffered.
+# As the manual page says, in debugging mode stdout is line buffered.
+# In these tests the output is redirected to a file, therefore stdout is
+# fully buffered.
+#
+# This is what actually happens, as of 2020-08-29. To verify it, set the
+# following breakpoints in CompatRunCommand:
+#
+# * the "!silent" line, to see all commands.
+# * the "fflush" line, to see stdout being flushed.
+# * the "status = WEXITSTATUS" line
+# * the "(continuing)" line
+# * the "(ignored)" line
+#
+# The breakpoints are visited in the following order:
+#
+# "ignore-errors begin"
+# Goes directly to STDOUT_FILENO since it is run in a child process.
+# "false ignore-errors"
+# Goes to the stdout buffer (CompatRunCommand, keyword "!silent") and
+# the immediate call to fflush(stdout) copies it to STDOUT_FILENO.
+# "*** Error code 1 (ignored)"
+# Goes to the stdout buffer but is not flushed (CompatRunCommand, near
+# the end).
+# "ignore-errors end"
+# Goes directly to STDOUT_FILENO.
+# "all begin"
+# Goes directly to STDOUT_FILENO.
+# "false all"
+# Goes to the stdout buffer, where the "*** Error code 1 (ignored)" is
+# still waiting to be flushed. These two lines are flushed now.
+# "*** Error code 1 (continuing)"
+# Goes to the stdout buffer.
+# "Stop."
+# Goes to the stdout buffer.
+# exit(1)
+# Flushes the stdout buffer to STDOUT_FILENO.
+
+all: ignore-errors
+
+ignore-errors: .IGNORE
+ @echo $@ begin
+ false $@
+ @echo $@ end
+
+all:
+ @echo $@ begin
+ false $@
+ @echo $@ end
diff --git a/contrib/bmake/unit-tests/depsrc-made.exp b/contrib/bmake/unit-tests/depsrc-made.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-made.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/depsrc-made.mk b/contrib/bmake/unit-tests/depsrc-made.mk
new file mode 100644
index 000000000000..9adeb2487496
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-made.mk
@@ -0,0 +1,8 @@
+# $NetBSD: depsrc-made.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special source .MADE in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/depsrc-make.exp b/contrib/bmake/unit-tests/depsrc-make.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-make.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/depsrc-make.mk b/contrib/bmake/unit-tests/depsrc-make.mk
new file mode 100644
index 000000000000..9631f3e913d0
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-make.mk
@@ -0,0 +1,8 @@
+# $NetBSD: depsrc-make.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special source .MAKE in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/depsrc-meta.exp b/contrib/bmake/unit-tests/depsrc-meta.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-meta.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/depsrc-meta.mk b/contrib/bmake/unit-tests/depsrc-meta.mk
new file mode 100644
index 000000000000..6adef4baa5de
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-meta.mk
@@ -0,0 +1,8 @@
+# $NetBSD: depsrc-meta.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special source .META in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/depsrc-nometa.exp b/contrib/bmake/unit-tests/depsrc-nometa.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-nometa.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/depsrc-nometa.mk b/contrib/bmake/unit-tests/depsrc-nometa.mk
new file mode 100644
index 000000000000..1942383856ab
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-nometa.mk
@@ -0,0 +1,8 @@
+# $NetBSD: depsrc-nometa.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special source .NOMETA in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/depsrc-nometa_cmp.exp b/contrib/bmake/unit-tests/depsrc-nometa_cmp.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-nometa_cmp.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/depsrc-nometa_cmp.mk b/contrib/bmake/unit-tests/depsrc-nometa_cmp.mk
new file mode 100644
index 000000000000..a0032d2c812a
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-nometa_cmp.mk
@@ -0,0 +1,8 @@
+# $NetBSD: depsrc-nometa_cmp.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special source .NOMETA_CMP in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/depsrc-nopath.exp b/contrib/bmake/unit-tests/depsrc-nopath.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-nopath.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/depsrc-nopath.mk b/contrib/bmake/unit-tests/depsrc-nopath.mk
new file mode 100644
index 000000000000..052c6f10db66
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-nopath.mk
@@ -0,0 +1,8 @@
+# $NetBSD: depsrc-nopath.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special source .NOPATH in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/depsrc-notmain.exp b/contrib/bmake/unit-tests/depsrc-notmain.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-notmain.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/depsrc-notmain.mk b/contrib/bmake/unit-tests/depsrc-notmain.mk
new file mode 100644
index 000000000000..6e4a68c3b70d
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-notmain.mk
@@ -0,0 +1,8 @@
+# $NetBSD: depsrc-notmain.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special source .NOTMAIN in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/depsrc-optional.exp b/contrib/bmake/unit-tests/depsrc-optional.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-optional.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/depsrc-optional.mk b/contrib/bmake/unit-tests/depsrc-optional.mk
new file mode 100644
index 000000000000..074dd0b4ef8f
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-optional.mk
@@ -0,0 +1,8 @@
+# $NetBSD: depsrc-optional.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special source .OPTIONAL in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/depsrc-phony.exp b/contrib/bmake/unit-tests/depsrc-phony.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-phony.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/depsrc-phony.mk b/contrib/bmake/unit-tests/depsrc-phony.mk
new file mode 100644
index 000000000000..73186e81a5f9
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-phony.mk
@@ -0,0 +1,8 @@
+# $NetBSD: depsrc-phony.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special source .PHONY in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/depsrc-precious.exp b/contrib/bmake/unit-tests/depsrc-precious.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-precious.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/depsrc-precious.mk b/contrib/bmake/unit-tests/depsrc-precious.mk
new file mode 100644
index 000000000000..699b83d767b1
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-precious.mk
@@ -0,0 +1,8 @@
+# $NetBSD: depsrc-precious.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special source .PRECIOUS in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/depsrc-recursive.exp b/contrib/bmake/unit-tests/depsrc-recursive.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-recursive.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/depsrc-recursive.mk b/contrib/bmake/unit-tests/depsrc-recursive.mk
new file mode 100644
index 000000000000..5c629338bc8d
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-recursive.mk
@@ -0,0 +1,8 @@
+# $NetBSD: depsrc-recursive.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special source .RECURSIVE in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/depsrc-silent.exp b/contrib/bmake/unit-tests/depsrc-silent.exp
new file mode 100644
index 000000000000..1ea493702c3b
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-silent.exp
@@ -0,0 +1,4 @@
+one
+two
+three
+exit status 0
diff --git a/contrib/bmake/unit-tests/depsrc-silent.mk b/contrib/bmake/unit-tests/depsrc-silent.mk
new file mode 100644
index 000000000000..98da7b387906
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-silent.mk
@@ -0,0 +1,12 @@
+# $NetBSD: depsrc-silent.mk,v 1.3 2020/08/29 17:34:21 rillig Exp $
+#
+# Tests for the special source .SILENT in dependency declarations,
+# which hides the commands, no matter whether they are prefixed with
+# '@' or not.
+
+# Without the .SILENT, the commands 'echo one' and 'echo two' would be
+# written to stdout.
+all: .SILENT
+ echo one
+ echo two
+ @echo three
diff --git a/contrib/bmake/unit-tests/depsrc-use.exp b/contrib/bmake/unit-tests/depsrc-use.exp
new file mode 100644
index 000000000000..c9810bda462d
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-use.exp
@@ -0,0 +1,6 @@
+first 1
+first 2
+second 1
+second 2
+directly
+exit status 0
diff --git a/contrib/bmake/unit-tests/depsrc-use.mk b/contrib/bmake/unit-tests/depsrc-use.mk
new file mode 100644
index 000000000000..17836cd39e23
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-use.mk
@@ -0,0 +1,24 @@
+# $NetBSD: depsrc-use.mk,v 1.4 2020/08/22 12:30:57 rillig Exp $
+#
+# Tests for the special source .USE in dependency declarations,
+# which allows to append common commands to other targets.
+
+all: action directly
+
+first: .USE
+ @echo first 1 # Using ${.TARGET} here would expand to "action"
+ @echo first 2
+
+second: .USE
+ @echo second 1
+ @echo second 2
+
+# It's possible but uncommon to have a .USE target with no commands.
+# This may happen as the result of expanding a .for loop.
+empty: .USE
+
+# It's possible but uncommon to directly make a .USEBEFORE target.
+directly: .USE
+ @echo directly
+
+action: first second empty
diff --git a/contrib/bmake/unit-tests/depsrc-usebefore-double-colon.exp b/contrib/bmake/unit-tests/depsrc-usebefore-double-colon.exp
new file mode 100755
index 000000000000..8bbdef5ad326
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-usebefore-double-colon.exp
@@ -0,0 +1,2 @@
+double-colon early 1
+exit status 0
diff --git a/contrib/bmake/unit-tests/depsrc-usebefore-double-colon.mk b/contrib/bmake/unit-tests/depsrc-usebefore-double-colon.mk
new file mode 100755
index 000000000000..448214112d32
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-usebefore-double-colon.mk
@@ -0,0 +1,30 @@
+# $NetBSD: depsrc-usebefore-double-colon.mk,v 1.1 2020/08/22 08:29:13 rillig Exp $
+#
+# Tests for the special source .USEBEFORE in dependency declarations,
+# combined with the double-colon dependency operator.
+
+all: action
+
+# The dependency operator :: allows commands to be added later to the same
+# target.
+double-colon:: .USEBEFORE
+ @echo double-colon early 1
+
+# This command is ignored, which kind of makes sense since this dependency
+# declaration has no .USEBEFORE source.
+double-colon::
+ @echo double-colon early 2
+
+# XXX: This command is ignored even though it has a .USEBEFORE source.
+# This is unexpected.
+double-colon:: .USEBEFORE
+ @echo double-colon early 3
+
+# At this point, the commands from the .USEBEFORE targets are copied to
+# the "action" target.
+action: double-colon
+
+# This command is not added to the "action" target since it comes too late.
+# The commands had been copied in the previous line already.
+double-colon::
+ @echo double-colon late
diff --git a/contrib/bmake/unit-tests/depsrc-usebefore.exp b/contrib/bmake/unit-tests/depsrc-usebefore.exp
new file mode 100644
index 000000000000..c9810bda462d
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-usebefore.exp
@@ -0,0 +1,6 @@
+first 1
+first 2
+second 1
+second 2
+directly
+exit status 0
diff --git a/contrib/bmake/unit-tests/depsrc-usebefore.mk b/contrib/bmake/unit-tests/depsrc-usebefore.mk
new file mode 100644
index 000000000000..c6be2bae0a9c
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-usebefore.mk
@@ -0,0 +1,24 @@
+# $NetBSD: depsrc-usebefore.mk,v 1.5 2020/08/22 11:53:18 rillig Exp $
+#
+# Tests for the special source .USEBEFORE in dependency declarations,
+# which allows to prepend common commands to other targets.
+
+all: action directly
+
+first: .USEBEFORE
+ @echo first 1 # Using ${.TARGET} here would expand to "action"
+ @echo first 2 # Using ${.TARGET} here would expand to "action"
+
+second: .USEBEFORE
+ @echo second 1
+ @echo second 2
+
+# It is possible but uncommon to have a .USEBEFORE target with no commands.
+# This may happen as the result of expanding a .for loop.
+empty: .USEBEFORE
+
+# It is possible but uncommon to directly make a .USEBEFORE target.
+directly: .USEBEFORE
+ @echo directly
+
+action: second first empty
diff --git a/contrib/bmake/unit-tests/depsrc-wait.exp b/contrib/bmake/unit-tests/depsrc-wait.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-wait.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/depsrc-wait.mk b/contrib/bmake/unit-tests/depsrc-wait.mk
new file mode 100644
index 000000000000..bdf3be4608bb
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc-wait.mk
@@ -0,0 +1,8 @@
+# $NetBSD: depsrc-wait.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special source .WAIT in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/depsrc.exp b/contrib/bmake/unit-tests/depsrc.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/depsrc.mk b/contrib/bmake/unit-tests/depsrc.mk
new file mode 100644
index 000000000000..d461e1111d0f
--- /dev/null
+++ b/contrib/bmake/unit-tests/depsrc.mk
@@ -0,0 +1,9 @@
+# $NetBSD: depsrc.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for special sources (those starting with a dot, followed by
+# uppercase letters) in dependency declarations, such as .PHONY.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/deptgt-begin.exp b/contrib/bmake/unit-tests/deptgt-begin.exp
new file mode 100644
index 000000000000..527020f6818d
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-begin.exp
@@ -0,0 +1,4 @@
+: parse time
+: .BEGIN
+: all
+exit status 0
diff --git a/contrib/bmake/unit-tests/deptgt-begin.mk b/contrib/bmake/unit-tests/deptgt-begin.mk
new file mode 100644
index 000000000000..c6ca2f4aa3c7
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-begin.mk
@@ -0,0 +1,13 @@
+# $NetBSD: deptgt-begin.mk,v 1.3 2020/08/29 17:34:21 rillig Exp $
+#
+# Tests for the special target .BEGIN in dependency declarations,
+# which is a container for commands that are run before any other
+# commands from the shell lines.
+
+.BEGIN:
+ : $@
+
+all:
+ : $@
+
+_!= echo : parse time 1>&2
diff --git a/contrib/bmake/unit-tests/deptgt-default.exp b/contrib/bmake/unit-tests/deptgt-default.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-default.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/deptgt-default.mk b/contrib/bmake/unit-tests/deptgt-default.mk
new file mode 100644
index 000000000000..814eaf72aed3
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-default.mk
@@ -0,0 +1,8 @@
+# $NetBSD: deptgt-default.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special target .DEFAULT in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/deptgt-delete_on_error.exp b/contrib/bmake/unit-tests/deptgt-delete_on_error.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-delete_on_error.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/deptgt-delete_on_error.mk b/contrib/bmake/unit-tests/deptgt-delete_on_error.mk
new file mode 100644
index 000000000000..476bb8a54c52
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-delete_on_error.mk
@@ -0,0 +1,8 @@
+# $NetBSD: deptgt-delete_on_error.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special target .DELETE_ON_ERROR in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/deptgt-end.exp b/contrib/bmake/unit-tests/deptgt-end.exp
new file mode 100644
index 000000000000..3effe2e3182f
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-end.exp
@@ -0,0 +1,4 @@
+: .BEGIN
+: all
+: .END
+exit status 0
diff --git a/contrib/bmake/unit-tests/deptgt-end.mk b/contrib/bmake/unit-tests/deptgt-end.mk
new file mode 100644
index 000000000000..b3d59a610a2f
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-end.mk
@@ -0,0 +1,13 @@
+# $NetBSD: deptgt-end.mk,v 1.3 2020/08/29 17:34:21 rillig Exp $
+#
+# Tests for the special target .END in dependency declarations,
+# which is run after making the desired targets.
+
+.BEGIN:
+ : $@
+
+.END:
+ : $@
+
+all:
+ : $@
diff --git a/contrib/bmake/unit-tests/deptgt-error.exp b/contrib/bmake/unit-tests/deptgt-error.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-error.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/deptgt-error.mk b/contrib/bmake/unit-tests/deptgt-error.mk
new file mode 100644
index 000000000000..07bc1b5d3408
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-error.mk
@@ -0,0 +1,8 @@
+# $NetBSD: deptgt-error.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special target .ERROR in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/deptgt-ignore.exp b/contrib/bmake/unit-tests/deptgt-ignore.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-ignore.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/deptgt-ignore.mk b/contrib/bmake/unit-tests/deptgt-ignore.mk
new file mode 100644
index 000000000000..6ace0841f28b
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-ignore.mk
@@ -0,0 +1,8 @@
+# $NetBSD: deptgt-ignore.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special target .IGNORE in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/deptgt-interrupt.exp b/contrib/bmake/unit-tests/deptgt-interrupt.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-interrupt.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/deptgt-interrupt.mk b/contrib/bmake/unit-tests/deptgt-interrupt.mk
new file mode 100644
index 000000000000..d555f864563e
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-interrupt.mk
@@ -0,0 +1,8 @@
+# $NetBSD: deptgt-interrupt.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special target .INTERRUPT in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/deptgt-main.exp b/contrib/bmake/unit-tests/deptgt-main.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-main.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/deptgt-main.mk b/contrib/bmake/unit-tests/deptgt-main.mk
new file mode 100644
index 000000000000..cf1b1b4fd340
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-main.mk
@@ -0,0 +1,8 @@
+# $NetBSD: deptgt-main.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special target .MAIN in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/deptgt-makeflags.exp b/contrib/bmake/unit-tests/deptgt-makeflags.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-makeflags.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/deptgt-makeflags.mk b/contrib/bmake/unit-tests/deptgt-makeflags.mk
new file mode 100644
index 000000000000..958751b64cfa
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-makeflags.mk
@@ -0,0 +1,8 @@
+# $NetBSD: deptgt-makeflags.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special target .MAKEFLAGS in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/deptgt-no_parallel.exp b/contrib/bmake/unit-tests/deptgt-no_parallel.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-no_parallel.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/deptgt-no_parallel.mk b/contrib/bmake/unit-tests/deptgt-no_parallel.mk
new file mode 100644
index 000000000000..247f6a9ac8a5
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-no_parallel.mk
@@ -0,0 +1,8 @@
+# $NetBSD: deptgt-no_parallel.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special target .NO_PARALLEL in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/deptgt-nopath.exp b/contrib/bmake/unit-tests/deptgt-nopath.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-nopath.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/deptgt-nopath.mk b/contrib/bmake/unit-tests/deptgt-nopath.mk
new file mode 100644
index 000000000000..a8cde2657dde
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-nopath.mk
@@ -0,0 +1,8 @@
+# $NetBSD: deptgt-nopath.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special target .NOPATH in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/deptgt-notparallel.exp b/contrib/bmake/unit-tests/deptgt-notparallel.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-notparallel.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/deptgt-notparallel.mk b/contrib/bmake/unit-tests/deptgt-notparallel.mk
new file mode 100644
index 000000000000..db08aa9d3558
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-notparallel.mk
@@ -0,0 +1,8 @@
+# $NetBSD: deptgt-notparallel.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special target .NOTPARALLEL in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/deptgt-objdir.exp b/contrib/bmake/unit-tests/deptgt-objdir.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-objdir.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/deptgt-objdir.mk b/contrib/bmake/unit-tests/deptgt-objdir.mk
new file mode 100644
index 000000000000..f5e9a15d8cb3
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-objdir.mk
@@ -0,0 +1,8 @@
+# $NetBSD: deptgt-objdir.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special target .OBJDIR in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/deptgt-order.exp b/contrib/bmake/unit-tests/deptgt-order.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-order.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/deptgt-order.mk b/contrib/bmake/unit-tests/deptgt-order.mk
new file mode 100644
index 000000000000..003552f57a49
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-order.mk
@@ -0,0 +1,8 @@
+# $NetBSD: deptgt-order.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special target .ORDER in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/deptgt-path-suffix.exp b/contrib/bmake/unit-tests/deptgt-path-suffix.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-path-suffix.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/deptgt-path-suffix.mk b/contrib/bmake/unit-tests/deptgt-path-suffix.mk
new file mode 100644
index 000000000000..3a7e697bc748
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-path-suffix.mk
@@ -0,0 +1,8 @@
+# $NetBSD: deptgt-path-suffix.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special target .PATH.suffix in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/deptgt-path.exp b/contrib/bmake/unit-tests/deptgt-path.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-path.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/deptgt-path.mk b/contrib/bmake/unit-tests/deptgt-path.mk
new file mode 100644
index 000000000000..f74d1a46d1b1
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-path.mk
@@ -0,0 +1,8 @@
+# $NetBSD: deptgt-path.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special target .PATH in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/deptgt-phony.exp b/contrib/bmake/unit-tests/deptgt-phony.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-phony.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/deptgt-phony.mk b/contrib/bmake/unit-tests/deptgt-phony.mk
new file mode 100644
index 000000000000..7f9909fa89a2
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-phony.mk
@@ -0,0 +1,8 @@
+# $NetBSD: deptgt-phony.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special target .PHONY in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/deptgt-precious.exp b/contrib/bmake/unit-tests/deptgt-precious.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-precious.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/deptgt-precious.mk b/contrib/bmake/unit-tests/deptgt-precious.mk
new file mode 100644
index 000000000000..07b2bf719a4f
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-precious.mk
@@ -0,0 +1,8 @@
+# $NetBSD: deptgt-precious.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special target .PRECIOUS in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/deptgt-shell.exp b/contrib/bmake/unit-tests/deptgt-shell.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-shell.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/deptgt-shell.mk b/contrib/bmake/unit-tests/deptgt-shell.mk
new file mode 100644
index 000000000000..71b535d89115
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-shell.mk
@@ -0,0 +1,8 @@
+# $NetBSD: deptgt-shell.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special target .SHELL in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/deptgt-silent.exp b/contrib/bmake/unit-tests/deptgt-silent.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-silent.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/deptgt-silent.mk b/contrib/bmake/unit-tests/deptgt-silent.mk
new file mode 100644
index 000000000000..9ae64567fb97
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-silent.mk
@@ -0,0 +1,8 @@
+# $NetBSD: deptgt-silent.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special target .SILENT in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/deptgt-stale.exp b/contrib/bmake/unit-tests/deptgt-stale.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-stale.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/deptgt-stale.mk b/contrib/bmake/unit-tests/deptgt-stale.mk
new file mode 100644
index 000000000000..cfde5a2bfd49
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-stale.mk
@@ -0,0 +1,8 @@
+# $NetBSD: deptgt-stale.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special target .STALE in dependency declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/deptgt-suffixes.exp b/contrib/bmake/unit-tests/deptgt-suffixes.exp
new file mode 100644
index 000000000000..b24f47becdaf
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-suffixes.exp
@@ -0,0 +1,7 @@
+#*** Suffixes:
+# `.custom-null' [1] (SUFF_NULL)
+# To:
+# From:
+# Search Path: . ..
+#*** Transformations:
+exit status 0
diff --git a/contrib/bmake/unit-tests/deptgt-suffixes.mk b/contrib/bmake/unit-tests/deptgt-suffixes.mk
new file mode 100644
index 000000000000..791ff5eb5f03
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-suffixes.mk
@@ -0,0 +1,18 @@
+# $NetBSD: deptgt-suffixes.mk,v 1.3 2020/08/28 04:05:35 rillig Exp $
+#
+# Tests for the special target .SUFFIXES in dependency declarations.
+#
+# See also:
+# varname-dot-includes.mk
+# varname-dot-libs.mk
+
+.MAKEFLAGS: -dg1
+
+.SUFFIXES: .custom-null
+
+# TODO: What is the effect of this? How is it useful?
+.NULL: .custom-null
+.PATH.custom-null: . ..
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/deptgt.exp b/contrib/bmake/unit-tests/deptgt.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/deptgt.mk b/contrib/bmake/unit-tests/deptgt.mk
new file mode 100644
index 000000000000..add21dc0baee
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt.mk
@@ -0,0 +1,9 @@
+# $NetBSD: deptgt.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for special targets like .BEGIN or .SUFFIXES in dependency
+# declarations.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/dir-expand-path.exp b/contrib/bmake/unit-tests/dir-expand-path.exp
new file mode 100644
index 000000000000..74de255298bb
--- /dev/null
+++ b/contrib/bmake/unit-tests/dir-expand-path.exp
@@ -0,0 +1,4 @@
+dir-expand-path.dir.1/file1.src
+dir-expand-path.dir.1/file2.src
+dir-expand-path.dir.2/file3.src
+exit status 0
diff --git a/contrib/bmake/unit-tests/dir-expand-path.mk b/contrib/bmake/unit-tests/dir-expand-path.mk
new file mode 100755
index 000000000000..1ce349cce385
--- /dev/null
+++ b/contrib/bmake/unit-tests/dir-expand-path.mk
@@ -0,0 +1,19 @@
+# $NetBSD: dir-expand-path.mk,v 1.1 2020/08/22 21:55:54 rillig Exp $
+#
+# Tests for filename expansion in the search path.
+
+_!= rm -rf dir-expand-path.dir.*
+_!= mkdir dir-expand-path.dir.1
+_!= mkdir dir-expand-path.dir.2
+_!= touch dir-expand-path.dir.1/file1.src
+_!= touch dir-expand-path.dir.1/file2.src
+_!= touch dir-expand-path.dir.2/file3.src
+
+.PATH: dir-expand-path.dir.1
+.PATH: dir-expand-path.dir.2
+
+all: *.src
+ @printf '%s\n' ${.ALLSRC:O}
+
+.END:
+ @rm -rf dir-expand-path.dir.*
diff --git a/contrib/bmake/unit-tests/dir.exp b/contrib/bmake/unit-tests/dir.exp
new file mode 100644
index 000000000000..874a081e97ab
--- /dev/null
+++ b/contrib/bmake/unit-tests/dir.exp
@@ -0,0 +1,19 @@
+1
+2
+3
+4
+5
+13
+14
+15
+pre-patch
+pre-configure
+patch
+configure
+fetch
+fetch-post
+extract
+extract-post
+dup-1
+single-word
+exit status 0
diff --git a/contrib/bmake/unit-tests/dir.mk b/contrib/bmake/unit-tests/dir.mk
new file mode 100644
index 000000000000..f5926375312e
--- /dev/null
+++ b/contrib/bmake/unit-tests/dir.mk
@@ -0,0 +1,58 @@
+# $NetBSD: dir.mk,v 1.4 2020/07/31 20:16:21 rillig Exp $
+#
+# Tests for dir.c.
+
+# Dependency lines may use braces for expansion.
+all: {one,two,three}
+
+one:
+ @echo 1
+two:
+ @echo 2
+three:
+ @echo 3
+
+# The braces may start in the middle of a word.
+all: f{our,ive}
+
+four:
+ @echo 4
+five:
+ @echo 5
+six:
+ @echo 6
+
+# But nested braces don't work.
+all: {{thi,fou}r,fif}teen
+
+thirteen:
+ @echo 13
+fourteen:
+ @echo 14
+fifteen:
+ @echo 15
+
+# There may be multiple brace groups side by side.
+all: {pre-,}{patch,configure}
+
+pre-patch patch pre-configure configure:
+ @echo $@
+
+# Empty pieces are allowed in the braces.
+all: {fetch,extract}{,-post}
+
+fetch fetch-post extract extract-post:
+ @echo $@
+
+# The expansions may have duplicates.
+# These are merged together because of the dependency line.
+all: dup-{1,1,1,1,1,1,1}
+
+dup-1:
+ @echo $@
+
+# Other than in Bash, the braces are also expanded if there is no comma.
+all: {{{{{{{{{{single-word}}}}}}}}}}
+
+single-word:
+ @echo $@
diff --git a/contrib/bmake/unit-tests/directive-elif.exp b/contrib/bmake/unit-tests/directive-elif.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-elif.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/directive-elif.mk b/contrib/bmake/unit-tests/directive-elif.mk
new file mode 100644
index 000000000000..fc8bb7e7d197
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-elif.mk
@@ -0,0 +1,8 @@
+# $NetBSD: directive-elif.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the .elif directive.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/directive-elifdef.exp b/contrib/bmake/unit-tests/directive-elifdef.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-elifdef.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/directive-elifdef.mk b/contrib/bmake/unit-tests/directive-elifdef.mk
new file mode 100644
index 000000000000..f960c1513e8e
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-elifdef.mk
@@ -0,0 +1,8 @@
+# $NetBSD: directive-elifdef.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the .elifdef directive.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/directive-elifmake.exp b/contrib/bmake/unit-tests/directive-elifmake.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-elifmake.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/directive-elifmake.mk b/contrib/bmake/unit-tests/directive-elifmake.mk
new file mode 100644
index 000000000000..c78e3aa91332
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-elifmake.mk
@@ -0,0 +1,8 @@
+# $NetBSD: directive-elifmake.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the .elifmake directive.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/directive-elifndef.exp b/contrib/bmake/unit-tests/directive-elifndef.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-elifndef.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/directive-elifndef.mk b/contrib/bmake/unit-tests/directive-elifndef.mk
new file mode 100644
index 000000000000..19bb66c11b01
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-elifndef.mk
@@ -0,0 +1,8 @@
+# $NetBSD: directive-elifndef.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the .elifndef directive.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/directive-elifnmake.exp b/contrib/bmake/unit-tests/directive-elifnmake.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-elifnmake.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/directive-elifnmake.mk b/contrib/bmake/unit-tests/directive-elifnmake.mk
new file mode 100644
index 000000000000..11c4b973c1c8
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-elifnmake.mk
@@ -0,0 +1,8 @@
+# $NetBSD: directive-elifnmake.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the .elifnmake directive.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/directive-else.exp b/contrib/bmake/unit-tests/directive-else.exp
new file mode 100644
index 000000000000..387577099b81
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-else.exp
@@ -0,0 +1,8 @@
+make: "directive-else.mk" line 10: ok
+make: "directive-else.mk" line 14: ok
+make: "directive-else.mk" line 20: if-less else
+make: "directive-else.mk" line 26: ok
+make: "directive-else.mk" line 27: warning: extra else
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/directive-else.mk b/contrib/bmake/unit-tests/directive-else.mk
new file mode 100644
index 000000000000..3afa5648af67
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-else.mk
@@ -0,0 +1,32 @@
+# $NetBSD: directive-else.mk,v 1.3 2020/08/29 18:50:25 rillig Exp $
+#
+# Tests for the .else directive.
+
+# The .else directive does not take any arguments.
+# As of 2020-08-29, make doesn't warn about this.
+.if 0
+.warning must not be reached
+.else 123
+.info ok
+.endif
+
+.if 1
+.info ok
+.else 123
+.warning must not be reached
+.endif
+
+# An .else without a corresponding .if is an error.
+.else
+
+# Accidental extra .else directives are detected too.
+.if 0
+.warning must not be reached
+.else
+.info ok
+.else
+.info After an extra .else, everything is skipped.
+.endif
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/directive-endif.exp b/contrib/bmake/unit-tests/directive-endif.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-endif.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/directive-endif.mk b/contrib/bmake/unit-tests/directive-endif.mk
new file mode 100644
index 000000000000..12b608ffbeee
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-endif.mk
@@ -0,0 +1,8 @@
+# $NetBSD: directive-endif.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the .endif directive.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/directive-error.exp b/contrib/bmake/unit-tests/directive-error.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-error.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/directive-error.mk b/contrib/bmake/unit-tests/directive-error.mk
new file mode 100644
index 000000000000..3980016221ab
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-error.mk
@@ -0,0 +1,8 @@
+# $NetBSD: directive-error.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the .error directive.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/directive-export-env.exp b/contrib/bmake/unit-tests/directive-export-env.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-export-env.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/directive-export-env.mk b/contrib/bmake/unit-tests/directive-export-env.mk
new file mode 100644
index 000000000000..49d1edb9f6fe
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-export-env.mk
@@ -0,0 +1,8 @@
+# $NetBSD: directive-export-env.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the .export-env directive.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/directive-export-literal.exp b/contrib/bmake/unit-tests/directive-export-literal.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-export-literal.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/directive-export-literal.mk b/contrib/bmake/unit-tests/directive-export-literal.mk
new file mode 100644
index 000000000000..ed3957b288d6
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-export-literal.mk
@@ -0,0 +1,8 @@
+# $NetBSD: directive-export-literal.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the .export-literal directive.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/directive-export.exp b/contrib/bmake/unit-tests/directive-export.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-export.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/directive-export.mk b/contrib/bmake/unit-tests/directive-export.mk
new file mode 100644
index 000000000000..c7f9181b4b5a
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-export.mk
@@ -0,0 +1,8 @@
+# $NetBSD: directive-export.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the .export directive.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/directive-for-generating-endif.exp b/contrib/bmake/unit-tests/directive-for-generating-endif.exp
new file mode 100755
index 000000000000..9e1301abf96f
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-for-generating-endif.exp
@@ -0,0 +1,7 @@
+make: "directive-for-generating-endif.mk" line 21: if-less endif
+make: "directive-for-generating-endif.mk" line 21: if-less endif
+make: "directive-for-generating-endif.mk" line 21: if-less endif
+make: "directive-for-generating-endif.mk" line 0: 3 open conditionals
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/directive-for-generating-endif.mk b/contrib/bmake/unit-tests/directive-for-generating-endif.mk
new file mode 100755
index 000000000000..b4d709551003
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-for-generating-endif.mk
@@ -0,0 +1,25 @@
+# $NetBSD: directive-for-generating-endif.mk,v 1.1 2020/08/29 18:50:25 rillig Exp $
+#
+# Test whether a .for loop can be used to generate multiple .endif
+# directives to close nested .if directives. Depending on the exact
+# implementation, this might have been possible.
+#
+# If it were possible, the 3 .if directives would perfectly match the
+# 3 .endif directives generated by the .for loop.
+#
+# After the "included file" from the .for loop, the 3 .if directives
+# are still open.
+#
+# See For_Run and ParseReadLine. Each .for loop is treated like a separately
+# included file, and in each included file the .if/.endif directives must be
+# balanced.
+
+.if 1
+. if 2
+. if 3
+.for i in 3 2 1
+.endif
+.endfor
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/directive-for.exp b/contrib/bmake/unit-tests/directive-for.exp
new file mode 100755
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-for.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/directive-for.mk b/contrib/bmake/unit-tests/directive-for.mk
new file mode 100755
index 000000000000..5b3f299e5711
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-for.mk
@@ -0,0 +1,97 @@
+# $NetBSD: directive-for.mk,v 1.2 2020/09/02 22:58:59 rillig Exp $
+#
+# Tests for the .for directive.
+
+# Using the .for loop, lists of values can be produced.
+# In simple cases, the :@var@${var}@ variable modifier can be used to
+# reach the same effects.
+#
+.undef NUMBERS
+.for num in 1 2 3
+NUMBERS+= ${num}
+.endfor
+.if ${NUMBERS} != "1 2 3"
+. error
+.endif
+
+# The .for loop also works for multiple iteration variables.
+.for name value in VARNAME value NAME2 value2
+${name}= ${value}
+.endfor
+.if ${VARNAME} != "value" || ${NAME2} != "value2"
+. error
+.endif
+
+# The .for loop splits the items at whitespace, taking quotes into account,
+# just like the :M or :S variable modifiers.
+#
+# Until 2012-06-03, it had split the items exactly at whitespace, without
+# taking the quotes into account.
+#
+.undef WORDS
+.for var in one t\ w\ o "three three" 'four four' `five six`
+WORDS+= counted
+.endfor
+.if ${WORDS:[#]} != 6
+. error
+.endif
+
+# In the body of the .for loop, the iteration variables can be accessed
+# like normal variables, even though they are not really variables.
+#
+# Instead, the expression ${var} is transformed into ${:U1}, ${:U2} and so
+# on, before the loop body is evaluated.
+#
+# A notable effect of this implementation technique is that the .for
+# iteration variables and the normal global variables live in separate
+# namespaces and do not influence each other.
+#
+var= value before
+var2= value before
+.for var var2 in 1 2 3 4
+.endfor
+.if ${var} != "value before"
+. warning After the .for loop, var must still have its original value.
+.endif
+.if ${var2} != "value before"
+. warning After the .for loop, var2 must still have its original value.
+.endif
+
+# Everything from the paragraph above also applies if the loop body is
+# empty, even if there is no actual iteration since the loop items are
+# also empty.
+#
+var= value before
+var2= value before
+.for var var2 in ${:U}
+.endfor
+.if ${var} != "value before"
+. warning After the .for loop, var must still have its original value.
+.endif
+.if ${var2} != "value before"
+. warning After the .for loop, var2 must still have its original value.
+.endif
+
+# Until 2008-12-21, the values of the iteration variables were simply
+# inserted as plain text and then parsed as usual, which made it possible
+# to achieve all kinds of strange effects.
+#
+# Before that date, the .for loop expanded to:
+# EXPANSION+= value
+# Since that date, the .for loop expands to:
+# EXPANSION${:U+}= value
+#
+EXPANSION= before
+EXPANSION+ = before
+.for plus in +
+EXPANSION${plus}= value
+.endfor
+.if ${EXPANSION} != "before"
+. error This must be a make from before 2009.
+.endif
+.if ${EXPANSION+} != "value"
+. error This must be a make from before 2009.
+.endif
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/directive-if.exp b/contrib/bmake/unit-tests/directive-if.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-if.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/directive-if.mk b/contrib/bmake/unit-tests/directive-if.mk
new file mode 100644
index 000000000000..72d7d0e2d920
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-if.mk
@@ -0,0 +1,8 @@
+# $NetBSD: directive-if.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the .if directive.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/directive-ifdef.exp b/contrib/bmake/unit-tests/directive-ifdef.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-ifdef.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/directive-ifdef.mk b/contrib/bmake/unit-tests/directive-ifdef.mk
new file mode 100644
index 000000000000..64f073fb5ae1
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-ifdef.mk
@@ -0,0 +1,8 @@
+# $NetBSD: directive-ifdef.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the .ifdef directive.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/directive-ifmake.exp b/contrib/bmake/unit-tests/directive-ifmake.exp
new file mode 100644
index 000000000000..5fefeb252b48
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-ifmake.exp
@@ -0,0 +1,10 @@
+make: "directive-ifmake.mk" line 8: ok: positive condition works
+make: "directive-ifmake.mk" line 19: ok: negation works
+make: "directive-ifmake.mk" line 25: ok: double negation works
+make: "directive-ifmake.mk" line 32: ok: both mentioned
+make: "directive-ifmake.mk" line 39: ok: only those mentioned
+make: "directive-ifmake.mk" line 49: Targets can even be added at parse time.
+: first
+: second
+: late-target
+exit status 0
diff --git a/contrib/bmake/unit-tests/directive-ifmake.mk b/contrib/bmake/unit-tests/directive-ifmake.mk
new file mode 100644
index 000000000000..20329bc5ce25
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-ifmake.mk
@@ -0,0 +1,55 @@
+# $NetBSD: directive-ifmake.mk,v 1.4 2020/08/30 14:25:45 rillig Exp $
+#
+# Tests for the .ifmake directive, which provides a shortcut for asking
+# whether a certain target is requested to be made from the command line.
+
+# This is the most basic form.
+.ifmake first
+.info ok: positive condition works
+.else
+.warning positive condition fails
+.endif
+
+# The not operator works as expected.
+# An alternative interpretation were that this condition is asking whether
+# the target "!first" was requested. To distinguish this, see the next test.
+.ifmake !first
+.warning unexpected
+.else
+.info ok: negation works
+.endif
+
+# See if the exclamation mark really means "not", or if it is just part of
+# the target name.
+.ifmake !!first
+.info ok: double negation works
+.else
+.warning double negation fails
+.endif
+
+# Multiple targets can be combined using the && and || operators.
+.ifmake first && second
+.info ok: both mentioned
+.else
+.warning && does not work as expected
+.endif
+
+# Negation also works in complex conditions.
+.ifmake first && !unmentioned
+.info ok: only those mentioned
+.else
+.warning && with ! does not work as expected
+.endif
+
+# Using the .MAKEFLAGS special dependency target, arbitrary command
+# line options can be added at parse time. This means that it is
+# possible to extend the targets to be made.
+.MAKEFLAGS: late-target
+.ifmake late-target
+.info Targets can even be added at parse time.
+.else
+.info No, targets cannot be added at parse time anymore.
+.endif
+
+first second unmentioned late-target:
+ : $@
diff --git a/contrib/bmake/unit-tests/directive-ifndef.exp b/contrib/bmake/unit-tests/directive-ifndef.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-ifndef.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/directive-ifndef.mk b/contrib/bmake/unit-tests/directive-ifndef.mk
new file mode 100644
index 000000000000..0981f817fcfd
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-ifndef.mk
@@ -0,0 +1,8 @@
+# $NetBSD: directive-ifndef.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the .ifndef directive.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/directive-ifnmake.exp b/contrib/bmake/unit-tests/directive-ifnmake.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-ifnmake.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/directive-ifnmake.mk b/contrib/bmake/unit-tests/directive-ifnmake.mk
new file mode 100644
index 000000000000..2a20249f6c76
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-ifnmake.mk
@@ -0,0 +1,8 @@
+# $NetBSD: directive-ifnmake.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the .ifnmake directive.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/directive-info.exp b/contrib/bmake/unit-tests/directive-info.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-info.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/directive-info.mk b/contrib/bmake/unit-tests/directive-info.mk
new file mode 100644
index 000000000000..3eb972ad7a0e
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-info.mk
@@ -0,0 +1,8 @@
+# $NetBSD: directive-info.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the .info directive.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/directive-undef.exp b/contrib/bmake/unit-tests/directive-undef.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-undef.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/directive-undef.mk b/contrib/bmake/unit-tests/directive-undef.mk
new file mode 100644
index 000000000000..ff91eeddb41f
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-undef.mk
@@ -0,0 +1,17 @@
+# $NetBSD: directive-undef.mk,v 1.3 2020/08/23 19:30:13 rillig Exp $
+#
+# Tests for the .undef directive.
+
+# As of 2020-07-28, .undef only undefines the first variable.
+# All further variable names are silently ignored.
+# See parse.c, string literal "undef".
+1= 1
+2= 2
+3= 3
+.undef 1 2 3
+.if ${1:U_}${2:U_}${3:U_} != _23
+.warning $1$2$3
+.endif
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/directive-unexport-env.exp b/contrib/bmake/unit-tests/directive-unexport-env.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-unexport-env.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/directive-unexport-env.mk b/contrib/bmake/unit-tests/directive-unexport-env.mk
new file mode 100644
index 000000000000..34d867d706ef
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-unexport-env.mk
@@ -0,0 +1,8 @@
+# $NetBSD: directive-unexport-env.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the .unexport-env directive.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/directive-unexport.exp b/contrib/bmake/unit-tests/directive-unexport.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-unexport.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/directive-unexport.mk b/contrib/bmake/unit-tests/directive-unexport.mk
new file mode 100644
index 000000000000..679387aac083
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-unexport.mk
@@ -0,0 +1,8 @@
+# $NetBSD: directive-unexport.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the .unexport directive.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/directive-warning.exp b/contrib/bmake/unit-tests/directive-warning.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-warning.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/directive-warning.mk b/contrib/bmake/unit-tests/directive-warning.mk
new file mode 100644
index 000000000000..e1e636e3ec9f
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-warning.mk
@@ -0,0 +1,8 @@
+# $NetBSD: directive-warning.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the .warning directive.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/directive.exp b/contrib/bmake/unit-tests/directive.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/directive.mk b/contrib/bmake/unit-tests/directive.mk
new file mode 100644
index 000000000000..8d01a49a34cf
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive.mk
@@ -0,0 +1,8 @@
+# $NetBSD: directive.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the preprocessing directives, such as .if or .info.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/directives.exp b/contrib/bmake/unit-tests/directives.exp
new file mode 100644
index 000000000000..15dd9aa9e962
--- /dev/null
+++ b/contrib/bmake/unit-tests/directives.exp
@@ -0,0 +1,42 @@
+make: "directives.mk" line 10: begin .export tests
+make: "directives.mk" line 11: Unknown directive "expor"
+make: "directives.mk" line 20: begin .export-env tests
+make: "directives.mk" line 30: begin .export-literal tests
+make: "directives.mk" line 40: begin .info tests
+make: "directives.mk" line 41: Unknown directive "inf"
+make: "directives.mk" line 42: Unknown directive "info"
+make: "directives.mk" line 43: message
+make: "directives.mk" line 44: indented message
+make: "directives.mk" line 45: Unknown directive "information"
+make: "directives.mk" line 46: message
+make: "directives.mk" line 50: begin .undef tests
+make: "directives.mk" line 51: Unknown directive "unde"
+make: "directives.mk" line 60: begin .unexport tests
+make: "directives.mk" line 61: Unknown directive "unexpor"
+make: "directives.mk" line 70: begin .unexport-env tests
+make: "directives.mk" line 80: begin .warning tests
+make: "directives.mk" line 81: Unknown directive "warn"
+make: "directives.mk" line 82: Unknown directive "warnin"
+make: "directives.mk" line 83: Unknown directive "warning"
+make: "directives.mk" line 84: warning: message
+make: "directives.mk" line 85: Unknown directive "warnings"
+make: "directives.mk" line 86: warning: messages
+make: "directives.mk" line 90: begin .elif misspellings tests, part 1
+make: "directives.mk" line 100: begin .elif misspellings tests, part 2
+make: "directives.mk" line 110: begin .elif misspellings tests, part 3
+make: "directives.mk" line 120: which branch is taken on misspelling after false?
+make: "directives.mk" line 127: else taken
+make: "directives.mk" line 130: which branch is taken on misspelling after true?
+make: "directives.mk" line 132: Unknown directive "elsif"
+make: "directives.mk" line 133: 1 taken
+make: "directives.mk" line 134: Unknown directive "elsif"
+make: "directives.mk" line 135: 2 taken
+make: "directives.mk" line 140: Unknown directive "indented"
+make: "directives.mk" line 141: Unknown directive "indented"
+make: "directives.mk" line 142: Unknown directive "indented"
+make: "directives.mk" line 143: Unknown directive "info"
+make: "directives.mk" line 150: Could not find nonexistent.mk
+make: "directives.mk" line 160: end of the tests
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/directives.mk b/contrib/bmake/unit-tests/directives.mk
new file mode 100644
index 000000000000..50e8e1315dda
--- /dev/null
+++ b/contrib/bmake/unit-tests/directives.mk
@@ -0,0 +1,163 @@
+# $NetBSD: directives.mk,v 1.5 2020/07/28 20:57:59 rillig Exp $
+#
+# Tests for parsing directives, in the same order as in the manual page.
+#
+# Each test group has 10 lines, to keep the line numbers in directives.exp
+# stable.
+#
+# no tests for .error since it exits immediately, see ParseMessage.
+
+.info begin .export tests
+.expor # misspelled
+.export # oops: missing argument
+.export VARNAME
+.exporting works # oops: misspelled
+
+
+
+
+
+.info begin .export-env tests
+.export-en # oops: misspelled
+.export-env
+.export-environment # oops: misspelled
+
+
+
+
+
+
+.info begin .export-literal tests
+.export-litera # oops: misspelled
+.export-literal # oops: missing argument
+.export-literal VARNAME
+.export-literally # oops: misspelled
+
+
+
+
+
+.info begin .info tests
+.inf # misspelled
+.info # oops: message should be "missing parameter"
+.info message
+.info indented message
+.information
+.information message # oops: misspelled
+.info.man: # not a message, but a suffix rule
+
+
+.info begin .undef tests
+.unde # misspelled
+.undef # oops: missing argument
+.undefined # oops: misspelled
+.undef VARNAME
+
+
+
+
+
+.info begin .unexport tests
+.unexpor # misspelled
+.unexport # oops: missing argument
+.unexport VARNAME # ok
+.unexporting works # oops: misspelled
+
+
+
+
+
+.info begin .unexport-env tests
+.unexport-en # misspelled
+.unexport-env # ok
+.unexport-environment # oops: misspelled
+
+
+
+
+
+
+.info begin .warning tests
+.warn # misspelled
+.warnin # misspelled
+.warning # oops: should be "missing argument"
+.warning message # ok
+.warnings # misspelled
+.warnings messages # oops
+
+
+
+.info begin .elif misspellings tests, part 1
+.if 1
+.elif 1 # ok
+.elsif 1 # oops: misspelled
+.elseif 1 # oops: misspelled
+.endif
+
+
+
+
+.info begin .elif misspellings tests, part 2
+.if 0
+.elif 0 # ok
+.elsif 0 # oops: misspelled
+.elseif 0 # oops: misspelled
+.endif
+
+
+
+
+.info begin .elif misspellings tests, part 3
+.if 0
+.elsif 0 # oops: misspelled
+.endif
+.if 0
+.elseif 0 # oops: misspelled
+.endif
+
+
+
+.info which branch is taken on misspelling after false?
+.if 0
+.elsif 1
+.info 1 taken
+.elsif 2
+.info 2 taken
+.else
+.info else taken
+.endif
+
+.info which branch is taken on misspelling after true?
+.if 1
+.elsif 1
+.info 1 taken
+.elsif 2
+.info 2 taken
+.else
+.info else taken
+.endif
+
+.indented none
+. indented 2 spaces
+. indented tab
+.${:Uinfo} directives cannot be indirect
+
+
+
+
+
+
+.include "nonexistent.mk"
+.include "/dev/null" # size 0
+# including a directory technically succeeds, but shouldn't.
+#.include "." # directory
+
+
+
+
+
+
+.info end of the tests
+
+all:
+ @:
diff --git a/contrib/bmake/unit-tests/envfirst.exp b/contrib/bmake/unit-tests/envfirst.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/envfirst.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/envfirst.mk b/contrib/bmake/unit-tests/envfirst.mk
new file mode 100644
index 000000000000..f4744bd61285
--- /dev/null
+++ b/contrib/bmake/unit-tests/envfirst.mk
@@ -0,0 +1,42 @@
+# $NetBSD: envfirst.mk,v 1.2 2020/07/27 18:57:42 rillig Exp $
+#
+# The -e option makes environment variables stronger than global variables.
+
+.if ${FROM_ENV} != value-from-env
+.error ${FROM_ENV}
+.endif
+
+# Try to override the variable; this does not have any effect.
+FROM_ENV= value-from-mk
+.if ${FROM_ENV} != value-from-env
+.error ${FROM_ENV}
+.endif
+
+# Try to append to the variable; this also doesn't have any effect.
+FROM_ENV+= appended
+.if ${FROM_ENV} != value-from-env
+.error ${FROM_ENV}
+.endif
+
+# The default assignment also cannot change the variable.
+FROM_ENV?= default
+.if ${FROM_ENV} != value-from-env
+.error ${FROM_ENV}
+.endif
+
+# Neither can the assignment modifiers.
+.if ${FROM_ENV::=from-condition}
+.endif
+.if ${FROM_ENV} != value-from-env
+.error ${FROM_ENV}
+.endif
+
+# Even .undef doesn't work since it only affects the global context,
+# which is independent from the environment variables.
+.undef FROM_ENV
+.if ${FROM_ENV} != value-from-env
+.error ${FROM_ENV}
+.endif
+
+all:
+ @: nothing
diff --git a/contrib/bmake/unit-tests/export-all.mk b/contrib/bmake/unit-tests/export-all.mk
index 200412f3b90a..c1910cac96b9 100644
--- a/contrib/bmake/unit-tests/export-all.mk
+++ b/contrib/bmake/unit-tests/export-all.mk
@@ -1,4 +1,4 @@
-# $Id: export-all.mk,v 1.1.1.2 2015/04/10 20:43:38 sjg Exp $
+# $Id: export-all.mk,v 1.1.1.3 2020/07/28 16:57:18 sjg Exp $
UT_OK=good
UT_F=fine
@@ -17,6 +17,7 @@ UT_OKDIR = ${${here}/../${here:T}:L:${M_tA}:T}
.export
+FILTER_CMD= grep ^UT_
.include "export.mk"
UT_TEST=export-all
diff --git a/contrib/bmake/unit-tests/export-variants.exp b/contrib/bmake/unit-tests/export-variants.exp
new file mode 100755
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/export-variants.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/export-variants.mk b/contrib/bmake/unit-tests/export-variants.mk
new file mode 100755
index 000000000000..1e254f1816cf
--- /dev/null
+++ b/contrib/bmake/unit-tests/export-variants.mk
@@ -0,0 +1,40 @@
+# $NetBSD: export-variants.mk,v 1.2 2020/08/08 13:00:07 rillig Exp $
+#
+# Test whether exported variables apply to each variant of running
+# external commands:
+#
+# The != assignments.
+# The :!cmd! modifier.
+# The :sh modifier.
+
+SHVAR!= env | grep ^UT_ || true
+.if ${SHVAR} != ""
+.warning At this point, no variable should be exported.
+.endif
+
+.if ${:!env | grep ^UT_ || true!} != ""
+.warning At this point, no variable should be exported.
+.endif
+
+.if ${env | grep ^UT_ || true:L:sh} != ""
+.warning At this point, no variable should be exported.
+.endif
+
+UT_VAR= value
+.export UT_VAR
+
+SHVAR!= env | grep ^UT_ || true
+.if ${SHVAR} != "UT_VAR=value"
+.warning At this point, no variable should be exported.
+.endif
+
+.if ${:!env | grep ^UT_ || true!} != "UT_VAR=value"
+.warning At this point, some variables should be exported.
+.endif
+
+.if ${env | grep ^UT_ || true:L:sh} != "UT_VAR=value"
+.warning At this point, some variables should be exported.
+.endif
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/export.exp b/contrib/bmake/unit-tests/export.exp
index 143771ce90ae..c0e9b98c5a44 100644
--- a/contrib/bmake/unit-tests/export.exp
+++ b/contrib/bmake/unit-tests/export.exp
@@ -1,3 +1,5 @@
+&=ampersand
+MAKELEVEL=1
UT_DOLLAR=This is $UT_FU
UT_FOO=foobar is fubar
UT_FU=fubar
diff --git a/contrib/bmake/unit-tests/export.mk b/contrib/bmake/unit-tests/export.mk
index 01f69f918161..b98d175af314 100644
--- a/contrib/bmake/unit-tests/export.mk
+++ b/contrib/bmake/unit-tests/export.mk
@@ -1,22 +1,43 @@
-# $Id: export.mk,v 1.1.1.1 2014/08/30 18:57:18 sjg Exp $
+# $Id: export.mk,v 1.1.1.4 2020/08/08 22:34:25 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.
+# believe 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
+
+.if !defined(.MAKE.PID)
+.error .MAKE.PID must be defined
+.endif
+@= at
+%= percent
+*= asterisk
+${:U!}= exclamation # A direct != would try to run "exclamation"
+ # as a shell command and assign its output
+ # to the empty variable.
+&= ampersand
+
+# This is ignored because it is internal.
.export .MAKE.PID
+# These are ignored because they are local to the target.
+.export @
+.export %
+.export *
+.export !
+.export &
+# This is ignored because it is undefined.
+.export UNDEFINED
BAR=bar is ${UT_FU}
.MAKE.EXPORTED+= UT_ZOO UT_TEST
-all:
- @env | grep '^UT_' | sort
+FILTER_CMD?= egrep -v '^(MAKEFLAGS|PATH|PWD|SHLVL|_)='
+all:
+ @env | ${FILTER_CMD} | sort
diff --git a/contrib/bmake/unit-tests/forloop.mk b/contrib/bmake/unit-tests/forloop.mk
index dd3f07ddb19a..d61a53816df7 100644
--- a/contrib/bmake/unit-tests/forloop.mk
+++ b/contrib/bmake/unit-tests/forloop.mk
@@ -1,4 +1,4 @@
-# $Id: forloop.mk,v 1.1.1.2 2020/05/05 00:54:40 sjg Exp $
+# $Id: forloop.mk,v 1.1.1.3 2020/09/02 18:35:14 sjg Exp $
all: for-loop
@@ -36,7 +36,7 @@ X!= echo 'a=$a b=$b' >&2; echo
# Since at least 1993, iteration stops at the first newline.
# Back then, the .newline variable didn't exist, therefore it was unlikely
-# that a newline ever occured.
+# that a newline ever occurred.
.for var in a${.newline}b${.newline}c
X!= echo 'newline-item=('${var:Q}')' 1>&2; echo
.endfor
diff --git a/contrib/bmake/unit-tests/impsrc.exp b/contrib/bmake/unit-tests/impsrc.exp
index 23e8347d205d..3eb050e080c5 100644
--- a/contrib/bmake/unit-tests/impsrc.exp
+++ b/contrib/bmake/unit-tests/impsrc.exp
@@ -1,13 +1,13 @@
-expected: source4
-actual: source4
+expected:
+actual:
expected: target1.x
actual: target1.x
expected: target1.y
actual: target1.y
-expected: source1
-actual: source1
-expected: source2
-actual: source2
-expected: source1
-actual: source1
+expected:
+actual:
+expected:
+actual:
+expected:
+actual:
exit status 0
diff --git a/contrib/bmake/unit-tests/impsrc.mk b/contrib/bmake/unit-tests/impsrc.mk
index 95ae0c34f3b5..6fb31adac938 100644
--- a/contrib/bmake/unit-tests/impsrc.mk
+++ b/contrib/bmake/unit-tests/impsrc.mk
@@ -1,4 +1,4 @@
-# $NetBSD: impsrc.mk,v 1.2 2014/08/30 22:21:07 sjg Exp $
+# $NetBSD: impsrc.mk,v 1.3 2020/08/07 13:43:50 rillig Exp $
# Does ${.IMPSRC} work properly?
# It should be set, in order of precedence, to ${.TARGET} of:
@@ -6,6 +6,8 @@
# 2) the first prerequisite from the dependency line of an explicit rule, or
# 3) the first prerequisite of an explicit rule.
#
+# Items 2 and 3 work in GNU make.
+# Items 2 and 3 are not required by POSIX 2018.
all: target1.z target2 target3 target4
@@ -19,25 +21,40 @@ all: target1.z target2 target3 target4
@echo 'expected: target1.y'
@echo 'actual: $<'
+# (3) Making target1.z out of target1.y is done because of an inference rule.
+# Therefore $< is available here.
+
+# (2) This is an additional dependency on the inference rule .x.y.
+# The dependency target1.x comes from the inference rule,
+# therefore it is available as $<.
target1.y: source3
+# (1) This is an explicit dependency, not an inference rule.
+# Therefore POSIX does not specify that $< be available here.
target1.x: source4
- @echo 'expected: source4'
+ @echo 'expected: ' # either 'source4' or ''
@echo 'actual: $<'
+# (4) This is an explicit dependency, independent of any inference rule.
+# Therefore $< is not available here.
target2: source1 source2
- @echo 'expected: source1'
+ @echo 'expected: '
@echo 'actual: $<'
+# (5) These are two explicit dependency rules.
+# The first doesn't have any dependencies, only the second has.
+# If any, the value of $< would be 'source2'.
target3: source1
target3: source2 source3
- @echo 'expected: source2'
+ @echo 'expected: '
@echo 'actual: $<'
+# (6) The explicit rule does not have associated commands.
+# The value of $< might come from that rule,
+# but it's equally fine to leave $< undefined.
target4: source1
target4:
- @echo 'expected: source1'
+ @echo 'expected: '
@echo 'actual: $<'
source1 source2 source3 source4:
-
diff --git a/contrib/bmake/unit-tests/include-main.mk b/contrib/bmake/unit-tests/include-main.mk
index 452b6102a5ce..a53dd886b800 100644
--- a/contrib/bmake/unit-tests/include-main.mk
+++ b/contrib/bmake/unit-tests/include-main.mk
@@ -1,11 +1,11 @@
-# $NetBSD: include-main.mk,v 1.1 2020/05/17 12:36:26 rillig Exp $
+# $NetBSD: include-main.mk,v 1.2 2020/07/27 20:55:59 rillig Exp $
#
# Demonstrates that the .INCLUDEDFROMFILE magic variable does not behave
# as described in the manual page.
#
# The manual page says that it is the "filename of the file this Makefile
# was included from", while in reality it is the "filename in which the
-# latest .include happened".
+# latest .include happened". See parse.c, function ParseSetIncludeFile.
#
.if !defined(.INCLUDEDFROMFILE)
diff --git a/contrib/bmake/unit-tests/lint.exp b/contrib/bmake/unit-tests/lint.exp
new file mode 100755
index 000000000000..9cd0eeb1ddf4
--- /dev/null
+++ b/contrib/bmake/unit-tests/lint.exp
@@ -0,0 +1,4 @@
+make: In the :@ modifier of "VAR", the variable name "${:Ubar:S,b,v,}" must not contain a dollar.
+y@:Q}
+xvaluey
+exit status 0
diff --git a/contrib/bmake/unit-tests/lint.mk b/contrib/bmake/unit-tests/lint.mk
new file mode 100755
index 000000000000..56cd6be27860
--- /dev/null
+++ b/contrib/bmake/unit-tests/lint.mk
@@ -0,0 +1,17 @@
+# $NetBSD: lint.mk,v 1.2 2020/08/08 13:00:07 rillig Exp $
+#
+# Demonstrates stricter checks that are only enabled in the lint mode,
+# using the -dL option.
+
+# Ouch: as of 2020-08-03, make exits successfully even though the error
+# message has been issued as PARSE_FATAL.
+
+# Ouch: as of 2020-08-03, the variable is malformed and parsing stops
+# for a moment, but is continued after the wrongly-guessed end of the
+# variable, which echoes "y@:Q}".
+
+all: mod-loop-varname
+
+mod-loop-varname:
+ @echo ${VAR:Uvalue:@${:Ubar:S,b,v,}@x${var}y@:Q}
+ @echo ${VAR:Uvalue:@!@x$!y@:Q} # surprisingly allowed
diff --git a/contrib/bmake/unit-tests/make-exported.exp b/contrib/bmake/unit-tests/make-exported.exp
new file mode 100755
index 000000000000..7408f16c4e6c
--- /dev/null
+++ b/contrib/bmake/unit-tests/make-exported.exp
@@ -0,0 +1,3 @@
+-literal=make-exported-value
+UT_VAR=
+exit status 0
diff --git a/contrib/bmake/unit-tests/make-exported.mk b/contrib/bmake/unit-tests/make-exported.mk
new file mode 100755
index 000000000000..36f4b356ae53
--- /dev/null
+++ b/contrib/bmake/unit-tests/make-exported.mk
@@ -0,0 +1,16 @@
+# $NetBSD: make-exported.mk,v 1.1 2020/08/09 12:59:16 rillig Exp $
+#
+# As of 2020-08-09, the code in Var_Export is shared between the .export
+# directive and the .MAKE.EXPORTED variable. This leads to non-obvious
+# behavior for certain variable assignments.
+
+-env= make-exported-value
+-literal= make-exported-value
+UT_VAR= ${UNEXPANDED}
+
+# The following behavior is probably not intended.
+.MAKE.EXPORTED= -env # like .export-env
+.MAKE.EXPORTED= -literal UT_VAR # like .export-literal PATH
+
+all:
+ @env | sort | grep -E '^UT_|make-exported-value' || true
diff --git a/contrib/bmake/unit-tests/moderrs.exp b/contrib/bmake/unit-tests/moderrs.exp
index cb51aa09d909..f017cae4633a 100644
--- a/contrib/bmake/unit-tests/moderrs.exp
+++ b/contrib/bmake/unit-tests/moderrs.exp
@@ -10,7 +10,132 @@ 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)
+Expect: Unfinished modifier for VAR (',' missing)
+make: Unfinished modifier for VAR (',' missing)
VAR:S,V,v=
+Expect: 2 errors about missing @ delimiter
+make: Unfinished modifier for UNDEF ('@' missing)
+
+make: Unfinished modifier for UNDEF ('@' missing)
+
+1 2 3
+modloop-close:
+make: Unclosed variable specification (expecting '}') for "UNDEF" (value "1}... 2}... 3}...") modifier @
+1}... 2}... 3}...
+1}... 2}... 3}...
+Expect: 2 errors about missing ] delimiter
+make: Unfinished modifier for UNDEF (']' missing)
+
+make: Unfinished modifier for UNDEF (']' missing)
+
+13=
+12345=ok
+Expect: 2 errors about missing ! delimiter
+make: Unfinished modifier for VARNAME ('!' missing)
+
+make: Unfinished modifier for ! ('!' missing)
+
+mod-subst-delimiter:
+make: Missing delimiter for :S modifier
+
+make: Unfinished modifier for VAR (',' missing)
+
+make: Unfinished modifier for VAR (',' missing)
+
+make: Unfinished modifier for VAR (',' missing)
+
+make: Unfinished modifier for VAR (',' missing)
+
+make: Unclosed variable specification (expecting '}') for "VAR" (value "TheVariable") modifier S
+TheVariable
+TheVariable
+make: Missing delimiter for :S modifier
+1:
+make: Unfinished modifier for VAR (',' missing)
+2:
+make: Unfinished modifier for VAR (',' missing)
+3:
+make: Unfinished modifier for VAR (',' missing)
+
+make: Unfinished modifier for VAR (',' missing)
+
+make: Unclosed variable specification (expecting '}') for "VAR" (value "TheVariable") modifier S
+TheVariable
+TheVariable
+mod-regex-delimiter:
+make: Missing delimiter for :C modifier
+
+make: Unfinished modifier for VAR (',' missing)
+
+make: Unfinished modifier for VAR (',' missing)
+
+make: Unfinished modifier for VAR (',' missing)
+
+make: Unfinished modifier for VAR (',' missing)
+
+make: Unclosed variable specification (expecting '}') for "VAR" (value "TheVariable") modifier C
+TheVariable
+TheVariable
+make: Missing delimiter for :C modifier
+1:
+make: Unfinished modifier for VAR (',' missing)
+2:
+make: Unfinished modifier for VAR (',' missing)
+3:
+make: Unfinished modifier for VAR (',' missing)
+
+make: Unfinished modifier for VAR (',' missing)
+
+make: Unclosed variable specification (expecting '}') for "VAR" (value "TheVariable") modifier C
+TheVariable
+TheVariable
+mod-regex-undefined-subexpression:
+one one 2 3 5 8 one3 2one 34
+make: No match for subexpression \2
+make: No match for subexpression \2
+make: No match for subexpression \1
+make: No match for subexpression \2
+make: No match for subexpression \1
+()+() ()+() ()+() 3 5 8 (3)+() ()+(1) 34
+mod-ts-parse:
+112358132134
+15152535558513521534
+make: Bad modifier `:ts\65oct' for FIB
+65oct}
+make: Bad modifier `:tsxy' for FIB
+xy}
+mod-t-parse:
+make: Bad modifier `:t' for FIB
+
+make: Bad modifier `:txy' for FIB
+y}
+make: Bad modifier `:t' for FIB
+
+make: Bad modifier `:t' for FIB
+M*}
+mod-ifelse-parse:
+make: Unfinished modifier for FIB (':' missing)
+
+make: Unfinished modifier for FIB (':' missing)
+
+make: Unfinished modifier for FIB ('}' missing)
+
+make: Unfinished modifier for FIB ('}' missing)
+
+then
+mod-remember-parse:
+1 1 2 3 5 8 13 21 34
+make: Unknown modifier '_'
+
+mod-sysv-parse:
+make: Unknown modifier '3'
+make: Unclosed variable specification (expecting '}') for "FIB" (value "") modifier 3
+
+make: Unknown modifier '3'
+make: Unclosed variable specification (expecting '}') for "FIB" (value "") modifier 3
+
+make: Unknown modifier '3'
+make: Unclosed variable specification (expecting '}') for "FIB" (value "") modifier 3
+
+1 1 2 x3 5 8 1x3 21 34
exit status 0
diff --git a/contrib/bmake/unit-tests/moderrs.mk b/contrib/bmake/unit-tests/moderrs.mk
index d0eb17ff6df6..931158c8ecb9 100644
--- a/contrib/bmake/unit-tests/moderrs.mk
+++ b/contrib/bmake/unit-tests/moderrs.mk
@@ -1,4 +1,4 @@
-# $Id: moderrs.mk,v 1.1.1.1 2014/08/30 18:57:18 sjg Exp $
+# $Id: moderrs.mk,v 1.1.1.8 2020/08/26 16:40:43 sjg Exp $
#
# various modifier error tests
@@ -8,7 +8,20 @@ MOD_UNKN=Z
MOD_TERM=S,V,v
MOD_S:= ${MOD_TERM},
-all: modunkn modunknV varterm vartermV modtermV
+FIB= 1 1 2 3 5 8 13 21 34
+
+all: modunkn modunknV varterm vartermV modtermV modloop
+all: modloop-close
+all: modwords
+all: modexclam
+all: mod-subst-delimiter
+all: mod-regex-delimiter
+all: mod-regex-undefined-subexpression
+all: mod-ts-parse
+all: mod-t-parse
+all: mod-ifelse-parse
+all: mod-remember-parse
+all: mod-sysv-parse
modunkn:
@echo "Expect: Unknown modifier 'Z'"
@@ -27,5 +40,137 @@ vartermV:
@echo VAR:${MOD_TERM},=${VAR:${MOD_S}
modtermV:
- @echo "Expect: Unclosed substitution for VAR (, missing)"
+ @echo "Expect: Unfinished modifier for VAR (',' missing)"
-@echo "VAR:${MOD_TERM}=${VAR:${MOD_TERM}}"
+
+modloop:
+ @echo "Expect: 2 errors about missing @ delimiter"
+ @echo ${UNDEF:U1 2 3:@var}
+ @echo ${UNDEF:U1 2 3:@var@...}
+ @echo ${UNDEF:U1 2 3:@var@${var}@}
+
+# The closing brace after the ${var} is part of the replacement string.
+# In ParseModifierPart, braces and parentheses don't have to be balanced.
+# This is contrary to the :M, :N modifiers, where both parentheses and
+# braces must be balanced.
+# This is also contrary to the SysV modifier, where only the actually
+# used delimiter (either braces or parentheses) must be balanced.
+modloop-close:
+ @echo $@:
+ @echo ${UNDEF:U1 2 3:@var@${var}}...@
+ @echo ${UNDEF:U1 2 3:@var@${var}}...@}
+
+modwords:
+ @echo "Expect: 2 errors about missing ] delimiter"
+ @echo ${UNDEF:U1 2 3:[}
+ @echo ${UNDEF:U1 2 3:[#}
+
+ # out of bounds => empty
+ @echo 13=${UNDEF:U1 2 3:[13]}
+
+ # Word index out of bounds.
+ #
+ # On LP64I32, strtol returns LONG_MAX,
+ # which is then truncated to int (undefined behavior),
+ # typically resulting in -1.
+ # This -1 is interpreted as "the last word".
+ #
+ # On ILP32, strtol returns LONG_MAX,
+ # which is a large number.
+ # This results in a range from LONG_MAX - 1 to 3,
+ # which is empty.
+ @echo 12345=${UNDEF:U1 2 3:[123451234512345123451234512345]:S,^$,ok,:S,^3$,ok,}
+
+modexclam:
+ @echo "Expect: 2 errors about missing ! delimiter"
+ @echo ${VARNAME:!echo}
+ # When the final exclamation mark is missing, there is no
+ # fallback to the SysV substitution modifier.
+ # If there were a fallback, the output would be "exclam",
+ # and the above would have produced an "Unknown modifier '!'".
+ @echo ${!:L:!=exclam}
+
+mod-subst-delimiter:
+ @echo $@:
+ @echo ${VAR:S
+ @echo ${VAR:S,
+ @echo ${VAR:S,from
+ @echo ${VAR:S,from,
+ @echo ${VAR:S,from,to
+ @echo ${VAR:S,from,to,
+ @echo ${VAR:S,from,to,}
+ @echo 1: ${VAR:S
+ @echo 2: ${VAR:S,
+ @echo 3: ${VAR:S,from
+ @echo ${VAR:S,from,
+ @echo ${VAR:S,from,to
+ @echo ${VAR:S,from,to,
+ @echo ${VAR:S,from,to,}
+
+mod-regex-delimiter:
+ @echo $@:
+ @echo ${VAR:C
+ @echo ${VAR:C,
+ @echo ${VAR:C,from
+ @echo ${VAR:C,from,
+ @echo ${VAR:C,from,to
+ @echo ${VAR:C,from,to,
+ @echo ${VAR:C,from,to,}
+ @echo 1: ${VAR:C
+ @echo 2: ${VAR:C,
+ @echo 3: ${VAR:C,from
+ @echo ${VAR:C,from,
+ @echo ${VAR:C,from,to
+ @echo ${VAR:C,from,to,
+ @echo ${VAR:C,from,to,}
+
+# In regular expressions with alternatives, not all capturing groups are
+# always set; some may be missing. Warn about these.
+#
+# Since there is no way to turn off this warning, the combination of
+# alternative matches and capturing groups is not widely used.
+#
+# A newly added modifier 'U' such as in :C,(a.)|(b.),\1\2,U might be added
+# for treating undefined capturing groups as empty, but that would create a
+# syntactical ambiguity since the :S and :C modifiers are open-ended (see
+# mod-subst-chain). Luckily the modifier :U does not make sense after :C,
+# therefore this case does not happen in practice.
+# The sub-modifier for the :C modifier would have to be chosen wisely.
+mod-regex-undefined-subexpression:
+ @echo $@:
+ @echo ${FIB:C,1(.*),one\1,} # all ok
+ @echo ${FIB:C,1(.*)|2(.*),(\1)+(\2),:Q} # no match for subexpression
+
+mod-ts-parse:
+ @echo $@:
+ @echo ${FIB:ts}
+ @echo ${FIB:ts\65} # octal 065 == U+0035 == '5'
+ @echo ${FIB:ts\65oct} # bad modifier
+ @echo ${FIB:tsxy} # modifier too long
+
+mod-t-parse:
+ @echo $@:
+ @echo ${FIB:t
+ @echo ${FIB:txy}
+ @echo ${FIB:t}
+ @echo ${FIB:t:M*}
+
+mod-ifelse-parse:
+ @echo $@:
+ @echo ${FIB:?
+ @echo ${FIB:?then
+ @echo ${FIB:?then:
+ @echo ${FIB:?then:else
+ @echo ${FIB:?then:else}
+
+mod-remember-parse:
+ @echo $@:
+ @echo ${FIB:_} # ok
+ @echo ${FIB:__} # modifier name too long
+
+mod-sysv-parse:
+ @echo $@:
+ @echo ${FIB:3
+ @echo ${FIB:3=
+ @echo ${FIB:3=x3
+ @echo ${FIB:3=x3} # ok
diff --git a/contrib/bmake/unit-tests/modmatch.exp b/contrib/bmake/unit-tests/modmatch.exp
index a7bf8b748f5b..fcaf6c02ed69 100644
--- a/contrib/bmake/unit-tests/modmatch.exp
+++ b/contrib/bmake/unit-tests/modmatch.exp
@@ -14,7 +14,4 @@ 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
-Upper=One Two Three Four
-Lower=five six seven
-nose=One Three five
exit status 0
diff --git a/contrib/bmake/unit-tests/modmatch.mk b/contrib/bmake/unit-tests/modmatch.mk
index c631bbd3440f..f15b3d699a8e 100644
--- a/contrib/bmake/unit-tests/modmatch.mk
+++ b/contrib/bmake/unit-tests/modmatch.mk
@@ -1,3 +1,6 @@
+# $NetBSD: modmatch.mk,v 1.8 2020/08/16 20:03:53 rillig Exp $
+#
+# Tests for the :M and :S modifiers.
X=a b c d e
@@ -15,7 +18,7 @@ res = no
res = OK
.endif
-all: show-libs check-cclass slow
+all: show-libs
show-libs:
@for x in $X; do ${.MAKE} -f ${MAKEFILE} show LIB=$$x; done
@@ -25,15 +28,3 @@ 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}"'
-
-LIST= One Two Three Four five six seven
-
-check-cclass:
- @echo Upper=${LIST:M[A-Z]*}
- @echo Lower=${LIST:M[^A-Z]*}
- @echo nose=${LIST:M[^s]*[ex]}
-
-# Before 2020-06-13, this expression took quite a long time in Str_Match,
-# calling itself 601080390 times for 16 asterisks.
-slow: .PHONY
- @:;: ${:U****************:M****************b:Q}
diff --git a/contrib/bmake/unit-tests/modmisc.exp b/contrib/bmake/unit-tests/modmisc.exp
index 619ae3c7fc11..94f131052fdc 100644
--- a/contrib/bmake/unit-tests/modmisc.exp
+++ b/contrib/bmake/unit-tests/modmisc.exp
@@ -1,3 +1,4 @@
+make: Unknown modifier '$'
path=':/bin:/tmp::/:.:/no/such/dir:.'
path='/bin:/tmp:/:/no/such/dir'
path='/bin:/tmp:/:/no/such/dir'
@@ -7,45 +8,14 @@ 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
-dirname of 'a/b/c def a.b.c a.b/c a a.a .gitignore a a.a' is 'a/b . . a.b . . . . .'
-basename of 'a/b/c def a.b.c a.b/c a a.a .gitignore a a.a' is 'c def a.b.c c a a.a .gitignore a a.a'
-suffix of 'a/b/c def a.b.c a.b/c a a.a .gitignore a a.a' is 'c b/c a gitignore a'
-root of 'a/b/c def a.b.c a.b/c a a.a .gitignore a a.a' is 'a/b/c def a.b a a a a a'
S:
C:
@:
S:empty
C:empty
@:
-:a b b c:
-:a b b c:
-: b c:
-:a c:
-:x__ 3 x__ 3:
-:a b b c:
-:a b b c:
-: b c:
-make: RE substitution error: (details omitted)
-make: Unclosed substitution for (, missing)
-:C,word,____,:Q}:
-:a c:
-:x__ 3 x__ 3:
-:+one+ +two+ +three+:
-mod-at-resolve:w1d2d3w w2i3w w1i2d3 2i${RES3}w w1d2d3 2i${RES3} 1i${RES2}w:
-mod-subst-dollar:$1:
-mod-subst-dollar:$2:
-mod-subst-dollar:$3:
-mod-subst-dollar:$4:
-mod-subst-dollar:$5:
-mod-subst-dollar:$6:
-mod-subst-dollar:$7:
-mod-subst-dollar:$8:
-mod-subst-dollar:U8:
-mod-subst-dollar:$$$$:
-mod-loop-dollar:1:
-mod-loop-dollar:${word}:
-mod-loop-dollar:$3$:
-mod-loop-dollar:$${word}$:
-mod-loop-dollar:$$5$$:
-mod-loop-dollar:$$${word}$$:
+mod-quote: new
+
+line
+mod-break-many-words: 500
exit status 0
diff --git a/contrib/bmake/unit-tests/modmisc.mk b/contrib/bmake/unit-tests/modmisc.mk
index d0c334342934..f2977da8a0ba 100644
--- a/contrib/bmake/unit-tests/modmisc.mk
+++ b/contrib/bmake/unit-tests/modmisc.mk
@@ -1,4 +1,4 @@
-# $Id: modmisc.mk,v 1.1.1.3 2020/07/09 22:35:19 sjg Exp $
+# $Id: modmisc.mk,v 1.1.1.15 2020/08/23 15:52:08 sjg Exp $
#
# miscellaneous modifier tests
@@ -15,13 +15,15 @@ MOD_HOMES=S,/home/,/homes/,
MOD_OPT=@d@$${exists($$d):?$$d:$${d:S,/usr,/opt,}}@
MOD_SEP=S,:, ,g
-all: modvar modvarloop modsysv mod-HTE emptyvar undefvar
-all: mod-S mod-C mod-at-varname mod-at-resolve
-all: mod-subst-dollar mod-loop-dollar
+all: modvar modvarloop modsysv emptyvar undefvar
+all: mod-quote
+all: mod-break-many-words
+# See also sysv.mk.
modsysv:
@echo "The answer is ${libfoo.a:L:libfoo.a=42}"
+# Demonstrates modifiers that are given indirectly from a variable.
modvar:
@echo "path='${path}'"
@echo "path='${path:${MOD_NODOT}}'"
@@ -39,13 +41,6 @@ modvarloop:
@echo "paths=${paths}"
@echo "PATHS=${paths:tu}"
-PATHNAMES= a/b/c def a.b.c a.b/c a a.a .gitignore a a.a
-mod-HTE:
- @echo "dirname of '"${PATHNAMES:Q}"' is '"${PATHNAMES:H:Q}"'"
- @echo "basename of '"${PATHNAMES:Q}"' is '"${PATHNAMES:T:Q}"'"
- @echo "suffix of '"${PATHNAMES:Q}"' is '"${PATHNAMES:E:Q}"'"
- @echo "root of '"${PATHNAMES:Q}"' is '"${PATHNAMES:R:Q}"'"
-
# When a modifier is applied to the "" variable, the result is discarded.
emptyvar:
@echo S:${:S,^$,empty,}
@@ -61,67 +56,37 @@ undefvar:
@echo C:${:U:C,^$,empty,}
@echo @:${:U:@var@empty@}
-mod-S:
- @echo :${:Ua b b c:S,a b,,:Q}:
- @echo :${:Ua b b c:S,a b,,1:Q}:
- @echo :${:Ua b b c:S,a b,,W:Q}:
- @echo :${:Ua b b c:S,b,,g:Q}:
- @echo :${:U1 2 3 1 2 3:S,1 2,___,Wg:S,_,x,:Q}:
-
-mod-C:
- @echo :${:Ua b b c:C,a b,,:Q}:
- @echo :${:Ua b b c:C,a b,,1:Q}:
- @echo :${:Ua b b c:C,a b,,W:Q}:
- @echo :${:Uword1 word2:C,****,____,g:C,word,____,:Q}:
- @echo :${:Ua b b c:C,b,,g:Q}:
- @echo :${:U1 2 3 1 2 3:C,1 2,___,Wg:C,_,x,:Q}:
-
-# In the :@ modifier, the name of the loop variable can even be generated
-# dynamically. There's no practical use-case for this, and hopefully nobody
-# will ever depend on this, but technically it's possible.
-mod-at-varname:
- @echo :${:Uone two three:@${:Ubar:S,b,v,}@+${var}+@:Q}:
-
-# The :@ modifier resolves the variables a little more often than expected.
-# In particular, it resolves _all_ variables from the context, and not only
-# the loop variable (in this case v).
-#
-# The d means direct reference, the i means indirect reference.
-RESOLVE= ${RES1} $${RES1}
-RES1= 1d${RES2} 1i$${RES2}
-RES2= 2d${RES3} 2i$${RES3}
-RES3= 3
-
-mod-at-resolve:
- @echo $@:${RESOLVE:@v@w${v}w@:Q}:
-
-# No matter how many dollar characters there are, they all get merged
-# into a single dollar by the :S modifier.
-mod-subst-dollar:
- @echo $@:${:U1:S,^,$,:Q}:
- @echo $@:${:U2:S,^,$$,:Q}:
- @echo $@:${:U3:S,^,$$$,:Q}:
- @echo $@:${:U4:S,^,$$$$,:Q}:
- @echo $@:${:U5:S,^,$$$$$,:Q}:
- @echo $@:${:U6:S,^,$$$$$$,:Q}:
- @echo $@:${:U7:S,^,$$$$$$$,:Q}:
- @echo $@:${:U8:S,^,$$$$$$$$,:Q}:
-# This generates no dollar at all:
- @echo $@:${:UU8:S,^,${:U$$$$$$$$},:Q}:
-# Here is an alternative way to generate dollar characters.
-# It's unexpectedly complicated though.
- @echo $@:${:U:range=5:ts\x24:C,[0-9],,g:Q}:
-
-# Demonstrate that it is possible to generate dollar characters using the
-# :@ modifier.
+
+mod-quote:
+ @echo $@: new${.newline:Q}${.newline:Q}line
+
+# Cover the bmake_realloc in brk_string.
+mod-break-many-words:
+ @echo $@: ${UNDEF:U:range=500:[#]}
+
+# To apply a modifier indirectly via another variable, the whole
+# modifier must be put into a single variable.
+.if ${value:L:${:US}${:U,value,replacement,}} != "S,value,replacement,}"
+.warning unexpected
+.endif
+
+# Adding another level of indirection (the 2 nested :U expressions) helps.
+.if ${value:L:${:U${:US}${:U,value,replacement,}}} != "replacement"
+.warning unexpected
+.endif
+
+# Multiple indirect modifiers can be applied one after another as long as
+# they are separated with colons.
+.if ${value:L:${:US,a,A,}:${:US,e,E,}} != "vAluE"
+.warning unexpected
+.endif
+
+# An indirect variable that evaluates to the empty string is allowed though.
+# This makes it possible to define conditional modifiers, like this:
#
-# These are edge cases that could have resulted in a parse error as well
-# since the $@ at the end could have been interpreted as a variable, which
-# would mean a missing closing @ delimiter.
-mod-loop-dollar:
- @echo $@:${:U1:@word@${word}$@:Q}:
- @echo $@:${:U2:@word@$${word}$$@:Q}:
- @echo $@:${:U3:@word@$$${word}$$$@:Q}:
- @echo $@:${:U4:@word@$$$${word}$$$$@:Q}:
- @echo $@:${:U5:@word@$$$$${word}$$$$$@:Q}:
- @echo $@:${:U6:@word@$$$$$${word}$$$$$$@:Q}:
+# M.little-endian= S,1234,4321,
+# M.big-endian= # none
+.if ${value:L:${:Dempty}S,a,A,} != "vAlue"
+.warning unexpected
+.endif
+
diff --git a/contrib/bmake/unit-tests/modorder.exp b/contrib/bmake/unit-tests/modorder.exp
deleted file mode 100644
index 8e0aad2e2027..000000000000
--- a/contrib/bmake/unit-tests/modorder.exp
+++ /dev/null
@@ -1,12 +0,0 @@
-LIST = one two three four five six seven eight nine ten
-LIST:O = eight five four nine one seven six ten three two
-LIST:Or = two three ten six seven one nine four five eight
-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}
-exit status 0
diff --git a/contrib/bmake/unit-tests/modorder.mk b/contrib/bmake/unit-tests/modorder.mk
deleted file mode 100644
index 89e64b43c57c..000000000000
--- a/contrib/bmake/unit-tests/modorder.mk
+++ /dev/null
@@ -1,24 +0,0 @@
-# $NetBSD: modorder.mk,v 1.3 2020/06/09 01:48:17 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}"
- @echo "LIST:Or = ${LIST:Or}"
- # 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 each of the
- # 3 :Ox tests will incorrectly fail with probability 2.756E-7, which
- # lets the whole test fail once in 1.209.600 runs, on average.
- @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/contrib/bmake/unit-tests/modts.exp b/contrib/bmake/unit-tests/modts.exp
index 338964963a86..5db79fc96586 100644
--- a/contrib/bmake/unit-tests/modts.exp
+++ b/contrib/bmake/unit-tests/modts.exp
@@ -1,34 +1,3 @@
-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"
-LIST:ts/xa:tu="ONE
-TWO
-THREE
-FOUR
-FIVE
-SIX"
make: Bad modifier `:tx' for LIST
LIST:tx="}"
make: Bad modifier `:ts\X' for LIST
@@ -36,4 +5,10 @@ LIST:ts/x:tu="\X:tu}"
FU_mod-ts="a/b/cool"
FU_mod-ts:ts:T="cool" == cool?
B.${AAA:ts}="Baaa" == Baaa?
+:ts :S => aaxBbxaaxbbxaaxbb
+:ts :S space => axa a axc
+:ts :S space :M => axaxaxaxc
+:ts :S => axa a axc
+:ts :S :@ => axa a axc
+:ts :S :@ :M => axaxaxaxc
exit status 0
diff --git a/contrib/bmake/unit-tests/modts.mk b/contrib/bmake/unit-tests/modts.mk
index e66dc25a2a02..3219538d5e4a 100644
--- a/contrib/bmake/unit-tests/modts.mk
+++ b/contrib/bmake/unit-tests/modts.mk
@@ -7,7 +7,7 @@ FU_mod-ts = a / b / cool
AAA= a a a
B.aaa= Baaa
-all: mod-ts
+all: mod-ts mod-ts-space
# Use print or printf iff they are builtin.
# XXX note that this causes problems, when make decides
@@ -21,24 +21,27 @@ 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:ts/xa:tu="${LIST:ts\xa: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?'
+
+mod-ts-space:
+ # After the :ts modifier, the whole string is interpreted as a single
+ # word since all spaces have been replaced with x.
+ @${PRINT} ':ts :S => '${aa bb aa bb aa bb:L:tsx:S,b,B,:Q}
+
+ # The :ts modifier also applies to word separators that are added
+ # afterwards.
+ @${PRINT} ':ts :S space => '${a ababa c:L:tsx:S,b, ,g:Q}
+ @${PRINT} ':ts :S space :M => '${a ababa c:L:tsx:S,b, ,g:M*:Q}
+
+ # Not all modifiers behave this way though. Some of them always use
+ # a space as word separator instead of the :ts separator.
+ # This seems like an oversight during implementation.
+ @${PRINT} ':ts :S => '${a ababa c:L:tsx:S,b, ,g:Q}
+ @${PRINT} ':ts :S :@ => '${a ababa c:L:tsx:S,b, ,g:@v@${v}@:Q}
+
+ # A final :M* modifier applies the :ts separator again, though.
+ @${PRINT} ':ts :S :@ :M => '${a ababa c:L:tsx:S,b, ,g:@v@${v}@:M*:Q}
diff --git a/contrib/bmake/unit-tests/opt-backwards.exp b/contrib/bmake/unit-tests/opt-backwards.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-backwards.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/opt-backwards.mk b/contrib/bmake/unit-tests/opt-backwards.mk
new file mode 100644
index 000000000000..bc0b1eff8530
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-backwards.mk
@@ -0,0 +1,8 @@
+# $NetBSD: opt-backwards.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the -B command line option.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/opt-chdir.exp b/contrib/bmake/unit-tests/opt-chdir.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-chdir.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/opt-chdir.mk b/contrib/bmake/unit-tests/opt-chdir.mk
new file mode 100644
index 000000000000..8735fddbef9e
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-chdir.mk
@@ -0,0 +1,8 @@
+# $NetBSD: opt-chdir.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the -C command line option.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/opt-debug-g1.exp b/contrib/bmake/unit-tests/opt-debug-g1.exp
new file mode 100755
index 000000000000..d6d014a0353f
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-debug-g1.exp
@@ -0,0 +1,15 @@
+#*** Input graph:
+# all, made UNMADE, type OP_DEPENDS, flags none
+# made-target, made UNMADE, type OP_DEPENDS, flags none
+# made-target-no-sources, made UNMADE, type OP_DEPENDS, flags none
+# made-source, made UNMADE, type OP_DEPENDS, flags none
+# unmade-target, made UNMADE, type OP_DEPENDS, flags none
+# unmade-sources, made UNMADE, type none, flags none
+# unmade-target-no-sources, made UNMADE, type OP_DEPENDS, flags none
+
+
+#
+# Files that are only sources:
+# unmade-sources [unmade-sources]
+#*** Transformations:
+exit status 0
diff --git a/contrib/bmake/unit-tests/opt-debug-g1.mk b/contrib/bmake/unit-tests/opt-debug-g1.mk
new file mode 100755
index 000000000000..3104fbf91bc1
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-debug-g1.mk
@@ -0,0 +1,19 @@
+# $NetBSD: opt-debug-g1.mk,v 1.1 2020/08/27 19:00:17 rillig Exp $
+#
+# Tests for the -dg1 command line option, which prints the input
+# graph before making anything.
+
+all: made-target made-target-no-sources
+
+made-target: made-source
+
+made-source:
+
+made-target-no-sources:
+
+unmade-target: unmade-sources
+
+unmade-target-no-sources:
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/opt-debug.exp b/contrib/bmake/unit-tests/opt-debug.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-debug.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/opt-debug.mk b/contrib/bmake/unit-tests/opt-debug.mk
new file mode 100644
index 000000000000..afd740a6caab
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-debug.mk
@@ -0,0 +1,8 @@
+# $NetBSD: opt-debug.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the -d command line option.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/opt-define.exp b/contrib/bmake/unit-tests/opt-define.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-define.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/opt-define.mk b/contrib/bmake/unit-tests/opt-define.mk
new file mode 100644
index 000000000000..ce0516ba44bc
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-define.mk
@@ -0,0 +1,8 @@
+# $NetBSD: opt-define.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the -D command line option.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/opt-env.exp b/contrib/bmake/unit-tests/opt-env.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-env.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/opt-env.mk b/contrib/bmake/unit-tests/opt-env.mk
new file mode 100644
index 000000000000..32e95ef41f5a
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-env.mk
@@ -0,0 +1,8 @@
+# $NetBSD: opt-env.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the -e command line option.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/opt-file.exp b/contrib/bmake/unit-tests/opt-file.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-file.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/opt-file.mk b/contrib/bmake/unit-tests/opt-file.mk
new file mode 100644
index 000000000000..86bc100bebc2
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-file.mk
@@ -0,0 +1,8 @@
+# $NetBSD: opt-file.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the -f command line option.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/opt-ignore.exp b/contrib/bmake/unit-tests/opt-ignore.exp
new file mode 100644
index 000000000000..265e143e3471
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-ignore.exp
@@ -0,0 +1,12 @@
+dependency 1
+dependency 2
+dependency 3
+other 1
+other 2
+main 1
+main 2
+*** Error code 1 (ignored)
+*** Error code 7 (ignored)
+*** Error code 1 (ignored)
+*** Error code 1 (ignored)
+exit status 0
diff --git a/contrib/bmake/unit-tests/opt-ignore.mk b/contrib/bmake/unit-tests/opt-ignore.mk
new file mode 100644
index 000000000000..6d6d8dc3a339
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-ignore.mk
@@ -0,0 +1,30 @@
+# $NetBSD: opt-ignore.mk,v 1.3 2020/08/23 14:28:04 rillig Exp $
+#
+# Tests for the -i command line option, which ignores the exit status of the
+# shell commands, and just continues with the next command, even from the same
+# target.
+#
+# Is there a situation in which this option is useful?
+#
+# Why are the "Error code" lines all collected at the bottom of the output
+# file, where they cannot be related to the individual shell commands that
+# failed?
+
+all: dependency other
+
+dependency:
+ @echo dependency 1
+ @false
+ @echo dependency 2
+ @:; exit 7
+ @echo dependency 3
+
+other:
+ @echo other 1
+ @false
+ @echo other 2
+
+all:
+ @echo main 1
+ @false
+ @echo main 2
diff --git a/contrib/bmake/unit-tests/opt-include-dir.exp b/contrib/bmake/unit-tests/opt-include-dir.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-include-dir.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/opt-include-dir.mk b/contrib/bmake/unit-tests/opt-include-dir.mk
new file mode 100644
index 000000000000..d61a6c979ea5
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-include-dir.mk
@@ -0,0 +1,8 @@
+# $NetBSD: opt-include-dir.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the -I command line option.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/opt-jobs-internal.exp b/contrib/bmake/unit-tests/opt-jobs-internal.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-jobs-internal.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/opt-jobs-internal.mk b/contrib/bmake/unit-tests/opt-jobs-internal.mk
new file mode 100644
index 000000000000..5426807ca98b
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-jobs-internal.mk
@@ -0,0 +1,8 @@
+# $NetBSD: opt-jobs-internal.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the (intentionally undocumented) -J command line option.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/opt-jobs.exp b/contrib/bmake/unit-tests/opt-jobs.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-jobs.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/opt-jobs.mk b/contrib/bmake/unit-tests/opt-jobs.mk
new file mode 100644
index 000000000000..7d54d08a8421
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-jobs.mk
@@ -0,0 +1,8 @@
+# $NetBSD: opt-jobs.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the -j command line option.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/opt-keep-going.exp b/contrib/bmake/unit-tests/opt-keep-going.exp
new file mode 100644
index 000000000000..de1b1ae582f4
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-keep-going.exp
@@ -0,0 +1,6 @@
+dependency 1
+other 1
+*** Error code 1 (continuing)
+*** Error code 1 (continuing)
+`all' not remade because of errors.
+exit status 0
diff --git a/contrib/bmake/unit-tests/opt-keep-going.mk b/contrib/bmake/unit-tests/opt-keep-going.mk
new file mode 100644
index 000000000000..1a2124ccb5f1
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-keep-going.mk
@@ -0,0 +1,24 @@
+# $NetBSD: opt-keep-going.mk,v 1.3 2020/08/23 14:28:04 rillig Exp $
+#
+# Tests for the -k command line option, which stops building a target as soon
+# as an error is detected, but continues building the other, independent
+# targets, as far as possible.
+
+all: dependency other
+
+dependency:
+ @echo dependency 1
+ @false
+ @echo dependency 2
+ @:; exit 7
+ @echo dependency 3
+
+other:
+ @echo other 1
+ @false
+ @echo other 2
+
+all:
+ @echo main 1
+ @false
+ @echo main 2
diff --git a/contrib/bmake/unit-tests/opt-m-include-dir.exp b/contrib/bmake/unit-tests/opt-m-include-dir.exp
new file mode 100644
index 000000000000..bce0e4be05ac
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-m-include-dir.exp
@@ -0,0 +1,2 @@
+ok
+exit status 0
diff --git a/contrib/bmake/unit-tests/opt-m-include-dir.mk b/contrib/bmake/unit-tests/opt-m-include-dir.mk
new file mode 100644
index 000000000000..6e0801390395
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-m-include-dir.mk
@@ -0,0 +1,61 @@
+# $NetBSD: opt-m-include-dir.mk,v 1.4 2020/09/01 20:14:34 rillig Exp $
+#
+# Tests for the -m command line option, which adds a directory to the
+# search path for the .include <...> directive.
+#
+# The .../canary.mk special argument starts searching in the current
+# directory and walks towards the file system root, until it finds a
+# directory that contains a file called canary.mk.
+#
+# To set up this scenario, the file step2.mk is created deep in a hierarchy
+# of subdirectories. Another file called opt-m-step3.mk is created a few
+# steps up in the directory hierarchy, serving as the canary file.
+#
+# Next to the canary file, there is opt-m-step3.mk. This file is found
+# by mentioning its simple name in an .include directive. It defines the
+# target "step2" that is needed by "step2.mk".
+
+.if ${.PARSEFILE:T} == "opt-m-include-dir.mk"
+
+# Set up the other files needed for this test.
+
+TEST_DIR:= ${.PARSEFILE:R}.tmp/sub/sub/sub/workdir
+CANARY_FILE:= ${.PARSEFILE:R}.tmp/sub/opt-m-canary.mk
+ACTUAL_FILE:= ${.PARSEFILE:R}.tmp/sub/opt-m-step3.mk
+
+_!= mkdir -p ${TEST_DIR}
+_!= > ${CANARY_FILE}
+_!= cp ${MAKEFILE} ${TEST_DIR}/step2.mk
+_!= cp ${MAKEFILE} ${ACTUAL_FILE}
+
+step1:
+ @${.MAKE} -C ${TEST_DIR} -f step2.mk step2
+
+.END:
+ @rm -rf ${MAKEFILE:R}.tmp
+
+.elif ${.PARSEFILE:T} == "step2.mk"
+
+# This is the file deep in the directory hierarchy. It sets up the
+# search path for the .include <...> directive and then includes a
+# single file from that search path.
+
+# This option adds .tmp/sub to the search path for .include <...>.
+.MAKEFLAGS: -m .../opt-m-canary.mk
+
+# This option does not add any directory to the search path since the
+# canary file does not exist.
+.MAKEFLAGS: -m .../does-not-exist
+
+.include <opt-m-step3.mk>
+
+.elif ${.PARSEFILE:T} == "opt-m-step3.mk"
+
+# This file is included by step2.mk.
+
+step2:
+ @echo ok
+
+.else
+. error
+.endif
diff --git a/contrib/bmake/unit-tests/opt-no-action-at-all.exp b/contrib/bmake/unit-tests/opt-no-action-at-all.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-no-action-at-all.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/opt-no-action-at-all.mk b/contrib/bmake/unit-tests/opt-no-action-at-all.mk
new file mode 100644
index 000000000000..6ab385946691
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-no-action-at-all.mk
@@ -0,0 +1,8 @@
+# $NetBSD: opt-no-action-at-all.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the -N command line option.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/opt-no-action.exp b/contrib/bmake/unit-tests/opt-no-action.exp
new file mode 100644
index 000000000000..381d68e2f2d9
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-no-action.exp
@@ -0,0 +1,13 @@
+command during parsing
+echo '.BEGIN: hidden command'
+echo '.BEGIN: run always'
+.BEGIN: run always
+echo 'main: hidden command'
+echo 'main: run always'
+main: run always
+run-always: hidden command
+run-always: run always
+echo '.END: hidden command'
+echo '.END: run always'
+.END: run always
+exit status 0
diff --git a/contrib/bmake/unit-tests/opt-no-action.mk b/contrib/bmake/unit-tests/opt-no-action.mk
new file mode 100644
index 000000000000..32b3b1564acb
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-no-action.mk
@@ -0,0 +1,33 @@
+# $NetBSD: opt-no-action.mk,v 1.3 2020/08/19 05:25:26 rillig Exp $
+#
+# Tests for the -n command line option, which runs almost no commands.
+# It just outputs them, to be inspected by human readers.
+# Only commands that are in a .MAKE target or prefixed by '+' are run.
+
+# This command cannot be prevented from being run since it is used at parse
+# time, and any later variable assignments may depend on its result.
+!= echo 'command during parsing' 1>&2; echo
+
+all: main
+all: run-always
+
+# Both of these commands are printed, but only the '+' command is run.
+.BEGIN:
+ @echo '$@: hidden command'
+ @+echo '$@: run always'
+
+# Both of these commands are printed, but only the '+' command is run.
+main:
+ @echo '$@: hidden command'
+ @+echo '$@: run always'
+
+# None of these commands is printed, but both are run, because this target
+# depends on the special source ".MAKE".
+run-always: .MAKE
+ @echo '$@: hidden command'
+ @+echo '$@: run always'
+
+# Both of these commands are printed, but only the '+' command is run.
+.END:
+ @echo '$@: hidden command'
+ @+echo '$@: run always'
diff --git a/contrib/bmake/unit-tests/opt-query.exp b/contrib/bmake/unit-tests/opt-query.exp
new file mode 100644
index 000000000000..38025dcf4d3a
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-query.exp
@@ -0,0 +1,2 @@
+command during parsing
+exit status 1
diff --git a/contrib/bmake/unit-tests/opt-query.mk b/contrib/bmake/unit-tests/opt-query.mk
new file mode 100644
index 000000000000..04e605991140
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-query.mk
@@ -0,0 +1,24 @@
+# $NetBSD: opt-query.mk,v 1.3 2020/08/19 05:13:18 rillig Exp $
+#
+# Tests for the -q command line option.
+#
+# The -q option only looks at the dependencies between the targets.
+# None of the commands in the targets are run, not even those that are
+# prefixed with '+'.
+
+# This command cannot be prevented from being run since it is used at parse
+# time, and any later variable assignments may depend on its result.
+!= echo 'command during parsing' 1>&2; echo
+
+# None of these commands are run.
+.BEGIN:
+ @echo '$@: hidden command'
+ @+echo '$@: run always'
+
+# None of these commands are run.
+all:
+ @echo '$@: hidden command'
+ @+echo '$@: run always'
+
+# The exit status 1 is because the "all" target has to be made, that is,
+# it is not up-to-date.
diff --git a/contrib/bmake/unit-tests/opt-raw.exp b/contrib/bmake/unit-tests/opt-raw.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-raw.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/opt-raw.mk b/contrib/bmake/unit-tests/opt-raw.mk
new file mode 100644
index 000000000000..d3591bb99dab
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-raw.mk
@@ -0,0 +1,8 @@
+# $NetBSD: opt-raw.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the -r command line option.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/opt-silent.exp b/contrib/bmake/unit-tests/opt-silent.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-silent.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/opt-silent.mk b/contrib/bmake/unit-tests/opt-silent.mk
new file mode 100644
index 000000000000..7822d46ac48a
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-silent.mk
@@ -0,0 +1,8 @@
+# $NetBSD: opt-silent.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the -s command line option.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/opt-touch.exp b/contrib/bmake/unit-tests/opt-touch.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-touch.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/opt-touch.mk b/contrib/bmake/unit-tests/opt-touch.mk
new file mode 100644
index 000000000000..5093c5cad6ac
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-touch.mk
@@ -0,0 +1,8 @@
+# $NetBSD: opt-touch.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the -t command line option.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/opt-tracefile.exp b/contrib/bmake/unit-tests/opt-tracefile.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-tracefile.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/opt-tracefile.mk b/contrib/bmake/unit-tests/opt-tracefile.mk
new file mode 100644
index 000000000000..b62392ca913c
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-tracefile.mk
@@ -0,0 +1,8 @@
+# $NetBSD: opt-tracefile.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the -T command line option.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/opt-var-expanded.exp b/contrib/bmake/unit-tests/opt-var-expanded.exp
new file mode 100644
index 000000000000..4fcc0b99ae1b
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-var-expanded.exp
@@ -0,0 +1,3 @@
+other value $$
+value
+exit status 0
diff --git a/contrib/bmake/unit-tests/opt-var-expanded.mk b/contrib/bmake/unit-tests/opt-var-expanded.mk
new file mode 100644
index 000000000000..0b4088a82082
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-var-expanded.mk
@@ -0,0 +1,6 @@
+# $NetBSD: opt-var-expanded.mk,v 1.3 2020/08/23 14:28:04 rillig Exp $
+#
+# Tests for the -v command line option.
+
+VAR= other ${VALUE} $$$$
+VALUE= value
diff --git a/contrib/bmake/unit-tests/opt-var-literal.exp b/contrib/bmake/unit-tests/opt-var-literal.exp
new file mode 100644
index 000000000000..e7f653b769b9
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-var-literal.exp
@@ -0,0 +1,3 @@
+other ${VALUE} $$$$
+value
+exit status 0
diff --git a/contrib/bmake/unit-tests/opt-var-literal.mk b/contrib/bmake/unit-tests/opt-var-literal.mk
new file mode 100644
index 000000000000..a819e7537105
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-var-literal.mk
@@ -0,0 +1,6 @@
+# $NetBSD: opt-var-literal.mk,v 1.3 2020/08/23 14:28:04 rillig Exp $
+#
+# Tests for the -V command line option.
+
+VAR= other ${VALUE} $$$$
+VALUE= value
diff --git a/contrib/bmake/unit-tests/opt-warnings-as-errors.exp b/contrib/bmake/unit-tests/opt-warnings-as-errors.exp
new file mode 100644
index 000000000000..bd54bb673f08
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-warnings-as-errors.exp
@@ -0,0 +1,7 @@
+make: "opt-warnings-as-errors.mk" line 5: warning: message 1
+make: parsing warnings being treated as errors
+make: "opt-warnings-as-errors.mk" line 6: warning: message 2
+parsing continues
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/opt-warnings-as-errors.mk b/contrib/bmake/unit-tests/opt-warnings-as-errors.mk
new file mode 100644
index 000000000000..905753410db0
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-warnings-as-errors.mk
@@ -0,0 +1,11 @@
+# $NetBSD: opt-warnings-as-errors.mk,v 1.3 2020/08/23 14:28:04 rillig Exp $
+#
+# Tests for the -W command line option, which turns warnings into errors.
+
+.warning message 1
+.warning message 2
+
+_!= echo 'parsing continues' 1>&2
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/opt-where-am-i.exp b/contrib/bmake/unit-tests/opt-where-am-i.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-where-am-i.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/opt-where-am-i.mk b/contrib/bmake/unit-tests/opt-where-am-i.mk
new file mode 100644
index 000000000000..9158a598174c
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-where-am-i.mk
@@ -0,0 +1,8 @@
+# $NetBSD: opt-where-am-i.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the -w command line option.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/opt-x-reduce-exported.exp b/contrib/bmake/unit-tests/opt-x-reduce-exported.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-x-reduce-exported.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/opt-x-reduce-exported.mk b/contrib/bmake/unit-tests/opt-x-reduce-exported.mk
new file mode 100644
index 000000000000..7ee8e7c7eff0
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-x-reduce-exported.mk
@@ -0,0 +1,8 @@
+# $NetBSD: opt-x-reduce-exported.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the -x command line option.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/opt.exp b/contrib/bmake/unit-tests/opt.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/opt.mk b/contrib/bmake/unit-tests/opt.mk
new file mode 100644
index 000000000000..eae430965df7
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt.mk
@@ -0,0 +1,8 @@
+# $NetBSD: opt.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the command line options.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/phony-end.exp b/contrib/bmake/unit-tests/phony-end.exp
index c3c517ccc25c..f1bff4ef6d5e 100644
--- a/contrib/bmake/unit-tests/phony-end.exp
+++ b/contrib/bmake/unit-tests/phony-end.exp
@@ -1,5 +1,5 @@
.TARGET="phony" .PREFIX="phony" .IMPSRC=""
-.TARGET="all" .PREFIX="all" .IMPSRC="phony"
+.TARGET="all" .PREFIX="all" .IMPSRC=""
.TARGET="ok" .PREFIX="ok" .IMPSRC=""
.TARGET="also.ok" .PREFIX="also.ok" .IMPSRC=""
.TARGET="bug" .PREFIX="bug" .IMPSRC=""
diff --git a/contrib/bmake/unit-tests/posix1.mk b/contrib/bmake/unit-tests/posix1.mk
index 50b0a63ee696..f5d8e21678ea 100644
--- a/contrib/bmake/unit-tests/posix1.mk
+++ b/contrib/bmake/unit-tests/posix1.mk
@@ -1,4 +1,4 @@
-# $NetBSD: posix1.mk,v 1.3 2014/08/30 22:21:08 sjg Exp $
+# $NetBSD: posix1.mk,v 1.4 2020/08/10 18:19:58 rillig Exp $
# Keep the default suffixes from interfering, just in case.
.SUFFIXES:
@@ -9,6 +9,8 @@ all: line-continuations suffix-substitution localvars
.BEGIN: clean
clean:
@rm -f lib.a dir/* dummy obj*
+.END:
+ @rm -f lib.a dir/* dummy obj*
#
# Line continuations
diff --git a/contrib/bmake/unit-tests/recursive.exp b/contrib/bmake/unit-tests/recursive.exp
new file mode 100644
index 000000000000..bb5db75a474c
--- /dev/null
+++ b/contrib/bmake/unit-tests/recursive.exp
@@ -0,0 +1,5 @@
+make: "recursive.mk" line 34: Unclosed variable "MISSING_PAREN"
+make: "recursive.mk" line 35: Unclosed variable "MISSING_BRACE"
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/recursive.mk b/contrib/bmake/unit-tests/recursive.mk
new file mode 100644
index 000000000000..bc5a2817b333
--- /dev/null
+++ b/contrib/bmake/unit-tests/recursive.mk
@@ -0,0 +1,37 @@
+# $NetBSD: recursive.mk,v 1.2 2020/08/06 05:52:45 rillig Exp $
+#
+# In -dL mode, a variable may get expanded before it makes sense.
+# This would stop make from doing anything since the "recursive" error
+# is fatal and exits immediately.
+#
+# The purpose of evaluating that variable early was just to detect
+# whether there are unclosed variables. It might be enough to parse the
+# variable value without VARE_WANTRES for that purpose.
+#
+# Seen in pkgsrc/x11/libXfixes, and probably many more package that use
+# GNU Automake.
+
+AM_V_lt = $(am__v_lt_$(V))
+am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY))
+am__v_lt_0 = --silent
+am__v_lt_1 =
+
+# On 2020-08-06, make reported: "Variable am__v_lt_ is recursive."
+libXfixes_la_LINK = ... $(AM_V_lt) ...
+
+# somewhere later ...
+AM_DEFAULT_VERBOSITY = 1
+
+
+# The purpose of the -dL flag is to detect unclosed variables. This
+# can be achieved by just parsing the variable and not evaluating it.
+#
+# When the variable is only parsed but not evaluated, bugs in nested
+# variables are not discovered. But these are hard to produce anyway,
+# therefore that's acceptable. In most practical cases, the missing
+# brace would be detected directly in the line where it is produced.
+MISSING_BRACE_INDIRECT:= ${:U\${MISSING_BRACE}
+UNCLOSED = $(MISSING_PAREN
+UNCLOSED = ${MISSING_BRACE
+UNCLOSED = ${MISSING_BRACE_INDIRECT}
+
diff --git a/contrib/bmake/unit-tests/sh-dots.exp b/contrib/bmake/unit-tests/sh-dots.exp
new file mode 100755
index 000000000000..19482717087b
--- /dev/null
+++ b/contrib/bmake/unit-tests/sh-dots.exp
@@ -0,0 +1,15 @@
+first first
+hidden hidden
+make: exec(...) failed (No such file or directory)
+hidden delayed hidden
+repeated repeated
+commented commented
+*** Error code 1 (ignored)
+... # Run the below commands later
+<normalized: ...: not found>
+commented delayed commented
+first delayed first
+repeated delayed repeated
+repeated delayed twice repeated
+*** Error code 127 (ignored)
+exit status 0
diff --git a/contrib/bmake/unit-tests/sh-dots.mk b/contrib/bmake/unit-tests/sh-dots.mk
new file mode 100755
index 000000000000..36da5bce7a53
--- /dev/null
+++ b/contrib/bmake/unit-tests/sh-dots.mk
@@ -0,0 +1,37 @@
+# $NetBSD: sh-dots.mk,v 1.1 2020/08/22 11:27:02 rillig Exp $
+#
+# Tests for the special shell command line "...", which does not run the
+# commands below it but appends them to the list of commands that are run
+# at the end.
+
+all: first hidden repeated commented
+
+# The ${.TARGET} correctly expands to the target name, even though the
+# commands are run separately from the main commands.
+first:
+ @echo first ${.TARGET}
+ ...
+ @echo first delayed ${.TARGET}
+
+# The dots cannot be prefixed by the usual @-+ characters.
+# They must be written exactly as dots.
+hidden: .IGNORE
+ @echo hidden ${.TARGET}
+ @...
+ @echo hidden delayed ${.TARGET}
+
+# Since the shell command lines don't recognize '#' as comment character,
+# the "..." is not interpreted specially here.
+commented: .IGNORE
+ @echo commented ${.TARGET}
+ ... # Run the below commands later
+ @echo commented delayed ${.TARGET}
+
+# The "..." can appear more than once, even though that doesn't make sense.
+# The second "..." is a no-op.
+repeated: .IGNORE
+ @echo repeated ${.TARGET}
+ ...
+ @echo repeated delayed ${.TARGET}
+ ...
+ @echo repeated delayed twice ${.TARGET}
diff --git a/contrib/bmake/unit-tests/sh-jobs-error.exp b/contrib/bmake/unit-tests/sh-jobs-error.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/sh-jobs-error.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/sh-jobs-error.mk b/contrib/bmake/unit-tests/sh-jobs-error.mk
new file mode 100644
index 000000000000..febc06999366
--- /dev/null
+++ b/contrib/bmake/unit-tests/sh-jobs-error.mk
@@ -0,0 +1,9 @@
+# $NetBSD: sh-jobs-error.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for error handling in the "run in jobs mode" part of the "Shell
+# Commands" section from the manual page.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/sh-jobs.exp b/contrib/bmake/unit-tests/sh-jobs.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/sh-jobs.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/sh-jobs.mk b/contrib/bmake/unit-tests/sh-jobs.mk
new file mode 100644
index 000000000000..62172c2a0c86
--- /dev/null
+++ b/contrib/bmake/unit-tests/sh-jobs.mk
@@ -0,0 +1,9 @@
+# $NetBSD: sh-jobs.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the "run in jobs mode" part of the "Shell Commands" section
+# from the manual page.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/sh-leading-at.exp b/contrib/bmake/unit-tests/sh-leading-at.exp
new file mode 100644
index 000000000000..5ffa84690a40
--- /dev/null
+++ b/contrib/bmake/unit-tests/sh-leading-at.exp
@@ -0,0 +1,5 @@
+ok
+space after @
+echo 'echoed'
+echoed
+exit status 0
diff --git a/contrib/bmake/unit-tests/sh-leading-at.mk b/contrib/bmake/unit-tests/sh-leading-at.mk
new file mode 100644
index 000000000000..19a6e59e4e6a
--- /dev/null
+++ b/contrib/bmake/unit-tests/sh-leading-at.mk
@@ -0,0 +1,10 @@
+# $NetBSD: sh-leading-at.mk,v 1.3 2020/08/22 09:16:08 rillig Exp $
+#
+# Tests for shell commands preceded by an '@', to suppress printing
+# the command to stdout.
+
+all:
+ @
+ @echo 'ok'
+ @ echo 'space after @'
+ echo 'echoed'
diff --git a/contrib/bmake/unit-tests/sh-leading-hyphen.exp b/contrib/bmake/unit-tests/sh-leading-hyphen.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/sh-leading-hyphen.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/sh-leading-hyphen.mk b/contrib/bmake/unit-tests/sh-leading-hyphen.mk
new file mode 100644
index 000000000000..94be43495afb
--- /dev/null
+++ b/contrib/bmake/unit-tests/sh-leading-hyphen.mk
@@ -0,0 +1,9 @@
+# $NetBSD: sh-leading-hyphen.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for shell commands preceded by a '-', to ignore the exit status of
+# the command line.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/sh-leading-plus.exp b/contrib/bmake/unit-tests/sh-leading-plus.exp
new file mode 100644
index 000000000000..eb586d29f1c2
--- /dev/null
+++ b/contrib/bmake/unit-tests/sh-leading-plus.exp
@@ -0,0 +1,4 @@
+echo 'this command is not run'
+echo 'this command is run'
+this command is run
+exit status 0
diff --git a/contrib/bmake/unit-tests/sh-leading-plus.mk b/contrib/bmake/unit-tests/sh-leading-plus.mk
new file mode 100644
index 000000000000..75279d7d57fd
--- /dev/null
+++ b/contrib/bmake/unit-tests/sh-leading-plus.mk
@@ -0,0 +1,8 @@
+# $NetBSD: sh-leading-plus.mk,v 1.3 2020/08/23 14:46:33 rillig Exp $
+#
+# Tests for shell commands preceded by a '+', to run them even if
+# the command line option -n is given.
+
+all:
+ @echo 'this command is not run'
+ @+echo 'this command is run'
diff --git a/contrib/bmake/unit-tests/sh-meta-chars.exp b/contrib/bmake/unit-tests/sh-meta-chars.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/sh-meta-chars.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/sh-meta-chars.mk b/contrib/bmake/unit-tests/sh-meta-chars.mk
new file mode 100644
index 000000000000..126ca2ceb118
--- /dev/null
+++ b/contrib/bmake/unit-tests/sh-meta-chars.mk
@@ -0,0 +1,11 @@
+# $NetBSD: sh-meta-chars.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for running shell commands that contain meta-characters.
+#
+# These meta-characters decide whether the command is run by the shell
+# or executed directly via execv. See Cmd_Exec for details.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/sh-multi-line.exp b/contrib/bmake/unit-tests/sh-multi-line.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/sh-multi-line.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/sh-multi-line.mk b/contrib/bmake/unit-tests/sh-multi-line.mk
new file mode 100644
index 000000000000..35a773f5dde6
--- /dev/null
+++ b/contrib/bmake/unit-tests/sh-multi-line.mk
@@ -0,0 +1,9 @@
+# $NetBSD: sh-multi-line.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for multi-line shell commands, to ensure that the line breaks
+# are preserved.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/sh-single-line.exp b/contrib/bmake/unit-tests/sh-single-line.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/sh-single-line.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/sh-single-line.mk b/contrib/bmake/unit-tests/sh-single-line.mk
new file mode 100644
index 000000000000..9dae7f80c9a9
--- /dev/null
+++ b/contrib/bmake/unit-tests/sh-single-line.mk
@@ -0,0 +1,12 @@
+# $NetBSD: sh-single-line.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for running single-line shell commands.
+#
+# In jobs mode, the shell commands are combined into a single shell
+# program, as described in the manual page, section "Shell Commands",
+# "the entire script".
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/sh.exp b/contrib/bmake/unit-tests/sh.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/sh.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/sh.mk b/contrib/bmake/unit-tests/sh.mk
new file mode 100644
index 000000000000..f79a4099e990
--- /dev/null
+++ b/contrib/bmake/unit-tests/sh.mk
@@ -0,0 +1,9 @@
+# $NetBSD: sh.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for running shell commands from the targets, or from the != variable
+# assignment operator or the :sh variable modifier.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/sysv.exp b/contrib/bmake/unit-tests/sysv.exp
index 780a187783f3..610f97c39e85 100644
--- a/contrib/bmake/unit-tests/sysv.exp
+++ b/contrib/bmake/unit-tests/sysv.exp
@@ -12,5 +12,4 @@ asam.c.c
asam.c
a.c.c
-ax:Q b c d eb
exit status 0
diff --git a/contrib/bmake/unit-tests/sysv.mk b/contrib/bmake/unit-tests/sysv.mk
index 3a987441ee42..5c87579cc11b 100644
--- a/contrib/bmake/unit-tests/sysv.mk
+++ b/contrib/bmake/unit-tests/sysv.mk
@@ -1,4 +1,6 @@
-# $Id: sysv.mk,v 1.5 2020/07/04 18:16:55 sjg Exp $
+# $Id: sysv.mk,v 1.9 2020/08/23 16:08:32 sjg Exp $
+
+all: foo fun sam bla
FOO ?=
FOOBAR = ${FOO:=bar}
@@ -11,8 +13,6 @@ FUN = ${B}${S}fun
SUN = the Sun
# we expect nothing when FOO is empty
-all: foo fun sam bla words
-
foo:
@echo FOOBAR = ${FOOBAR}
.if empty(FOO)
@@ -41,8 +41,3 @@ BLA=
bla:
@echo $(BLA:%=foo/%x)
-
-# The :Q looks like a modifier but isn't.
-# It is part of the replacement string.
-words:
- @echo a${a b c d e:L:%a=x:Q}b
diff --git a/contrib/bmake/unit-tests/unexport-env.mk b/contrib/bmake/unit-tests/unexport-env.mk
index aaabcd46464a..bc5fb4914ddc 100644
--- a/contrib/bmake/unit-tests/unexport-env.mk
+++ b/contrib/bmake/unit-tests/unexport-env.mk
@@ -1,6 +1,7 @@
-# $Id: unexport-env.mk,v 1.1.1.1 2014/08/30 18:57:18 sjg Exp $
+# $Id: unexport-env.mk,v 1.1.1.2 2020/07/28 16:57:18 sjg Exp $
# pick up a bunch of exported vars
+FILTER_CMD= grep ^UT_
.include "export.mk"
# an example of setting up a minimal environment.
diff --git a/contrib/bmake/unit-tests/unexport.mk b/contrib/bmake/unit-tests/unexport.mk
index 0f1245292ba5..7d2e8f275173 100644
--- a/contrib/bmake/unit-tests/unexport.mk
+++ b/contrib/bmake/unit-tests/unexport.mk
@@ -1,8 +1,19 @@
-# $Id: unexport.mk,v 1.1.1.1 2014/08/30 18:57:18 sjg Exp $
+# $Id: unexport.mk,v 1.1.1.3 2020/08/08 22:34:25 sjg Exp $
# pick up a bunch of exported vars
+FILTER_CMD= grep ^UT_
.include "export.mk"
.unexport UT_ZOO UT_FOO
UT_TEST = unexport
+
+# Until 2020-08-08, Var_UnExport had special handling for '\n', that code
+# was not reachable though. At that point, backslash-newline has already
+# been replaced with a simple space, and variables are not yet expanded.
+UT_BEFORE_NL= before
+UT_AFTER_NL= after
+.export UT_BEFORE_NL UT_AFTER_NL
+.unexport \
+ UT_BEFORE_NL
+.unexport ${.newline} UT_AFTER_NL
diff --git a/contrib/bmake/unit-tests/use-inference.exp b/contrib/bmake/unit-tests/use-inference.exp
new file mode 100644
index 000000000000..14ecf0550574
--- /dev/null
+++ b/contrib/bmake/unit-tests/use-inference.exp
@@ -0,0 +1,4 @@
+Building use-inference.from from nothing
+make: don't know how to make use-inference.to (continuing)
+`all' not remade because of errors.
+exit status 0
diff --git a/contrib/bmake/unit-tests/use-inference.mk b/contrib/bmake/unit-tests/use-inference.mk
new file mode 100644
index 000000000000..b0e5017bc6fb
--- /dev/null
+++ b/contrib/bmake/unit-tests/use-inference.mk
@@ -0,0 +1,35 @@
+# $NetBSD: use-inference.mk,v 1.1 2020/08/09 16:32:28 rillig Exp $
+#
+# Demonstrate that .USE rules do not have an effect on inference rules.
+# At least not in the special case where the inference rule does not
+# have any associated commands.
+
+.SUFFIXES:
+.SUFFIXES: .from .to
+
+all: use-inference.to
+
+verbose: .USE
+ @echo 'Verbosely making $@ out of $>'
+
+.from.to: verbose
+# Since this inference rule does not have any associated commands, it
+# is ignored.
+#
+# @echo 'Building $@ from $<'
+
+use-inference.from: # assume it exists
+ @echo 'Building $@ from nothing'
+
+# Possible but unproven explanation:
+#
+# The main target is "all", which depends on "use-inference.to".
+# The inference connects the .from to the .to file, otherwise make
+# would not know that the .from file would need to be built.
+#
+# The .from file is then built.
+#
+# After this, make stops since it doesn't know how to make the .to file.
+# This is strange since make definitely knows about the .from.to suffix
+# inference rule. But it seems to ignore it, maybe because it doesn't
+# have any associated commands.
diff --git a/contrib/bmake/unit-tests/var-class-cmdline.exp b/contrib/bmake/unit-tests/var-class-cmdline.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-class-cmdline.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/var-class-cmdline.mk b/contrib/bmake/unit-tests/var-class-cmdline.mk
new file mode 100644
index 000000000000..c43b5351c329
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-class-cmdline.mk
@@ -0,0 +1,8 @@
+# $NetBSD: var-class-cmdline.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for variables specified on the command line.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/var-class-env.exp b/contrib/bmake/unit-tests/var-class-env.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-class-env.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/var-class-env.mk b/contrib/bmake/unit-tests/var-class-env.mk
new file mode 100644
index 000000000000..6e6b4891d3fd
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-class-env.mk
@@ -0,0 +1,8 @@
+# $NetBSD: var-class-env.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for variables specified in the process environment.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/var-class-global.exp b/contrib/bmake/unit-tests/var-class-global.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-class-global.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/var-class-global.mk b/contrib/bmake/unit-tests/var-class-global.mk
new file mode 100644
index 000000000000..81345ffda463
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-class-global.mk
@@ -0,0 +1,8 @@
+# $NetBSD: var-class-global.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for global variables, which are the most common variables.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/var-class-local-legacy.exp b/contrib/bmake/unit-tests/var-class-local-legacy.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-class-local-legacy.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/var-class-local-legacy.mk b/contrib/bmake/unit-tests/var-class-local-legacy.mk
new file mode 100644
index 000000000000..bfd9733fd42b
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-class-local-legacy.mk
@@ -0,0 +1,8 @@
+# $NetBSD: var-class-local-legacy.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for legacy target-local variables, such as ${<F} or ${@D}.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/var-class-local.exp b/contrib/bmake/unit-tests/var-class-local.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-class-local.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/var-class-local.mk b/contrib/bmake/unit-tests/var-class-local.mk
new file mode 100644
index 000000000000..e75f08ba75a3
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-class-local.mk
@@ -0,0 +1,8 @@
+# $NetBSD: var-class-local.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for target-local variables, such as ${.TARGET} or $@.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/var-class.exp b/contrib/bmake/unit-tests/var-class.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-class.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/var-class.mk b/contrib/bmake/unit-tests/var-class.mk
new file mode 100644
index 000000000000..b20fca565e16
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-class.mk
@@ -0,0 +1,9 @@
+# $NetBSD: var-class.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the different variable classes (local, command-line, global,
+# environment), and which of them takes precedence over the others.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/var-op-append.exp b/contrib/bmake/unit-tests/var-op-append.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-op-append.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/var-op-append.mk b/contrib/bmake/unit-tests/var-op-append.mk
new file mode 100644
index 000000000000..b75880f95838
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-op-append.mk
@@ -0,0 +1,9 @@
+# $NetBSD: var-op-append.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the += variable assignment operator, which appends to a variable,
+# creating it if necessary.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/var-op-assign.exp b/contrib/bmake/unit-tests/var-op-assign.exp
new file mode 100644
index 000000000000..0e9e2d211a5f
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-op-assign.exp
@@ -0,0 +1,6 @@
+this will be evaluated later
+make: "var-op-assign.mk" line 52: Need an operator
+make: "var-op-assign.mk" line 86: Parsing still continues until here.
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/var-op-assign.mk b/contrib/bmake/unit-tests/var-op-assign.mk
new file mode 100644
index 000000000000..dadff6ed4b87
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-op-assign.mk
@@ -0,0 +1,89 @@
+# $NetBSD: var-op-assign.mk,v 1.4 2020/08/25 16:20:32 rillig Exp $
+#
+# Tests for the = variable assignment operator, which overwrites an existing
+# variable or creates it.
+
+# This is a simple variable assignment.
+# To the left of the assignment operator '=' there is the variable name,
+# and to the right is the variable value.
+#
+VAR= value
+
+# This condition demonstrates that whitespace around the assignment operator
+# is discarded. Otherwise the value would start with a single tab.
+#
+.if ${VAR} != "value"
+.error
+.endif
+
+# Whitespace to the left of the assignment operator is ignored as well.
+# The variable value can contain arbitrary characters.
+#
+# The '#' needs to be escaped with a backslash, this happens in a very
+# early stage of parsing and applies to all line types, except for the
+# commands, which are indented with a tab.
+#
+# The '$' needs to be escaped with another '$', otherwise it would refer to
+# another variable.
+#
+VAR =new value and \# some $$ special characters # comment
+
+# When a string literal appears in a condition, the escaping rules are
+# different. Run make with the -dc option to see the details.
+.if ${VAR} != "new value and \# some \$ special characters"
+.error ${VAR}
+.endif
+
+# The variable value may contain references to other variables.
+# In this example, the reference is to the variable with the empty name,
+# which always expands to an empty string. This alone would not produce
+# any side-effects, therefore the variable has a :!...! modifier that
+# executes a shell command.
+VAR= ${:! echo 'not yet evaluated' 1>&2 !}
+VAR= ${:! echo 'this will be evaluated later' 1>&2 !}
+
+# Now force the variable to be evaluated.
+# This outputs the line to stderr.
+.if ${VAR}
+.endif
+
+# In a variable assignment, the variable name must consist of a single word.
+#
+VARIABLE NAME= variable value
+
+# But if the whitespace appears inside parentheses or braces, everything is
+# fine.
+#
+# XXX: This was not an intentional decision, as variable names typically
+# neither contain parentheses nor braces. This is only a side-effect from
+# the implementation of the parser, which cheats when parsing a variable
+# name. It only counts parentheses and braces instead of properly parsing
+# nested variable expressions such as VAR.${param}.
+#
+VAR(spaces in parentheses)= ()
+VAR{spaces in braces}= {}
+
+# Be careful and use indirect variable names here, to prevent accidentally
+# accepting the test in case the parser just uses "VAR" as the variable name,
+# ignoring all the rest.
+#
+VARNAME_PAREN= VAR(spaces in parentheses)
+VARNAME_BRACES= VAR{spaces in braces}
+
+.if ${${VARNAME_PAREN}} != "()"
+.error
+.endif
+
+.if ${${VARNAME_BRACES}} != "{}"
+.error
+.endif
+
+# In safe mode, parsing would stop immediately after the "VARIABLE NAME="
+# line, since any commands run after that are probably working with
+# unexpected variable values.
+#
+# Therefore, just output an info message.
+.info Parsing still continues until here.
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/var-op-default.exp b/contrib/bmake/unit-tests/var-op-default.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-op-default.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/var-op-default.mk b/contrib/bmake/unit-tests/var-op-default.mk
new file mode 100644
index 000000000000..afb0c55f827c
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-op-default.mk
@@ -0,0 +1,9 @@
+# $NetBSD: var-op-default.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the ?= variable assignment operator, which only assigns
+# if the variable is still undefined.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/var-op-expand.exp b/contrib/bmake/unit-tests/var-op-expand.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-op-expand.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/var-op-expand.mk b/contrib/bmake/unit-tests/var-op-expand.mk
new file mode 100644
index 000000000000..07c5fb647759
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-op-expand.mk
@@ -0,0 +1,9 @@
+# $NetBSD: var-op-expand.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the := variable assignment operator, which expands its
+# right-hand side.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/var-op-shell.exp b/contrib/bmake/unit-tests/var-op-shell.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-op-shell.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/var-op-shell.mk b/contrib/bmake/unit-tests/var-op-shell.mk
new file mode 100644
index 000000000000..83580a89e6c2
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-op-shell.mk
@@ -0,0 +1,9 @@
+# $NetBSD: var-op-shell.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the != variable assignment operator, which runs its right-hand
+# side through the shell.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/var-op.exp b/contrib/bmake/unit-tests/var-op.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-op.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/var-op.mk b/contrib/bmake/unit-tests/var-op.mk
new file mode 100644
index 000000000000..9c9ffb8782f5
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-op.mk
@@ -0,0 +1,8 @@
+# $NetBSD: var-op.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the variable assignment operators.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/vardebug.exp b/contrib/bmake/unit-tests/vardebug.exp
new file mode 100644
index 000000000000..8a9eefad4b0c
--- /dev/null
+++ b/contrib/bmake/unit-tests/vardebug.exp
@@ -0,0 +1,80 @@
+Global:RELEVANT = yes
+Global:VAR = added
+Global:VAR = overwritten
+Global:delete VAR
+Global:delete VAR (not found)
+Var_Parse: ${:U} with VARE_WANTRES
+Applying ${:U} to "" (eflags = VARE_WANTRES, vflags = VAR_JUNK)
+Result of ${:U} is "" (eflags = VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP)
+Var_Set("${:U}", "empty name", ...) name expands to empty string - ignored
+Var_Parse: ${:U} with VARE_WANTRES
+Applying ${:U} to "" (eflags = VARE_WANTRES, vflags = VAR_JUNK)
+Result of ${:U} is "" (eflags = VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP)
+Var_Append("${:U}", "empty name", ...) name expands to empty string - ignored
+Global:FROM_CMDLINE = overwritten ignored!
+Global:VAR = 1
+Global:VAR = 1 2
+Global:VAR = 1 2 3
+Var_Parse: ${VAR:M[2]} with VARE_UNDEFERR|VARE_WANTRES
+Applying ${VAR:M...} to "1 2 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none)
+Pattern[VAR] for [1 2 3] is [[2]]
+ModifyWords: split "1 2 3" into 3 words
+VarMatch [1] [[2]]
+VarMatch [2] [[2]]
+VarMatch [3] [[2]]
+Result of ${VAR:M[2]} is "2" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none)
+Var_Parse: ${VAR:N[2]} with VARE_UNDEFERR|VARE_WANTRES
+Applying ${VAR:N...} to "1 2 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none)
+Pattern[VAR] for [1 2 3] is [[2]]
+ModifyWords: split "1 2 3" into 3 words
+Result of ${VAR:N[2]} is "1 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none)
+Var_Parse: ${VAR:S,2,two,} with VARE_UNDEFERR|VARE_WANTRES
+Applying ${VAR:S...} to "1 2 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none)
+Modifier part: "2"
+Modifier part: "two"
+ModifyWords: split "1 2 3" into 3 words
+Result of ${VAR:S,2,two,} is "1 two 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none)
+Var_Parse: ${VAR:Q} with VARE_UNDEFERR|VARE_WANTRES
+Applying ${VAR:Q} to "1 2 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none)
+QuoteMeta: [1\ 2\ 3]
+Result of ${VAR:Q} is "1\ 2\ 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none)
+Var_Parse: ${VAR:tu:tl:Q} with VARE_UNDEFERR|VARE_WANTRES
+Applying ${VAR:t...} to "1 2 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none)
+Result of ${VAR:tu} is "1 2 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none)
+Applying ${VAR:t...} to "1 2 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none)
+Result of ${VAR:tl} is "1 2 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none)
+Applying ${VAR:Q} to "1 2 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none)
+QuoteMeta: [1\ 2\ 3]
+Result of ${VAR:Q} is "1\ 2\ 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none)
+Var_Parse: ${:Uvalue:${:UM*e}:Mvalu[e]} with VARE_UNDEFERR|VARE_WANTRES
+Applying ${:U...} to "" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK)
+Result of ${:Uvalue} is "value" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP)
+Var_Parse: ${:UM*e}:Mvalu[e]} with VARE_UNDEFERR|VARE_WANTRES
+Applying ${:U...} to "" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK)
+Result of ${:UM*e} is "M*e" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP)
+Indirect modifier "M*e" from "${:UM*e}"
+Applying ${:M...} to "value" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP)
+Pattern[] for [value] is [*e]
+ModifyWords: split "value" into 1 words
+VarMatch [value] [*e]
+Result of ${:M*e} is "value" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP)
+Applying ${:M...} to "value" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP)
+Pattern[] for [value] is [valu[e]]
+ModifyWords: split "value" into 1 words
+VarMatch [value] [valu[e]]
+Result of ${:Mvalu[e]} is "value" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP)
+Var_Parse: ${:UVAR} with VARE_WANTRES
+Applying ${:U...} to "" (eflags = VARE_WANTRES, vflags = VAR_JUNK)
+Result of ${:UVAR} is "VAR" (eflags = VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP)
+Global:delete VAR
+Var_Parse: ${:Uvariable:unknown} with VARE_UNDEFERR|VARE_WANTRES
+Applying ${:U...} to "" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK)
+Result of ${:Uvariable} is "variable" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP)
+Applying ${:u...} to "variable" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP)
+make: Unknown modifier 'u'
+Result of ${:unknown} is error (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP)
+make: "vardebug.mk" line 44: Malformed conditional (${:Uvariable:unknown})
+Var_Parse: ${UNDEFINED} with VARE_UNDEFERR|VARE_WANTRES
+make: "vardebug.mk" line 53: Malformed conditional (${UNDEFINED})
+Global:RELEVANT = no
+exit status 1
diff --git a/contrib/bmake/unit-tests/vardebug.mk b/contrib/bmake/unit-tests/vardebug.mk
new file mode 100644
index 000000000000..f61df98d50de
--- /dev/null
+++ b/contrib/bmake/unit-tests/vardebug.mk
@@ -0,0 +1,59 @@
+# $NetBSD: vardebug.mk,v 1.3 2020/08/08 14:28:46 rillig Exp $
+#
+# Demonstrates the debugging output for var.c.
+
+RELEVANT= yes
+
+VAR= added # VarAdd
+VAR= overwritten # Var_Set
+.undef VAR # Var_Delete (found)
+.undef VAR # Var_Delete (not found)
+
+# The variable with the empty name cannot be set at all.
+${:U}= empty name # Var_Set
+${:U}+= empty name # Var_Append
+
+FROM_CMDLINE= overwritten # Var_Set (ignored)
+
+VAR= 1
+VAR+= 2
+VAR+= 3
+
+.if ${VAR:M[2]} # VarMatch
+.endif
+.if ${VAR:N[2]} # VarNoMatch (no debug output)
+.endif
+
+.if ${VAR:S,2,two,} # VarGetPattern
+.endif
+
+.if ${VAR:Q} # VarQuote
+.endif
+
+.if ${VAR:tu:tl:Q} # ApplyModifiers
+.endif
+
+# ApplyModifiers, "Got ..."
+.if ${:Uvalue:${:UM*e}:Mvalu[e]}
+.endif
+
+.undef ${:UVAR} # Var_Delete
+
+# When ApplyModifiers results in an error, this appears in the debug log
+# as "is error", without surrounding quotes.
+.if ${:Uvariable:unknown}
+.endif
+
+# XXX: The error message is "Malformed conditional", which is wrong.
+# The condition is syntactically fine, it just contains an undefined variable.
+#
+# There is a specialized error message for "Undefined variable", but as of
+# 2020-08-08, that is not covered by any unit tests. It might even be
+# unreachable.
+.if ${UNDEFINED}
+.endif
+
+RELEVANT= no
+
+all:
+ @:
diff --git a/contrib/bmake/unit-tests/varfind.exp b/contrib/bmake/unit-tests/varfind.exp
new file mode 100644
index 000000000000..13d8d60226a9
--- /dev/null
+++ b/contrib/bmake/unit-tests/varfind.exp
@@ -0,0 +1,15 @@
+VarFind-aliases.to: long explicit-dependency VarFind-aliases.from
+VarFind-aliases.to: abbr explicit-dependency VarFind-aliases.from
+VarFind-aliases.to: long
+VarFind-aliases.to: abbr
+VarFind-aliases.to: long VarFind-aliases.from
+VarFind-aliases.to: abbr VarFind-aliases.from
+VarFind-aliases.to: long
+VarFind-aliases.to: abbr
+VarFind-aliases.to: long explicit-dependency VarFind-aliases.from
+VarFind-aliases.to: abbr explicit-dependency VarFind-aliases.from
+VarFind-aliases.to: long VarFind-aliases
+VarFind-aliases.to: abbr VarFind-aliases
+VarFind-aliases.to: long VarFind-aliases.to
+VarFind-aliases.to: abbr VarFind-aliases.to
+exit status 0
diff --git a/contrib/bmake/unit-tests/varfind.mk b/contrib/bmake/unit-tests/varfind.mk
new file mode 100644
index 000000000000..6fb7bc630eaf
--- /dev/null
+++ b/contrib/bmake/unit-tests/varfind.mk
@@ -0,0 +1,31 @@
+# $NetBSD: varfind.mk,v 1.1 2020/07/25 21:19:29 rillig Exp $
+#
+# Demonstrates variable name aliases in VarFind.
+
+all: VarFind-aliases.to
+
+.SUFFIXES: .from .to
+
+VarFind-aliases.from:
+ @: do nothing
+
+VarFind-aliases.to: explicit-dependency
+
+explicit-dependency:
+ @: do nothing
+
+.from.to:
+ @echo $@: long ${.ALLSRC:Q}
+ @echo $@: abbr ${>:Q}
+ @echo $@: long ${.ARCHIVE:Q}
+ @echo $@: abbr ${!:Q}
+ @echo $@: long ${.IMPSRC:Q}
+ @echo $@: abbr ${<:Q}
+ @echo $@: long ${.MEMBER:Q}
+ @echo $@: abbr ${%:Q}
+ @echo $@: long ${.OODATE:Q}
+ @echo $@: abbr ${?:Q}
+ @echo $@: long ${.PREFIX:Q}
+ @echo $@: abbr ${*:Q}
+ @echo $@: long ${.TARGET:Q}
+ @echo $@: abbr ${@:Q}
diff --git a/contrib/bmake/unit-tests/varmisc.exp b/contrib/bmake/unit-tests/varmisc.exp
index b9a29141ce6b..e8f88d9ca51f 100644
--- a/contrib/bmake/unit-tests/varmisc.exp
+++ b/contrib/bmake/unit-tests/varmisc.exp
@@ -23,4 +23,52 @@ Version=123.456.789 == 123456789
Literal=3.4.5 == 3004005
We have target specific vars
MAN= make.1
+save-dollars: 0 = $
+save-dollars: 1 = $$
+save-dollars: 2 = $$
+save-dollars: False = $
+save-dollars: True = $$
+save-dollars: false = $
+save-dollars: true = $$
+save-dollars: Yes = $$
+save-dollars: No = $
+save-dollars: yes = $$
+save-dollars: no = $
+save-dollars: On = $$
+save-dollars: Off = $
+save-dollars: ON = $$
+save-dollars: OFF = $
+save-dollars: on = $$
+save-dollars: off = $
+export-appended: env
+export-appended: env
+export-appended: env mk
+parse-dynamic: parse-dynamic parse-dynamic before
+parse-dynamic: parse-dynamic parse-dynamic after
+parse-dynamic: parse-dynamic parse-dynamic after
+varerror-unclosed:begin
+make: Unclosed variable ""
+
+make: Unclosed variable "UNCLOSED"
+
+make: Unclosed variable "UNCLOSED"
+
+make: Unclosed variable "PATTERN"
+make: Unclosed variable specification (expecting '}') for "UNCLOSED" (value "") modifier M
+
+make: Unclosed variable "param"
+make: Unclosed variable "UNCLOSED."
+
+
+make: Unclosed variable "UNCLOSED.1"
+
+make: Unclosed variable "UNCLOSED.2"
+
+make: Unclosed variable "UNCLOSED.3"
+
+make: Unclosed variable "UNCLOSED_ORIG"
+
+varerror-unclosed:end
+target1-flags: we have: one two
+target2-flags: we have: one two three four
exit status 0
diff --git a/contrib/bmake/unit-tests/varmisc.mk b/contrib/bmake/unit-tests/varmisc.mk
index ab591db5c4fd..4c58b2a5dc77 100644
--- a/contrib/bmake/unit-tests/varmisc.mk
+++ b/contrib/bmake/unit-tests/varmisc.mk
@@ -1,9 +1,13 @@
-# $Id: varmisc.mk,v 1.11 2020/07/02 15:43:43 sjg Exp $
+# $Id: varmisc.mk,v 1.19 2020/08/31 16:28:10 sjg Exp $
#
# Miscellaneous variable tests.
all: unmatched_var_paren D_true U_true D_false U_false Q_lhs Q_rhs NQ_none \
strftime cmpv manok
+all: save-dollars
+all: export-appended
+all: parse-dynamic
+all: varerror-unclosed
unmatched_var_paren:
@echo ${foo::=foo-text}
@@ -74,7 +78,7 @@ manok:
@echo MAN=${MAN}
# This is an expanded variant of the above .for loop.
-# Between 2020-08-28 and 2020-07-02 this paragraph generated a wrong
+# Between 2020-06-28 and 2020-07-02 this paragraph generated a wrong
# error message "Variable VARNAME is recursive".
# When evaluating the !empty expression, the ${:U1} was not expanded and
# thus resulted in the seeming definition VARNAME=${VARNAME}, which is
@@ -82,3 +86,142 @@ manok:
VARNAME= ${VARNAME${:U1}}
.if defined(VARNAME${:U2}) && !empty(VARNAME${:U2})
.endif
+
+# begin .MAKE.SAVE_DOLLARS; see Var_Set_with_flags and s2Boolean.
+SD_VALUES= 0 1 2 False True false true Yes No yes no On Off ON OFF on off
+SD_4_DOLLARS= $$$$
+
+.for val in ${SD_VALUES}
+.MAKE.SAVE_DOLLARS:= ${val} # Must be := since a simple = has no effect.
+SD.${val}:= ${SD_4_DOLLARS}
+.endfor
+.MAKE.SAVE_DOLLARS:= yes
+
+save-dollars:
+.for val in ${SD_VALUES}
+ @printf '%s: %-8s = %s\n' $@ ${val} ${SD.${val}:Q}
+.endfor
+
+# Appending to an undefined variable does not add a space in front.
+.undef APPENDED
+APPENDED+= value
+.if ${APPENDED} != "value"
+.error "${APPENDED}"
+.endif
+
+# Appending to an empty variable adds a space between the old value
+# and the additional value.
+APPENDED= # empty
+APPENDED+= value
+.if ${APPENDED} != " value"
+.error "${APPENDED}"
+.endif
+
+# Appending to parameterized variables works as well.
+PARAM= param
+VAR.${PARAM}= 1
+VAR.${PARAM}+= 2
+.if ${VAR.param} != "1 2"
+.error "${VAR.param}"
+.endif
+
+# The variable name can contain arbitrary characters.
+# If the expanded variable name ends in a +, this still does not influence
+# the parser. The assignment operator is still a simple assignment.
+# Therefore, there is no need to add a space between the variable name
+# and the assignment operator.
+PARAM= +
+VAR.${PARAM}= 1
+VAR.${PARAM}+= 2
+.if ${VAR.+} != "1 2"
+.error "${VAR.+}"
+.endif
+.for param in + ! ?
+VAR.${param}= ${param}
+.endfor
+.if ${VAR.+} != "+" || ${VAR.!} != "!" || ${VAR.?} != "?"
+.error "${VAR.+}" "${VAR.!}" "${VAR.?}"
+.endif
+
+# Appending to a variable from the environment creates a copy of that variable
+# in the global context.
+# The appended value is not exported automatically.
+# When a variable is exported, the exported value is taken at the time of the
+# .export directive. Later changes to the variable have no effect.
+.export FROM_ENV_BEFORE
+FROM_ENV+= mk
+FROM_ENV_BEFORE+= mk
+FROM_ENV_AFTER+= mk
+.export FROM_ENV_AFTER
+
+export-appended:
+ @echo $@: "$$FROM_ENV"
+ @echo $@: "$$FROM_ENV_BEFORE"
+ @echo $@: "$$FROM_ENV_AFTER"
+
+# begin parse-dynamic
+#
+# Demonstrate that the target-specific variables are not evaluated in
+# the global context. They are preserved until there is a local context
+# in which resolving them makes sense.
+
+# There are different code paths for short names ...
+${:U>}= before
+GS_TARGET:= $@
+GS_MEMBER:= $%
+GS_PREFIX:= $*
+GS_ARCHIVE:= $!
+GS_ALLSRC:= $>
+${:U>}= after
+# ... and for braced short names ...
+GB_TARGET:= ${@}
+GB_MEMBER:= ${%}
+GB_PREFIX:= ${*}
+GB_ARCHIVE:= ${!}
+GB_ALLSRC:= ${>}
+# ... and for long names.
+GL_TARGET:= ${.TARGET}
+GL_MEMBER:= ${.MEMBER}
+GL_PREFIX:= ${.PREFIX}
+GL_ARCHIVE:= ${.ARCHIVE}
+GL_ALLSRC:= ${.ALLSRC}
+
+parse-dynamic:
+ @echo $@: ${GS_TARGET} ${GS_MEMBER} ${GS_PREFIX} ${GS_ARCHIVE} ${GS_ALLSRC}
+ @echo $@: ${GB_TARGET} ${GB_MEMBER} ${GB_PREFIX} ${GB_ARCHIVE} ${GB_ALLSRC}
+ @echo $@: ${GL_TARGET} ${GL_MEMBER} ${GL_PREFIX} ${GL_ARCHIVE} ${GL_ALLSRC}
+
+# Since 2020-07-28, make complains about unclosed variables.
+# Before that, it had complained about unclosed variables only when
+# parsing the modifiers, but not when parsing the variable name.
+
+UNCLOSED_INDIR_1= ${UNCLOSED_ORIG
+UNCLOSED_INDIR_2= ${UNCLOSED_INDIR_1}
+
+FLAGS= one two
+FLAGS+= ${FLAGS.${.ALLSRC:M*.c:T:u}}
+FLAGS.target2.c = three four
+
+target1.c:
+target2.c:
+
+all: target1-flags target2-flags
+target1-flags: target1.c
+ @echo $@: we have: ${FLAGS}
+
+target2-flags: target2.c
+ @echo $@: we have: ${FLAGS}
+
+varerror-unclosed:
+ @echo $@:begin
+ @echo $(
+ @echo $(UNCLOSED
+ @echo ${UNCLOSED
+ @echo ${UNCLOSED:M${PATTERN
+ @echo ${UNCLOSED.${param
+ @echo $
+.for i in 1 2 3
+ @echo ${UNCLOSED.${i}
+.endfor
+ @echo ${UNCLOSED_INDIR_2}
+ @echo $@:end
diff --git a/contrib/bmake/unit-tests/varmod-assign.exp b/contrib/bmake/unit-tests/varmod-assign.exp
new file mode 100644
index 000000000000..120c775d5f69
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-assign.exp
@@ -0,0 +1,26 @@
+mod-assign: first=1.
+mod-assign: last=3.
+mod-assign: appended=1 2 3.
+1
+2
+3
+mod-assign: ran:3.
+mod-assign: global: 1, 3, 1 2 3, 3.
+mod-assign-nested: then1t1
+mod-assign-nested: else2e2
+mod-assign-nested: then3t3
+mod-assign-nested: else4e4
+make: Bad modifier `:' for
+value}
+make: Bad modifier `:' for
+mod-assign-empty: overwritten}
+mod-assign-empty: VAR=overwritten
+make: Unknown modifier ':'
+
+sysv:y
+make: Unfinished modifier for ASSIGN ('}' missing)
+
+ok=word
+make: " echo word; false " returned non-zero status
+err=previous
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-assign.mk b/contrib/bmake/unit-tests/varmod-assign.mk
new file mode 100644
index 000000000000..82b7d947c484
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-assign.mk
@@ -0,0 +1,81 @@
+# $NetBSD: varmod-assign.mk,v 1.6 2020/08/25 21:16:53 rillig Exp $
+#
+# Tests for the obscure ::= variable modifiers, which perform variable
+# assignments during evaluation, just like the = operator in C.
+
+all: mod-assign
+all: mod-assign-nested
+all: mod-assign-empty
+all: mod-assign-parse
+all: mod-assign-shell-error
+
+mod-assign:
+ # The ::?= modifier applies the ?= assignment operator 3 times.
+ # The ?= operator only has an effect for the first time, therefore
+ # the variable FIRST ends up with the value 1.
+ @echo $@: ${1 2 3:L:@i@${FIRST::?=$i}@} first=${FIRST}.
+
+ # The ::= modifier applies the = assignment operator 3 times.
+ # The = operator overwrites the previous value, therefore the
+ # variable LAST ends up with the value 3.
+ @echo $@: ${1 2 3:L:@i@${LAST::=$i}@} last=${LAST}.
+
+ # The ::+= modifier applies the += assignment operator 3 times.
+ # The += operator appends 3 times to the variable, therefore
+ # the variable APPENDED ends up with the value "1 2 3".
+ @echo $@: ${1 2 3:L:@i@${APPENDED::+=$i}@} appended=${APPENDED}.
+
+ # The ::!= modifier applies the != assignment operator 3 times.
+ # The side effects of the shell commands are visible in the output.
+ # Just as with the ::= modifier, the last value is stored in the
+ # RAN variable.
+ @echo $@: ${echo.1 echo.2 echo.3:L:@i@${RAN::!=${i:C,.*,&; & 1>\&2,:S,., ,g}}@} ran:${RAN}.
+
+ # The assignments happen in the global scope and thus are
+ # preserved even after the shell command has been run.
+ @echo $@: global: ${FIRST:Q}, ${LAST:Q}, ${APPENDED:Q}, ${RAN:Q}.
+
+mod-assign-nested:
+ # The condition "1" is true, therefore THEN1 gets assigned a value,
+ # and IT1 as well. Nothing surprising here.
+ @echo $@: ${1:?${THEN1::=then1${IT1::=t1}}:${ELSE1::=else1${IE1::=e1}}}${THEN1}${ELSE1}${IT1}${IE1}
+
+ # The condition "0" is false, therefore ELSE1 gets assigned a value,
+ # and IE1 as well. Nothing surprising here as well.
+ @echo $@: ${0:?${THEN2::=then2${IT2::=t2}}:${ELSE2::=else2${IE2::=e2}}}${THEN2}${ELSE2}${IT2}${IE2}
+
+ # The same effects happen when the variables are defined elsewhere.
+ @echo $@: ${SINK3:Q}
+ @echo $@: ${SINK4:Q}
+SINK3:= ${1:?${THEN3::=then3${IT3::=t3}}:${ELSE3::=else3${IE3::=e3}}}${THEN3}${ELSE3}${IT3}${IE3}
+SINK4:= ${0:?${THEN4::=then4${IT4::=t4}}:${ELSE4::=else4${IE4::=e4}}}${THEN4}${ELSE4}${IT4}${IE4}
+
+mod-assign-empty:
+ # Assigning to the empty variable would obviously not work since that variable
+ # is write-protected. Therefore it is rejected early as a "bad modifier".
+ @echo ${::=value}
+ @echo $@: ${:Uvalue::=overwritten}
+
+ # The :L modifier sets the variable's value to its name.
+ # Since the name is still "VAR", assigning to that variable works.
+ @echo $@: ${VAR:L::=overwritten} VAR=${VAR}
+
+mod-assign-parse:
+ # The modifier for assignment operators starts with a ':'.
+ # An 'x' after that is an invalid modifier.
+ @echo ${ASSIGN::x} # 'x' is an unknown assignment operator
+
+ # When parsing an assignment operator fails because the operator is
+ # incomplete, make falls back to the SysV modifier.
+ @echo ${SYSV::=sysv\:x}${SYSV::x=:y}
+
+ @echo ${ASSIGN::=value # missing closing brace
+
+mod-assign-shell-error:
+ # If the command succeeds, the variable is assigned.
+ @${SH_OK::!= echo word; true } echo ok=${SH_OK}
+
+ # If the command fails, the variable keeps its previous value.
+ # FIXME: the error message says: "previous" returned non-zero status
+ @${SH_ERR::=previous}
+ @${SH_ERR::!= echo word; false } echo err=${SH_ERR}
diff --git a/contrib/bmake/unit-tests/varmod-defined.exp b/contrib/bmake/unit-tests/varmod-defined.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-defined.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-defined.mk b/contrib/bmake/unit-tests/varmod-defined.mk
new file mode 100644
index 000000000000..fa5bf5c2245c
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-defined.mk
@@ -0,0 +1,28 @@
+# $NetBSD: varmod-defined.mk,v 1.3 2020/08/25 21:58:08 rillig Exp $
+#
+# Tests for the :D variable modifier, which returns the given string
+# if the variable is defined. It is closely related to the :U modifier.
+
+DEF= defined
+.undef UNDEF
+
+# Since DEF is defined, the value of the expression is "value", not
+# "defined".
+#
+.if ${DEF:Dvalue} != "value"
+.error
+.endif
+
+# Since UNDEF is not defined, the "value" is ignored. Instead of leaving the
+# expression undefined, it is set to "", exactly to allow the expression to
+# be used in .if conditions. In this place, other undefined expressions
+# would generate an error message.
+# XXX: Ideally the error message would be "undefined variable", but as of
+# 2020-08-25 it is "Malformed conditional".
+#
+.if ${UNDEF:Dvalue} != ""
+.error
+.endif
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varmod-edge.exp b/contrib/bmake/unit-tests/varmod-edge.exp
index b3b2e3a92f95..94ba81e2e4f0 100644
--- a/contrib/bmake/unit-tests/varmod-edge.exp
+++ b/contrib/bmake/unit-tests/varmod-edge.exp
@@ -1,17 +1,22 @@
+make: "varmod-edge.mk" line omitted: ok M-paren
+make: "varmod-edge.mk" line omitted: ok M-mixed
+make: "varmod-edge.mk" line omitted: ok M-unescape
make: Unclosed variable specification (expecting '}') for "" (value "*)") modifier U
-make: Unclosed substitution for INP.eq-esc (= missing)
-ok M-paren
-ok M-mixed
-ok M-unescape
-ok M-nest-mix
-ok M-nest-brk
-ok M-pat-err
-ok M-bsbs
-ok M-bs1-par
-ok M-bs2-par
-ok M-128
-ok eq-ext
-ok eq-q
-ok eq-bs
-ok eq-esc
+make: "varmod-edge.mk" line omitted: ok M-nest-mix
+make: "varmod-edge.mk" line omitted: ok M-nest-brk
+make: "varmod-edge.mk" line omitted: ok M-pat-err
+make: "varmod-edge.mk" line omitted: ok M-bsbs
+make: "varmod-edge.mk" line omitted: ok M-bs1-par
+make: "varmod-edge.mk" line omitted: ok M-bs2-par
+make: "varmod-edge.mk" line omitted: ok M-128
+make: "varmod-edge.mk" line omitted: ok eq-ext
+make: "varmod-edge.mk" line omitted: ok eq-q
+make: "varmod-edge.mk" line omitted: ok eq-bs
+make: Unfinished modifier for INP.eq-esc ('=' missing)
+make: "varmod-edge.mk" line omitted: ok eq-esc
+make: "varmod-edge.mk" line omitted: ok colon
+make: Unknown modifier ':'
+make: Unknown modifier ':'
+make: "varmod-edge.mk" line omitted: ok colons
+ok
exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-edge.mk b/contrib/bmake/unit-tests/varmod-edge.mk
index 561eb6116891..e6f7f91c95c7 100644
--- a/contrib/bmake/unit-tests/varmod-edge.mk
+++ b/contrib/bmake/unit-tests/varmod-edge.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-edge.mk,v 1.7 2020/04/27 14:07:22 christos Exp $
+# $NetBSD: varmod-edge.mk,v 1.12 2020/08/08 13:29:09 rillig Exp $
#
# Tests for edge cases in variable modifiers.
#
@@ -143,20 +143,31 @@ INP.eq-bs= file.c file.c=%.o
MOD.eq-bs= ${INP.eq-bs:%.c\=%.o=%.ext}
EXP.eq-bs= file.c file.ext
-# Having only an escaped = results in a parse error.
-# The call to "pattern.lhs = VarGetPattern" fails.
+# Having only an escaped '=' results in a parse error.
+# The call to "pattern.lhs = ParseModifierPart" fails.
TESTS+= eq-esc
INP.eq-esc= file.c file...
MOD.eq-esc= ${INP.eq-esc:a\=b}
EXP.eq-esc= # empty
-# make: Unclosed substitution for INP.eq-esc (= missing)
+# make: Unfinished modifier for INP.eq-esc ('=' missing)
+
+TESTS+= colon
+INP.colon= value
+MOD.colon= ${INP.colon:}
+EXP.colon= value
+
+TESTS+= colons
+INP.colons= value
+MOD.colons= ${INP.colons::::}
+EXP.colons= # empty
-all:
.for test in ${TESTS}
. if ${MOD.${test}} == ${EXP.${test}}
- @printf 'ok %s\n' ${test:Q}''
+.info ok ${test}
. else
- @printf 'error in %s: expected %s, got %s\n' \
- ${test:Q}'' ${EXP.${test}:Q}'' ${MOD.${test}:Q}''
+.warning error in ${test}: expected "${EXP.${test}}", got "${MOD.${test}}"
. endif
.endfor
+
+all:
+ @echo ok
diff --git a/contrib/bmake/unit-tests/varmod-exclam-shell.exp b/contrib/bmake/unit-tests/varmod-exclam-shell.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-exclam-shell.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-exclam-shell.mk b/contrib/bmake/unit-tests/varmod-exclam-shell.mk
new file mode 100644
index 000000000000..2e811ddb4f5c
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-exclam-shell.mk
@@ -0,0 +1,28 @@
+# $NetBSD: varmod-exclam-shell.mk,v 1.2 2020/08/16 12:48:55 rillig Exp $
+#
+# Tests for the :!cmd! variable modifier.
+
+.if ${:!echo hello | tr 'l' 'l'!} != "hello"
+.warning unexpected
+.endif
+
+# The output is truncated at the first null byte.
+# Cmd_Exec returns only a string pointer without length information.
+.if ${:!echo hello | tr 'l' '\0'!} != "he"
+.warning unexpected
+.endif
+
+.if ${:!echo!} != ""
+.warning A newline at the end of the output must be stripped.
+.endif
+
+.if ${:!echo;echo!} != " "
+.warning Only a single newline at the end of the output is stripped.
+.endif
+
+.if ${:!echo;echo;echo;echo!} != " "
+.warning Other newlines in the output are converted to spaces.
+.endif
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varmod-extension.exp b/contrib/bmake/unit-tests/varmod-extension.exp
new file mode 100644
index 000000000000..24f7403c7f3f
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-extension.exp
@@ -0,0 +1,10 @@
+extension of 'a/b/c' is ''
+extension of 'def' is ''
+extension of 'a.b.c' is 'c'
+extension of 'a.b/c' is 'b/c'
+extension of 'a' is ''
+extension of 'a.a' is 'a'
+extension of '.gitignore' is 'gitignore'
+extension of 'a' is ''
+extension of 'a.a' is 'a'
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-extension.mk b/contrib/bmake/unit-tests/varmod-extension.mk
new file mode 100644
index 000000000000..db501f7234c7
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-extension.mk
@@ -0,0 +1,9 @@
+# $NetBSD: varmod-extension.mk,v 1.3 2020/08/23 15:09:15 rillig Exp $
+#
+# Tests for the :E variable modifier, which returns the filename extension
+# of each word in the variable.
+
+all:
+.for path in a/b/c def a.b.c a.b/c a a.a .gitignore a a.a
+ @echo "extension of '"${path:Q}"' is '"${path:E:Q}"'"
+.endfor
diff --git a/contrib/bmake/unit-tests/varmod-gmtime.exp b/contrib/bmake/unit-tests/varmod-gmtime.exp
new file mode 100644
index 000000000000..373e8de1a271
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-gmtime.exp
@@ -0,0 +1,9 @@
+mod-gmtime:
+%Y
+2020
+%Y
+%Y
+mod-gmtime-indirect:
+make: Unknown modifier '1'
+
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-gmtime.mk b/contrib/bmake/unit-tests/varmod-gmtime.mk
new file mode 100644
index 000000000000..0a05ad58d982
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-gmtime.mk
@@ -0,0 +1,35 @@
+# $NetBSD: varmod-gmtime.mk,v 1.2 2020/08/16 12:48:55 rillig Exp $
+#
+# Tests for the :gmtime variable modifier, which formats a timestamp
+# using strftime(3).
+
+all: mod-gmtime
+all: mod-gmtime-indirect
+
+mod-gmtime:
+ @echo $@:
+ @echo ${%Y:L:gmtim=1593536400} # modifier name too short
+ @echo ${%Y:L:gmtime=1593536400} # 2020-07-01T00:00:00Z
+ @echo ${%Y:L:gmtimer=1593536400} # modifier name too long
+ @echo ${%Y:L:gm=gm:M*}
+
+mod-gmtime-indirect:
+ @echo $@:
+
+ # As of 2020-08-16, it is not possible to pass the seconds via a
+ # variable expression. This is because parsing of the :gmtime
+ # modifier stops at the '$' and returns to ApplyModifiers.
+ #
+ # There, a colon would be skipped but not a dollar.
+ # Parsing therefore continues at the '$' of the ${:U159...}, looking
+ # for an ordinary variable modifier.
+ #
+ # At this point, the ${:U} is expanded and interpreted as a variable
+ # modifier, which results in the error message "Unknown modifier '1'".
+ #
+ # If ApplyModifier_Gmtime were to pass its argument through
+ # ParseModifierPart, this would work.
+ @echo ${%Y:L:gmtime=${:U1593536400}}
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varmod-hash.exp b/contrib/bmake/unit-tests/varmod-hash.exp
new file mode 100644
index 000000000000..f16f30903539
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-hash.exp
@@ -0,0 +1,9 @@
+make: Unknown modifier 'h'
+
+26bb0f5f
+12345
+make: Unknown modifier 'h'
+
+make: Unknown modifier 'h'
+
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-hash.mk b/contrib/bmake/unit-tests/varmod-hash.mk
new file mode 100644
index 000000000000..ef59268cb82c
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-hash.mk
@@ -0,0 +1,10 @@
+# $NetBSD: varmod-hash.mk,v 1.3 2020/08/23 15:13:21 rillig Exp $
+#
+# Tests for the :hash variable modifier.
+
+all:
+ @echo ${12345:L:has} # modifier name too short
+ @echo ${12345:L:hash} # ok
+ @echo ${12345:L:hash=SHA-256} # :hash does not accept '='
+ @echo ${12345:L:hasX} # misspelled
+ @echo ${12345:L:hashed} # modifier name too long
diff --git a/contrib/bmake/unit-tests/varmod-head.exp b/contrib/bmake/unit-tests/varmod-head.exp
new file mode 100644
index 000000000000..f0bf87f03012
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-head.exp
@@ -0,0 +1,10 @@
+head (dirname) of 'a/b/c' is 'a/b'
+head (dirname) of 'def' is '.'
+head (dirname) of 'a.b.c' is '.'
+head (dirname) of 'a.b/c' is 'a.b'
+head (dirname) of 'a' is '.'
+head (dirname) of 'a.a' is '.'
+head (dirname) of '.gitignore' is '.'
+head (dirname) of 'a' is '.'
+head (dirname) of 'a.a' is '.'
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-head.mk b/contrib/bmake/unit-tests/varmod-head.mk
new file mode 100644
index 000000000000..eda4820c5f14
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-head.mk
@@ -0,0 +1,9 @@
+# $NetBSD: varmod-head.mk,v 1.3 2020/08/23 15:09:15 rillig Exp $
+#
+# Tests for the :H variable modifier, which returns the dirname of
+# each of the words in the variable value.
+
+all:
+.for path in a/b/c def a.b.c a.b/c a a.a .gitignore a a.a
+ @echo "head (dirname) of '"${path:Q}"' is '"${path:H:Q}"'"
+.endfor
diff --git a/contrib/bmake/unit-tests/varmod-ifelse.exp b/contrib/bmake/unit-tests/varmod-ifelse.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-ifelse.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-ifelse.mk b/contrib/bmake/unit-tests/varmod-ifelse.mk
new file mode 100644
index 000000000000..e8e92b35d1c1
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-ifelse.mk
@@ -0,0 +1,9 @@
+# $NetBSD: varmod-ifelse.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the ${cond:?then:else} variable modifier, which evaluates either
+# the then-expression or the else-expression, depending on the condition.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varmod-l-name-to-value.exp b/contrib/bmake/unit-tests/varmod-l-name-to-value.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-l-name-to-value.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-l-name-to-value.mk b/contrib/bmake/unit-tests/varmod-l-name-to-value.mk
new file mode 100644
index 000000000000..b8a5877e1100
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-l-name-to-value.mk
@@ -0,0 +1,31 @@
+# $NetBSD: varmod-l-name-to-value.mk,v 1.3 2020/08/25 22:25:05 rillig Exp $
+#
+# Tests for the :L modifier, which returns the variable name as the new value.
+
+# The empty variable name leads to an empty string.
+.if ${:L} != ""
+.error
+.endif
+
+# The variable name is converted into an expression with the variable name
+# "VARNAME" and the value "VARNAME".
+.if ${VARNAME:L} != "VARNAME"
+.error
+.endif
+
+# The value of the expression can be modified afterwards.
+.if ${VARNAME:L:S,VAR,,} != "NAME"
+.error
+.endif
+
+# The name of the expression is still the same as before. Using the :L
+# modifier, it can be restored.
+#
+# Hmmm, this can be used as a double storage or a backup mechanism.
+# Probably unintended, but maybe useful.
+.if ${VARNAME:L:S,VAR,,:L} != "VARNAME"
+.error
+.endif
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varmod-localtime.exp b/contrib/bmake/unit-tests/varmod-localtime.exp
new file mode 100644
index 000000000000..69e4335be187
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-localtime.exp
@@ -0,0 +1,4 @@
+%Y
+2020
+%Y
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-localtime.mk b/contrib/bmake/unit-tests/varmod-localtime.mk
new file mode 100644
index 000000000000..fa4fd4f9cfb1
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-localtime.mk
@@ -0,0 +1,9 @@
+# $NetBSD: varmod-localtime.mk,v 1.3 2020/08/23 15:13:21 rillig Exp $
+#
+# Tests for the :localtime variable modifier, which returns the given time,
+# formatted as a local timestamp.
+
+all:
+ @echo ${%Y:L:localtim=1593536400} # modifier name too short
+ @echo ${%Y:L:localtime=1593536400} # 2020-07-01T00:00:00Z
+ @echo ${%Y:L:localtimer=1593536400} # modifier name too long
diff --git a/contrib/bmake/unit-tests/varmod-loop.exp b/contrib/bmake/unit-tests/varmod-loop.exp
new file mode 100644
index 000000000000..ce93eb5bc0c8
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-loop.exp
@@ -0,0 +1,16 @@
+:+one+ +two+ +three+:
+:x1y x2y x3y:
+:x1y x2y x3y:
+:mod-loop-varname: :x1y x2y x3y: ::
+:x1y x2y x3y:
+mod-loop-resolve:w1d2d3w w2i3w w1i2d3 2i${RES3}w w1d2d3 2i${RES3} 1i${RES2}w:
+mod-loop-varname-dollar:(1) (2) (3).
+mod-loop-varname-dollar:() () ().
+mod-loop-varname-dollar:() () ().
+mod-loop-dollar:1:
+mod-loop-dollar:${word}$:
+mod-loop-dollar:$3$:
+mod-loop-dollar:$${word}$$:
+mod-loop-dollar:$$5$$:
+mod-loop-dollar:$$${word}$$$:
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-loop.mk b/contrib/bmake/unit-tests/varmod-loop.mk
new file mode 100644
index 000000000000..561f4b95baa0
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-loop.mk
@@ -0,0 +1,63 @@
+# $NetBSD: varmod-loop.mk,v 1.2 2020/08/16 12:30:45 rillig Exp $
+#
+# Tests for the :@var@...${var}...@ variable modifier.
+
+all: mod-loop-varname
+all: mod-loop-resolve
+all: mod-loop-varname-dollar
+all: mod-loop-dollar
+
+# In the :@ modifier, the name of the loop variable can even be generated
+# dynamically. There's no practical use-case for this, and hopefully nobody
+# will ever depend on this, but technically it's possible.
+# Therefore, in -dL mode, this is forbidden, see lint.mk.
+mod-loop-varname:
+ @echo :${:Uone two three:@${:Ubar:S,b,v,}@+${var}+@:Q}:
+ # ":::" is a very creative variable name, unlikely in practice
+ # The expression ${\:\:\:} would not work since backslashes can only
+ # be escaped in the modifiers, but not in the variable name.
+ @echo :${:U1 2 3:@:::@x${${:U\:\:\:}}y@}:
+ # "@@" is another creative variable name.
+ @echo :${:U1 2 3:@\@\@@x${@@}y@}:
+ # Even "@" works as a variable name since the variable is installed
+ # in the "current" scope, which in this case is the one from the
+ # target.
+ @echo :$@: :${:U1 2 3:@\@@x${@}y@}: :$@:
+ # In extreme cases, even the backslash can be used as variable name.
+ # It needs to be doubled though.
+ @echo :${:U1 2 3:@\\@x${${:Ux:S,x,\\,}}y@}:
+
+# The :@ modifier resolves the variables a little more often than expected.
+# In particular, it resolves _all_ variables from the context, and not only
+# the loop variable (in this case v).
+#
+# The d means direct reference, the i means indirect reference.
+RESOLVE= ${RES1} $${RES1}
+RES1= 1d${RES2} 1i$${RES2}
+RES2= 2d${RES3} 2i$${RES3}
+RES3= 3
+
+mod-loop-resolve:
+ @echo $@:${RESOLVE:@v@w${v}w@:Q}:
+
+# Until 2020-07-20, the variable name of the :@ modifier could end with one
+# or two dollar signs, which were silently ignored.
+# There's no point in allowing a dollar sign in that position.
+mod-loop-varname-dollar:
+ @echo $@:${1 2 3:L:@v$@($v)@:Q}.
+ @echo $@:${1 2 3:L:@v$$@($v)@:Q}.
+ @echo $@:${1 2 3:L:@v$$$@($v)@:Q}.
+
+# Demonstrate that it is possible to generate dollar characters using the
+# :@ modifier.
+#
+# These are edge cases that could have resulted in a parse error as well
+# since the $@ at the end could have been interpreted as a variable, which
+# would mean a missing closing @ delimiter.
+mod-loop-dollar:
+ @echo $@:${:U1:@word@${word}$@:Q}:
+ @echo $@:${:U2:@word@$${word}$$@:Q}:
+ @echo $@:${:U3:@word@$$${word}$$$@:Q}:
+ @echo $@:${:U4:@word@$$$${word}$$$$@:Q}:
+ @echo $@:${:U5:@word@$$$$${word}$$$$$@:Q}:
+ @echo $@:${:U6:@word@$$$$$${word}$$$$$$@:Q}:
diff --git a/contrib/bmake/unit-tests/varmod-match-escape.exp b/contrib/bmake/unit-tests/varmod-match-escape.exp
new file mode 100755
index 000000000000..1e4c030c5b42
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-match-escape.exp
@@ -0,0 +1,3 @@
+Pattern[SPECIALS] for [\: : \\ * \*] is [\:]
+Pattern[SPECIALS] for [\: : \\ * \*] is [:]
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-match-escape.mk b/contrib/bmake/unit-tests/varmod-match-escape.mk
new file mode 100755
index 000000000000..7913bb476ae7
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-match-escape.mk
@@ -0,0 +1,20 @@
+# $NetBSD: varmod-match-escape.mk,v 1.1 2020/08/16 20:03:53 rillig Exp $
+#
+# As of 2020-08-01, the :M and :N modifiers interpret backslashes differently,
+# depending on whether there was a variable expression somewhere before the
+# first backslash or not. See ApplyModifier_Match, "copy = TRUE".
+#
+# Apart from the different and possibly confusing debug output, there is no
+# difference in behavior. When parsing the modifier text, only \{, \} and \:
+# are unescaped, and in the pattern matching these have the same meaning as
+# their plain variants '{', '}' and ':'. In the pattern matching from
+# Str_Match, only \*, \? or \[ would make a noticeable difference.
+SPECIALS= \: : \\ * \*
+RELEVANT= yes
+.if ${SPECIALS:M${:U}\:} != ${SPECIALS:M\:${:U}}
+.warning unexpected
+.endif
+RELEVANT= no
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varmod-match.exp b/contrib/bmake/unit-tests/varmod-match.exp
new file mode 100644
index 000000000000..11777e086d4f
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-match.exp
@@ -0,0 +1,5 @@
+match-char-class:
+ uppercase numbers: One Two Three Four
+ all the others: five six seven
+ starts with non-s, ends with [ex]: One Three five
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-match.mk b/contrib/bmake/unit-tests/varmod-match.mk
new file mode 100644
index 000000000000..805426a0fda9
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-match.mk
@@ -0,0 +1,22 @@
+# $NetBSD: varmod-match.mk,v 1.3 2020/08/16 20:03:53 rillig Exp $
+#
+# Tests for the :M variable modifier, which filters words that match the
+# given pattern.
+
+all: match-char-class
+all: slow
+
+
+NUMBERS= One Two Three Four five six seven
+
+match-char-class:
+ @echo '$@:'
+ @echo ' uppercase numbers: ${NUMBERS:M[A-Z]*}'
+ @echo ' all the others: ${NUMBERS:M[^A-Z]*}'
+ @echo ' starts with non-s, ends with [ex]: ${NUMBERS:M[^s]*[ex]}'
+
+
+# Before 2020-06-13, this expression took quite a long time in Str_Match,
+# calling itself 601080390 times for 16 asterisks.
+slow:
+ @: ${:U****************:M****************b}
diff --git a/contrib/bmake/unit-tests/varmod-no-match.exp b/contrib/bmake/unit-tests/varmod-no-match.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-no-match.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-no-match.mk b/contrib/bmake/unit-tests/varmod-no-match.mk
new file mode 100644
index 000000000000..2acb27e2e727
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-no-match.mk
@@ -0,0 +1,9 @@
+# $NetBSD: varmod-no-match.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the :N variable modifier, which filters words that do not match
+# the given pattern.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varmod-order-reverse.exp b/contrib/bmake/unit-tests/varmod-order-reverse.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-order-reverse.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-order-reverse.mk b/contrib/bmake/unit-tests/varmod-order-reverse.mk
new file mode 100644
index 000000000000..fa9d179560c8
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-order-reverse.mk
@@ -0,0 +1,13 @@
+# $NetBSD: varmod-order-reverse.mk,v 1.3 2020/08/16 20:13:10 rillig Exp $
+#
+# Tests for the :Or variable modifier, which returns the words, sorted in
+# descending order.
+
+NUMBERS= one two three four five six seven eight nine ten
+
+.if ${NUMBERS:Or} != "two three ten six seven one nine four five eight"
+.error ${NUMBERS:Or}
+.endif
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varmod-order-shuffle.exp b/contrib/bmake/unit-tests/varmod-order-shuffle.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-order-shuffle.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-order-shuffle.mk b/contrib/bmake/unit-tests/varmod-order-shuffle.mk
new file mode 100644
index 000000000000..b6eb6be4b1bc
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-order-shuffle.mk
@@ -0,0 +1,39 @@
+# $NetBSD: varmod-order-shuffle.mk,v 1.3 2020/08/16 20:43:01 rillig Exp $
+#
+# Tests for the :Ox variable modifier, which returns the words of the
+# variable, shuffled.
+#
+# As of 2020-08-16, make uses random(3) seeded by the current time in seconds.
+# This makes the random numbers completely predictable since there is no other
+# part of make that uses random numbers.
+
+NUMBERS= one two three four five six seven eight nine ten
+
+# 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 each of the
+# 3 :Ox tests will incorrectly fail with probability 2.756E-7, which
+# lets the whole test fail once in 1.209.600 runs, on average.
+
+# Create two shuffles using the := assignment operator.
+shuffled1:= ${NUMBERS:Ox}
+shuffled2:= ${NUMBERS:Ox}
+.if ${shuffled1} == ${shuffled2}
+.error ${shuffled1} == ${shuffled2}
+.endif
+
+# Sorting the list before shuffling it has no effect.
+shuffled1:= ${NUMBERS:O:Ox}
+shuffled2:= ${NUMBERS:O:Ox}
+.if ${shuffled1} == ${shuffled2}
+.error ${shuffled1} == ${shuffled2}
+.endif
+
+# Sorting after shuffling must produce the original numbers.
+sorted:= ${NUMBERS:Ox:O}
+.if ${sorted} != ${NUMBERS:O}
+.error ${sorted} != ${NUMBERS:O}
+.endif
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varmod-order.exp b/contrib/bmake/unit-tests/varmod-order.exp
new file mode 100644
index 000000000000..99d1d6ef164c
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-order.exp
@@ -0,0 +1,7 @@
+make: Bad modifier `:OX' for NUMBERS
+make: "varmod-order.mk" line 13: Undefined variable "${NUMBERS:OX"
+make: Bad modifier `:OxXX' for NUMBERS
+make: "varmod-order.mk" line 16: Undefined variable "${NUMBERS:Ox"
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/varmod-order.mk b/contrib/bmake/unit-tests/varmod-order.mk
new file mode 100644
index 000000000000..b079bdc34217
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-order.mk
@@ -0,0 +1,19 @@
+# $NetBSD: varmod-order.mk,v 1.4 2020/08/16 20:43:01 rillig Exp $
+#
+# Tests for the :O variable modifier, which returns the words, sorted in
+# ascending order.
+
+NUMBERS= one two three four five six seven eight nine ten
+
+.if ${NUMBERS:O} != "eight five four nine one seven six ten three two"
+.error ${NUMBERS:O}
+.endif
+
+# Unknown modifier "OX"
+_:= ${NUMBERS:OX}
+
+# Unknown modifier "OxXX"
+_:= ${NUMBERS:OxXX}
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varmod-path.exp b/contrib/bmake/unit-tests/varmod-path.exp
new file mode 100644
index 000000000000..ae7f6cc85748
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-path.exp
@@ -0,0 +1,4 @@
+varmod-path.subdir/varmod-path.phony
+varmod-path.subdir/varmod-path.real
+varmod-path.enoent
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-path.mk b/contrib/bmake/unit-tests/varmod-path.mk
new file mode 100644
index 000000000000..ebbf755ddbec
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-path.mk
@@ -0,0 +1,35 @@
+# $NetBSD: varmod-path.mk,v 1.3 2020/08/23 08:10:49 rillig Exp $
+#
+# Tests for the :P variable modifier, which looks up the path for a given
+# target.
+#
+# The phony target does not have a corresponding path, therefore ... oops,
+# as of 2020-08-23 it is nevertheless resolved to a path. This is probably
+# unintended.
+#
+# The real target is located in a subdirectory, and its full path is returned.
+# If it had been in the current directory, the difference between its path and
+# its name would not be visible.
+#
+# The enoent target does not exist, therefore the target name is returned.
+
+.MAIN: all
+
+_!= rm -rf varmod-path.subdir
+_!= mkdir varmod-path.subdir
+_!= > varmod-path.subdir/varmod-path.phony
+_!= > varmod-path.subdir/varmod-path.real
+
+# To have an effect, this .PATH declaration must be after the directory is created.
+.PATH: varmod-path.subdir
+
+varmod-path.phony: .PHONY
+varmod-path.real:
+
+all: varmod-path.phony varmod-path.real
+ @echo ${varmod-path.phony:P}
+ @echo ${varmod-path.real:P}
+ @echo ${varmod-path.enoent:P}
+
+.END:
+ @rm -rf varmod-path.subdir
diff --git a/contrib/bmake/unit-tests/varmod-quote-dollar.exp b/contrib/bmake/unit-tests/varmod-quote-dollar.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-quote-dollar.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-quote-dollar.mk b/contrib/bmake/unit-tests/varmod-quote-dollar.mk
new file mode 100644
index 000000000000..fedbe8a10f4b
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-quote-dollar.mk
@@ -0,0 +1,10 @@
+# $NetBSD: varmod-quote-dollar.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the :q variable modifier, which quotes the string for the shell
+# and doubles dollar signs, to prevent them from being interpreted by a
+# child process of make.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varmod-quote.exp b/contrib/bmake/unit-tests/varmod-quote.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-quote.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-quote.mk b/contrib/bmake/unit-tests/varmod-quote.mk
new file mode 100644
index 000000000000..adf736048e76
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-quote.mk
@@ -0,0 +1,9 @@
+# $NetBSD: varmod-quote.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the :Q variable modifier, which quotes the variable value
+# to be used in a shell program.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varmod-range.exp b/contrib/bmake/unit-tests/varmod-range.exp
new file mode 100644
index 000000000000..bdbd68edeb45
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-range.exp
@@ -0,0 +1,8 @@
+make: Unknown modifier 'r'
+
+1 2 3
+make: Unknown modifier 'r'
+
+make: Unknown modifier 'r'
+
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-range.mk b/contrib/bmake/unit-tests/varmod-range.mk
new file mode 100644
index 000000000000..9fd79cc97a81
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-range.mk
@@ -0,0 +1,10 @@
+# $NetBSD: varmod-range.mk,v 1.3 2020/08/23 15:13:21 rillig Exp $
+#
+# Tests for the :range variable modifier, which generates sequences
+# of integers from the given range.
+
+all:
+ @echo ${a b c:L:rang} # modifier name too short
+ @echo ${a b c:L:range} # ok
+ @echo ${a b c:L:rango} # misspelled
+ @echo ${a b c:L:ranger} # modifier name too long
diff --git a/contrib/bmake/unit-tests/varmod-remember.exp b/contrib/bmake/unit-tests/varmod-remember.exp
new file mode 100644
index 000000000000..448f817d8969
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-remember.exp
@@ -0,0 +1,3 @@
+1 2 3 1 2 3 1 2 3
+1 2 3, SAVED=3
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-remember.mk b/contrib/bmake/unit-tests/varmod-remember.mk
new file mode 100644
index 000000000000..68eb96a122c4
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-remember.mk
@@ -0,0 +1,12 @@
+# $NetBSD: varmod-remember.mk,v 1.3 2020/08/23 15:18:43 rillig Exp $
+#
+# Tests for the :_ modifier, which saves the current variable value
+# in the _ variable or another, to be used later again.
+
+# In the parameterized form, having the variable name on the right side of
+# the = assignment operator is confusing. In almost all other situations
+# the variable name is on the left-hand side of the = operator. Luckily
+# this modifier is only rarely needed.
+all:
+ @echo ${1 2 3:L:_:@var@${_}@}
+ @echo ${1 2 3:L:@var@${var:_=SAVED:}@}, SAVED=${SAVED}
diff --git a/contrib/bmake/unit-tests/varmod-root.exp b/contrib/bmake/unit-tests/varmod-root.exp
new file mode 100644
index 000000000000..24ecbb875f77
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-root.exp
@@ -0,0 +1,10 @@
+root of 'a/b/c' is 'a/b/c'
+root of 'def' is 'def'
+root of 'a.b.c' is 'a.b'
+root of 'a.b/c' is 'a'
+root of 'a' is 'a'
+root of 'a.a' is 'a'
+root of '.gitignore' is ''
+root of 'a' is 'a'
+root of 'a.a' is 'a'
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-root.mk b/contrib/bmake/unit-tests/varmod-root.mk
new file mode 100644
index 000000000000..88af42d82510
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-root.mk
@@ -0,0 +1,9 @@
+# $NetBSD: varmod-root.mk,v 1.3 2020/08/23 15:09:15 rillig Exp $
+#
+# Tests for the :R variable modifier, which returns the filename root
+# without the extension.
+
+all:
+.for path in a/b/c def a.b.c a.b/c a a.a .gitignore a a.a
+ @echo "root of '"${path:Q}"' is '"${path:R:Q}"'"
+.endfor
diff --git a/contrib/bmake/unit-tests/varmod-select-words.exp b/contrib/bmake/unit-tests/varmod-select-words.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-select-words.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-select-words.mk b/contrib/bmake/unit-tests/varmod-select-words.mk
new file mode 100644
index 000000000000..a9df25f9ff32
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-select-words.mk
@@ -0,0 +1,9 @@
+# $NetBSD: varmod-select-words.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the :[...] variable modifier, which selects a single word
+# or a range of words from a variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varmod-shell.exp b/contrib/bmake/unit-tests/varmod-shell.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-shell.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-shell.mk b/contrib/bmake/unit-tests/varmod-shell.mk
new file mode 100644
index 000000000000..052968004f1b
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-shell.mk
@@ -0,0 +1,9 @@
+# $NetBSD: varmod-shell.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the :sh variable modifier, which runs the shell command
+# given by the variable value and returns its output.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varmod-subst-regex.exp b/contrib/bmake/unit-tests/varmod-subst-regex.exp
new file mode 100644
index 000000000000..eb9ae7f41fb9
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-subst-regex.exp
@@ -0,0 +1,23 @@
+make: Regex compilation error: (details omitted)
+mod-regex-compile-error: C,word,____,:Q}.
+make: No subexpression \1
+make: No subexpression \1
+make: No subexpression \1
+make: No subexpression \1
+mod-regex-limits:11-missing:1 6
+mod-regex-limits:11-ok:1 22 446
+make: No subexpression \2
+make: No subexpression \2
+make: No subexpression \2
+make: No subexpression \2
+mod-regex-limits:22-missing:1 6
+make: No subexpression \2
+make: No subexpression \2
+make: No subexpression \2
+make: No subexpression \2
+mod-regex-limits:22-missing:1 6
+mod-regex-limits:22-ok:1 33 556
+mod-regex-limits:capture:ihgfedcbaabcdefghijABCDEFGHIJa0a1a2rest
+make: Regex compilation error: (details omitted)
+mod-regex-errors:
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-subst-regex.mk b/contrib/bmake/unit-tests/varmod-subst-regex.mk
new file mode 100644
index 000000000000..fe2758e401ec
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-subst-regex.mk
@@ -0,0 +1,87 @@
+# $NetBSD: varmod-subst-regex.mk,v 1.3 2020/08/28 17:15:04 rillig Exp $
+#
+# Tests for the :C,from,to, variable modifier.
+
+all: mod-regex-compile-error
+all: mod-regex-limits
+all: mod-regex-errors
+
+# The variable expression expands to 4 words. Of these words, none matches
+# the regular expression "a b" since these words don't contain any
+# whitespace.
+.if ${:Ua b b c:C,a b,,} != "a b b c"
+.error
+.endif
+
+# Using the '1' modifier does not change anything. The '1' modifier just
+# means to apply at most 1 replacement in the whole variable expression.
+.if ${:Ua b b c:C,a b,,1} != "a b b c"
+.error
+.endif
+
+# The 'W' modifier treats the whole variable value as a single big word,
+# containing whitespace. This big word matches the regular expression,
+# therefore it gets replaced. Whitespace is preserved after replacing.
+.if ${:Ua b b c:C,a b,,W} != " b c"
+.error
+.endif
+
+# The 'g' modifier does not have any effect here since each of the words
+# contains the character 'b' a single time.
+.if ${:Ua b b c:C,b,,g} != "a c"
+.error
+.endif
+
+# The first :C modifier has the 'W' modifier, which makes the whole
+# expression a single word. The 'g' modifier then replaces all occurrences
+# of "1 2" with "___". The 'W' modifier only applies to this single :C
+# modifier. This is demonstrated by the :C modifier that follows. If the
+# 'W' modifier would be preserved, only a single underscore would have been
+# replaced with an 'x'.
+.if ${:U1 2 3 1 2 3:C,1 2,___,Wg:C,_,x,} != "x__ 3 x__ 3"
+.error
+.endif
+
+# The regular expression does not match in the first word.
+# It matches once in the second word, and the \0\0 doubles that word.
+# In the third word, the regular expression matches as early as possible,
+# and since the matches must not overlap, the next possible match would
+# start at the 6, but at that point, there is only one character left,
+# and that cannot match the regular expression "..". Therefore only the
+# "45" is doubled in the result.
+.if ${:U1 23 456:C,..,\0\0,} != "1 2323 45456"
+.error
+.endif
+
+# The modifier '1' applies the replacement at most once, across the whole
+# variable value, no matter whether it is a single big word or many small
+# words.
+#
+# Up to 2020-08-28, the manual page said that the modifiers '1' and 'g'
+# were orthogonal, which was wrong.
+.if ${:U12345 12345:C,.,\0\0,1} != "112345 12345"
+.error
+.endif
+
+# Multiple asterisks form an invalid regular expression. This produces an
+# error message and (as of 2020-08-28) stops parsing in the middle of the
+# variable expression. The unparsed part of the expression is then copied
+# verbatim to the output, which is unexpected and can lead to strange shell
+# commands being run.
+mod-regex-compile-error:
+ @echo $@: ${:Uword1 word2:C,****,____,g:C,word,____,:Q}.
+
+# These tests generate error messages but as of 2020-08-28 just continue
+# parsing and execution as if nothing bad had happened.
+mod-regex-limits:
+ @echo $@:11-missing:${:U1 23 456:C,..,\1\1,:Q}
+ @echo $@:11-ok:${:U1 23 456:C,(.).,\1\1,:Q}
+ @echo $@:22-missing:${:U1 23 456:C,..,\2\2,:Q}
+ @echo $@:22-missing:${:U1 23 456:C,(.).,\2\2,:Q}
+ @echo $@:22-ok:${:U1 23 456:C,(.)(.),\2\2,:Q}
+ # The :C modifier only handles single-digit capturing groups,
+ # which is more than enough for daily use.
+ @echo $@:capture:${:UabcdefghijABCDEFGHIJrest:C,(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.),\9\8\7\6\5\4\3\2\1\0\10\11\12,}
+
+mod-regex-errors:
+ @echo $@: ${UNDEF:Uvalue:C,[,,}
diff --git a/contrib/bmake/unit-tests/varmod-subst.exp b/contrib/bmake/unit-tests/varmod-subst.exp
new file mode 100644
index 000000000000..e752fb8058a8
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-subst.exp
@@ -0,0 +1,51 @@
+mod-subst:
+:a b b c:
+:a b b c:
+: b c:
+:a c:
+:x__ 3 x__ 3:
+12345
+mod-subst-delimiter:
+1 two 3 horizontal tabulator
+1 two 3 space
+1 two 3 exclamation mark
+1 two 3 double quotes
+1 two 3 hash
+1 two 3 dollar
+1 two 3 percent
+1 two 3 apostrophe
+1 two 3 opening parenthesis
+1 two 3 closing parenthesis
+1 two 3 digit
+1 two 3 colon
+1 two 3 less than sign
+1 two 3 equal sign
+1 two 3 greater than sign
+1 two 3 question mark
+1 two 3 at
+1 two 3 letter
+1 two 3 opening bracket
+1 two 3 backslash
+1 two 3 closing bracket
+1 two 3 caret
+1 two 3 opening brace
+1 two 3 vertical line
+1 two 3 closing brace
+1 two 3 tilde
+mod-subst-chain:
+A B c.
+make: Unknown modifier 'i'
+.
+mod-subst-dollar:$1:
+mod-subst-dollar:$2:
+mod-subst-dollar:$3:
+mod-subst-dollar:$4:
+mod-subst-dollar:$5:
+mod-subst-dollar:$6:
+mod-subst-dollar:$7:
+mod-subst-dollar:$8:
+mod-subst-dollar:$40:
+mod-subst-dollar:U8:
+mod-subst-dollar:$$$$:
+mod-subst-dollar:$$$good3
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-subst.mk b/contrib/bmake/unit-tests/varmod-subst.mk
new file mode 100644
index 000000000000..de8d54df8506
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-subst.mk
@@ -0,0 +1,153 @@
+# $NetBSD: varmod-subst.mk,v 1.3 2020/08/19 06:10:06 rillig Exp $
+#
+# Tests for the :S,from,to, variable modifier.
+
+all: mod-subst
+all: mod-subst-delimiter
+all: mod-subst-chain
+all: mod-subst-dollar
+
+WORDS= sequences of letters
+.if ${WORDS:S,,,} != ${WORDS}
+.warning The empty pattern matches something.
+.endif
+.if ${WORDS:S,e,*,1} != "s*quences of letters"
+.warning The :S modifier flag '1' is not applied exactly once.
+.endif
+.if ${WORDS:S,f,*,1} != "sequences o* letters"
+.warning The :S modifier flag '1' is only applied to the first word,\
+ not to the first occurrence.
+.endif
+.if ${WORDS:S,e,*,} != "s*quences of l*tters"
+.warning The :S modifier does not replace every first match per word.
+.endif
+.if ${WORDS:S,e,*,g} != "s*qu*nc*s of l*tt*rs"
+.warning The :S modifier flag 'g' does not replace every occurrence.
+.endif
+.if ${WORDS:S,^sequ,occurr,} != "occurrences of letters"
+.warning The :S modifier fails for a short match anchored at the start.
+.endif
+.if ${WORDS:S,^of,with,} != "sequences with letters"
+.warning The :S modifier fails for an exact match anchored at the start.
+.endif
+.if ${WORDS:S,^office,does not match,} != ${WORDS}
+.warning The :S modifier matches a too long pattern anchored at the start.
+.endif
+.if ${WORDS:S,f$,r,} != "sequences or letters"
+.warning The :S modifier fails for a short match anchored at the end.
+.endif
+.if ${WORDS:S,s$,,} != "sequence of letter"
+.warning The :S modifier fails to replace one occurrence per word.
+.endif
+.if ${WORDS:S,of$,,} != "sequences letters"
+.warning The :S modifier fails for an exact match anchored at the end.
+.endif
+.if ${WORDS:S,eof$,,} != ${WORDS}
+.warning The :S modifier matches a too long pattern anchored at the end.
+.endif
+.if ${WORDS:S,^of$,,} != "sequences letters"
+.warning The :S modifier does not match a word anchored at both ends.
+.endif
+.if ${WORDS:S,^o$,,} != ${WORDS}
+.warning The :S modifier matches a prefix anchored at both ends.
+.endif
+.if ${WORDS:S,^f$,,} != ${WORDS}
+.warning The :S modifier matches a suffix anchored at both ends.
+.endif
+.if ${WORDS:S,^eof$,,} != ${WORDS}
+.warning The :S modifier matches a too long prefix anchored at both ends.
+.endif
+.if ${WORDS:S,^office$,,} != ${WORDS}
+.warning The :S modifier matches a too long suffix anchored at both ends.
+.endif
+
+mod-subst:
+ @echo $@:
+ @echo :${:Ua b b c:S,a b,,:Q}:
+ @echo :${:Ua b b c:S,a b,,1:Q}:
+ @echo :${:Ua b b c:S,a b,,W:Q}:
+ @echo :${:Ua b b c:S,b,,g:Q}:
+ @echo :${:U1 2 3 1 2 3:S,1 2,___,Wg:S,_,x,:Q}:
+ @echo ${:U12345:S,,sep,g:Q}
+
+# The :S and :C modifiers accept an arbitrary character as the delimiter,
+# including characters that are otherwise used as escape characters or
+# interpreted in a special way. This can be used to confuse humans.
+mod-subst-delimiter:
+ @echo $@:
+ @echo ${:U1 2 3:S 2 two :Q} horizontal tabulator
+ @echo ${:U1 2 3:S 2 two :Q} space
+ @echo ${:U1 2 3:S!2!two!:Q} exclamation mark
+ @echo ${:U1 2 3:S"2"two":Q} double quotes
+ # In shell command lines, the hash does not need to be escaped.
+ # It needs to be escaped in variable assignment lines though.
+ @echo ${:U1 2 3:S#2#two#:Q} hash
+ @echo ${:U1 2 3:S$2$two$:Q} dollar
+ @echo ${:U1 2 3:S%2%two%:Q} percent
+ @echo ${:U1 2 3:S'2'two':Q} apostrophe
+ @echo ${:U1 2 3:S(2(two(:Q} opening parenthesis
+ @echo ${:U1 2 3:S)2)two):Q} closing parenthesis
+ @echo ${:U1 2 3:S121two1:Q} digit
+ @echo ${:U1 2 3:S:2:two::Q} colon
+ @echo ${:U1 2 3:S<2<two<:Q} less than sign
+ @echo ${:U1 2 3:S=2=two=:Q} equal sign
+ @echo ${:U1 2 3:S>2>two>:Q} greater than sign
+ @echo ${:U1 2 3:S?2?two?:Q} question mark
+ @echo ${:U1 2 3:S@2@two@:Q} at
+ @echo ${:U1 2 3:Sa2atwoa:Q} letter
+ @echo ${:U1 2 3:S[2[two[:Q} opening bracket
+ @echo ${:U1 2 3:S\2\two\:Q} backslash
+ @echo ${:U1 2 3:S]2]two]:Q} closing bracket
+ @echo ${:U1 2 3:S^2^two^:Q} caret
+ @echo ${:U1 2 3:S{2{two{:Q} opening brace
+ @echo ${:U1 2 3:S|2|two|:Q} vertical line
+ @echo ${:U1 2 3:S}2}two}:Q} closing brace
+ @echo ${:U1 2 3:S~2~two~:Q} tilde
+
+# The :S and :C modifiers can be chained without a separating ':'.
+# This is not documented in the manual page.
+# It works because ApplyModifier_Subst scans for the known modifiers g1W
+# and then just returns to ApplyModifiers. There, the colon is optionally
+# skipped (see the *st.next == ':' at the end of the loop).
+#
+# Most other modifiers cannot be chained since their parsers skip until
+# the next ':' or '}' or ')'.
+mod-subst-chain:
+ @echo $@:
+ @echo ${:Ua b c:S,a,A,S,b,B,}.
+ # There is no 'i' modifier for the :S or :C modifiers.
+ # The error message is "make: Unknown modifier 'i'", which is
+ # kind of correct, although it is mixing the terms for variable
+ # modifiers with the matching modifiers.
+ @echo ${:Uvalue:S,a,x,i}.
+
+# No matter how many dollar characters there are, they all get merged
+# into a single dollar by the :S modifier.
+#
+# As of 2020-08-09, this is because ParseModifierPart sees a '$' and
+# calls Var_Parse to expand the variable. In all other places, the "$$"
+# is handled outside of Var_Parse. Var_Parse therefore considers "$$"
+# one of the "really stupid names", skips the first dollar, and parsing
+# continues with the next character. This repeats for the other dollar
+# signs, except the one before the delimiter. That one is handled by
+# the code that optionally interprets the '$' as the end-anchor in the
+# first part of the :S modifier. That code doesn't call Var_Parse but
+# simply copies the dollar to the result.
+mod-subst-dollar:
+ @echo $@:${:U1:S,^,$,:Q}:
+ @echo $@:${:U2:S,^,$$,:Q}:
+ @echo $@:${:U3:S,^,$$$,:Q}:
+ @echo $@:${:U4:S,^,$$$$,:Q}:
+ @echo $@:${:U5:S,^,$$$$$,:Q}:
+ @echo $@:${:U6:S,^,$$$$$$,:Q}:
+ @echo $@:${:U7:S,^,$$$$$$$,:Q}:
+ @echo $@:${:U8:S,^,$$$$$$$$,:Q}:
+ @echo $@:${:U40:S,^,$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$,:Q}:
+# This generates no dollar at all:
+ @echo $@:${:UU8:S,^,${:U$$$$$$$$},:Q}:
+# Here is an alternative way to generate dollar characters.
+# It's unexpectedly complicated though.
+ @echo $@:${:U:range=5:ts\x24:C,[0-9],,g:Q}:
+# In modifiers, dollars are escaped using the backslash, not using another
+# dollar sign. Therefore, creating a dollar sign is pretty simple:
+ @echo $@:${:Ugood3:S,^,\$\$\$,:Q}
diff --git a/contrib/bmake/unit-tests/varmod-sysv.exp b/contrib/bmake/unit-tests/varmod-sysv.exp
new file mode 100644
index 000000000000..d9049c889823
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-sysv.exp
@@ -0,0 +1,8 @@
+ax:Q b c d eb
+bcd.e
+&
+anchor-dollar: value
+anchor-dollar: valux
+mismatch: file.cpp file.h
+mismatch: renamed.c other.c
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-sysv.mk b/contrib/bmake/unit-tests/varmod-sysv.mk
new file mode 100644
index 000000000000..c8410ed900d8
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-sysv.mk
@@ -0,0 +1,61 @@
+# $NetBSD: varmod-sysv.mk,v 1.3 2020/08/23 14:52:06 rillig Exp $
+#
+# Tests for the ${VAR:from=to} variable modifier, which replaces the suffix
+# "from" with "to". It can also use '%' as a wildcard.
+#
+# This modifier is applied when the other modifiers don't match exactly.
+
+all: words ampersand anchor-dollar mismatch
+
+# The :Q looks like a modifier but isn't.
+# It is part of the replacement string.
+words:
+ @echo a${a b c d e:L:%a=x:Q}b
+
+# Before 2020-07-19, an ampersand could be used in the replacement part
+# of a SysV substitution modifier. This was probably a copy-and-paste
+# mistake since the SysV modifier code looked a lot like the code for the
+# :S and :C modifiers. The ampersand is not mentioned in the manual page.
+ampersand:
+ @echo ${:U${a.bcd.e:L:a.%=%}:Q}
+ @echo ${:U${a.bcd.e:L:a.%=&}:Q}
+
+# Before 2020-07-20, when a SysV modifier was parsed, a single dollar
+# before the '=' was interpreted as an anchor, which doesn't make sense
+# since the anchor was discarded immediately.
+anchor-dollar:
+ @echo $@: ${:U${value:L:e$=x}:Q}
+ @echo $@: ${:U${value:L:e=x}:Q}
+
+# Words that don't match are copied unmodified.
+# The % placeholder can be anywhere in the string.
+mismatch:
+ @echo $@: ${:Ufile.c file.h:%.c=%.cpp}
+ @echo $@: ${:Ufile.c other.c:file.%=renamed.%}
+
+# Trying to cover all possible variants of the SysV modifier.
+LIST= one two
+EXPR.1= ${LIST:o=X}
+EXP.1= one twX
+EXPR.2= ${LIST:o=}
+EXP.2= one tw
+EXPR.3= ${LIST:o=%}
+EXP.3= one tw%
+EXPR.4= ${LIST:%o=X}
+EXP.4= one X
+EXPR.5= ${LIST:o%=X}
+EXP.5= X two
+EXPR.6= ${LIST:o%e=X}
+EXP.6= X two
+EXPR.7= ${LIST:o%%e=X} # Only the first '%' is the wildcard.
+EXP.7= one two # None of the words contains a literal '%'.
+EXPR.8= ${LIST:%=%%}
+EXP.8= one% two%
+EXPR.9= ${LIST:%nes=%xxx} # lhs is longer than the word "one"
+EXP.9= one two
+
+.for i in ${:U:range=9}
+.if ${EXPR.$i} != ${EXP.$i}
+.warning test case $i expected "${EXP.$i}", got "${EXPR.$i}
+.endif
+.endfor
diff --git a/contrib/bmake/unit-tests/varmod-tail.exp b/contrib/bmake/unit-tests/varmod-tail.exp
new file mode 100644
index 000000000000..e25c1cc4b914
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-tail.exp
@@ -0,0 +1,10 @@
+tail (basename) of 'a/b/c' is 'c'
+tail (basename) of 'def' is 'def'
+tail (basename) of 'a.b.c' is 'a.b.c'
+tail (basename) of 'a.b/c' is 'c'
+tail (basename) of 'a' is 'a'
+tail (basename) of 'a.a' is 'a.a'
+tail (basename) of '.gitignore' is '.gitignore'
+tail (basename) of 'a' is 'a'
+tail (basename) of 'a.a' is 'a.a'
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-tail.mk b/contrib/bmake/unit-tests/varmod-tail.mk
new file mode 100644
index 000000000000..a8078cc67335
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-tail.mk
@@ -0,0 +1,9 @@
+# $NetBSD: varmod-tail.mk,v 1.3 2020/08/23 15:09:15 rillig Exp $
+#
+# Tests for the :T variable modifier, which returns the basename of each of
+# the words in the variable value.
+
+all:
+.for path in a/b/c def a.b.c a.b/c a a.a .gitignore a a.a
+ @echo "tail (basename) of '"${path:Q}"' is '"${path:T:Q}"'"
+.endfor
diff --git a/contrib/bmake/unit-tests/varmod-to-abs.exp b/contrib/bmake/unit-tests/varmod-to-abs.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-to-abs.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-to-abs.mk b/contrib/bmake/unit-tests/varmod-to-abs.mk
new file mode 100644
index 000000000000..7a74e89088e5
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-to-abs.mk
@@ -0,0 +1,9 @@
+# $NetBSD: varmod-to-abs.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the :tA variable modifier, which returns the absolute path for
+# each of the words in the variable value.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varmod-to-lower.exp b/contrib/bmake/unit-tests/varmod-to-lower.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-to-lower.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-to-lower.mk b/contrib/bmake/unit-tests/varmod-to-lower.mk
new file mode 100644
index 000000000000..38a4add4cfe5
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-to-lower.mk
@@ -0,0 +1,19 @@
+# $NetBSD: varmod-to-lower.mk,v 1.3 2020/08/28 17:21:02 rillig Exp $
+#
+# Tests for the :tl variable modifier, which returns the words in the
+# variable value, converted to lowercase.
+
+.if ${:UUPPER:tl} != "upper"
+.error
+.endif
+
+.if ${:Ulower:tl} != "lower"
+.error
+.endif
+
+.if ${:UMixeD case.:tl} != "mixed case."
+.error
+.endif
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varmod-to-many-words.exp b/contrib/bmake/unit-tests/varmod-to-many-words.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-to-many-words.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-to-many-words.mk b/contrib/bmake/unit-tests/varmod-to-many-words.mk
new file mode 100644
index 000000000000..10cddb00c5e4
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-to-many-words.mk
@@ -0,0 +1,9 @@
+# $NetBSD: varmod-to-many-words.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the :tw modifier, which treats the variable as many words,
+# to undo a previous :tW modifier.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varmod-to-one-word.exp b/contrib/bmake/unit-tests/varmod-to-one-word.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-to-one-word.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-to-one-word.mk b/contrib/bmake/unit-tests/varmod-to-one-word.mk
new file mode 100644
index 000000000000..0865ce8fb41f
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-to-one-word.mk
@@ -0,0 +1,9 @@
+# $NetBSD: varmod-to-one-word.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the :tW variable modifier, which treats the variable value
+# as a single word, for all following modifiers.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varmod-to-separator.exp b/contrib/bmake/unit-tests/varmod-to-separator.exp
new file mode 100644
index 000000000000..07dd5b5e7c5b
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-to-separator.exp
@@ -0,0 +1,9 @@
+make: Bad modifier `:tx' for WORDS
+make: "varmod-to-separator.mk" line 104: Malformed conditional (${WORDS:tx} != "anything")
+make: "varmod-to-separator.mk" line 108: Parsing continues here.
+make: Bad modifier `:t\X' for WORDS
+make: "varmod-to-separator.mk" line 112: Malformed conditional (${WORDS:t\X} != "anything")
+make: "varmod-to-separator.mk" line 115: Parsing continues here.
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/varmod-to-separator.mk b/contrib/bmake/unit-tests/varmod-to-separator.mk
new file mode 100644
index 000000000000..68e728c276dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-to-separator.mk
@@ -0,0 +1,118 @@
+# $NetBSD: varmod-to-separator.mk,v 1.3 2020/08/31 19:58:21 rillig Exp $
+#
+# Tests for the :ts variable modifier, which joins the words of the variable
+# using an arbitrary character as word separator.
+
+WORDS= one two three four five six
+
+# The words are separated by a single space, just as usual.
+.if ${WORDS:ts } != "one two three four five six"
+. warning Space as separator does not work.
+.endif
+
+# The separator can be an arbitrary character, for example a comma.
+.if ${WORDS:ts,} != "one,two,three,four,five,six"
+. warning Comma as separator does not work.
+.endif
+
+# After the :ts modifier, other modifiers can follow.
+.if ${WORDS:ts/:tu} != "ONE/TWO/THREE/FOUR/FIVE/SIX"
+. warning Chaining modifiers does not work.
+.endif
+
+# To use the ':' as the separator, just write it normally.
+# The first colon is the separator, the second ends the modifier.
+.if ${WORDS:ts::tu} != "ONE:TWO:THREE:FOUR:FIVE:SIX"
+. warning Colon as separator does not work.
+.endif
+
+# When there is just a colon but no other character, the words are
+# "separated" by an empty string, that is, they are all squashed
+# together.
+.if ${WORDS:ts:tu} != "ONETWOTHREEFOURFIVESIX"
+. warning Colon as separator does not work.
+.endif
+
+# Applying the :tu modifier first and then the :ts modifier does not change
+# anything since neither of these modifiers is related to how the string is
+# split into words. Beware of separating the words using a single or double
+# quote though, or other special characters like dollar or backslash.
+#
+# This example also demonstrates that the closing brace is not interpreted
+# as a separator, but as the closing delimiter of the whole variable
+# expression.
+.if ${WORDS:tu:ts} != "ONETWOTHREEFOURFIVESIX"
+. warning Colon as separator does not work.
+.endif
+
+# The '}' plays the same role as the ':' in the preceding examples.
+# Since there is a single character before it, that character is taken as
+# the separator.
+.if ${WORDS:tu:ts/} != "ONE/TWO/THREE/FOUR/FIVE/SIX"
+. warning Colon as separator does not work.
+.endif
+
+# Now it gets interesting and ambiguous: The separator could either be empty
+# since it is followed by a colon. Or it could be the colon since that
+# colon is followed by the closing brace. It's the latter case.
+.if ${WORDS:ts:} != "one:two:three:four:five:six"
+. warning Colon followed by closing brace does not work.
+.endif
+
+# As in the ${WORDS:tu:ts} example above, the separator is empty.
+.if ${WORDS:ts} != "onetwothreefourfivesix"
+. warning Empty separator before closing brace does not work.
+.endif
+
+# The :ts modifier can be followed by other modifiers.
+.if ${WORDS:ts:S/two/2/} != "one2threefourfivesix"
+. warning Separator followed by :S modifier does not work.
+.endif
+
+# The :ts modifier can follow other modifiers.
+.if ${WORDS:S/two/2/:ts} != "one2threefourfivesix"
+. warning :S modifier followed by :ts modifier does not work.
+.endif
+
+# The :ts modifier with an actual separator can be followed by other
+# modifiers.
+.if ${WORDS:ts/:S/two/2/} != "one/2/three/four/five/six"
+. warning The :ts modifier followed by an :S modifier does not work.
+.endif
+
+# The separator can be \n, which is a newline.
+.if ${WORDS:[1..3]:ts\n} != "one${.newline}two${.newline}three"
+. warning The separator \n does not produce a newline.
+.endif
+
+# The separator can be \t, which is a tab.
+.if ${WORDS:[1..3]:ts\t} != "one two three"
+. warning The separator \t does not produce a tab.
+.endif
+
+# The separator can be given as octal number.
+.if ${WORDS:[1..3]:ts\012:tu} != "ONE${.newline}TWO${.newline}THREE"
+. warning The separator \012 is not interpreted in octal ASCII.
+.endif
+
+# The separator can be given as hexadecimal number.
+.if ${WORDS:[1..3]:ts\xa:tu} != "ONE${.newline}TWO${.newline}THREE"
+. warning The separator \xa is not interpreted in hexadecimal ASCII.
+.endif
+
+# In the :t modifier, the :t must be followed by any of A, l, s, u.
+.if ${WORDS:tx} != "anything"
+. info This line is not reached because of the malformed condition.
+. info If this line were reached, it would be visible in the -dcpv log.
+.endif
+.info Parsing continues here.
+
+# After the backslash, only n, t, an octal number, or x and a hexadecimal
+# number are allowed.
+.if ${WORDS:t\X} != "anything"
+. info This line is not reached.
+.endif
+.info Parsing continues here.
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varmod-to-upper.exp b/contrib/bmake/unit-tests/varmod-to-upper.exp
new file mode 100644
index 000000000000..8453da538d3b
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-to-upper.exp
@@ -0,0 +1,2 @@
+mod-tu-space: A B
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-to-upper.mk b/contrib/bmake/unit-tests/varmod-to-upper.mk
new file mode 100644
index 000000000000..26c0b25e5142
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-to-upper.mk
@@ -0,0 +1,21 @@
+# $NetBSD: varmod-to-upper.mk,v 1.4 2020/08/28 17:21:02 rillig Exp $
+#
+# Tests for the :tu variable modifier, which returns the words in the
+# variable value, converted to uppercase.
+
+.if ${:UUPPER:tu} != "UPPER"
+.error
+.endif
+
+.if ${:Ulower:tu} != "LOWER"
+.error
+.endif
+
+.if ${:UMixeD case.:tu} != "MIXED CASE."
+.error
+.endif
+
+# The :tu and :tl modifiers operate on the variable value as a single string,
+# not as a list of words. Therefore, the adjacent spaces are preserved.
+mod-tu-space:
+ @echo $@: ${a b:L:tu:Q}
diff --git a/contrib/bmake/unit-tests/varmod-undefined.exp b/contrib/bmake/unit-tests/varmod-undefined.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-undefined.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-undefined.mk b/contrib/bmake/unit-tests/varmod-undefined.mk
new file mode 100644
index 000000000000..9ed35a8406fe
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-undefined.mk
@@ -0,0 +1,55 @@
+# $NetBSD: varmod-undefined.mk,v 1.3 2020/08/23 20:49:33 rillig Exp $
+#
+# Tests for the :U variable modifier, which returns the given string
+# if the variable is undefined.
+#
+# The pattern ${:Uword} is heavily used when expanding .for loops.
+
+# This is how an expanded .for loop looks like.
+# .for word in one
+# . if ${word} != one
+# . error ${word}
+# . endif
+# .endfor
+
+.if ${:Uone} != one
+. error ${:Uone}
+.endif
+
+# The variable expressions in the text of the :U modifier may be arbitrarily
+# nested.
+
+.if ${:U${:Unested}${${${:Udeeply}}}} != nested
+.error
+.endif
+
+# The nested variable expressions may contain braces, and these braces don't
+# need to match pairwise. In the following example, the :S modifier uses '{'
+# as delimiter, which confuses both editors and humans because the opening
+# and # closing braces don't match anymore. It's syntactically valid though.
+# For more similar examples, see varmod-subst.mk, mod-subst-delimiter.
+
+.if ${:U${:Uvalue:S{a{X{}} != vXlue
+.error
+.endif
+
+# The escaping rules for the :U modifier (left-hand side) and condition
+# string literals (right-hand side) are completely different.
+#
+# In the :U modifier, the backslash only escapes very few characters, all
+# other backslashes are retained.
+#
+# In condition string literals, the backslash always escapes the following
+# character, no matter whether it would be necessary or not.
+#
+# In both contexts, \n is an escaped letter n, not a newline; that's what
+# the .newline variable is for.
+#
+# Whitespace at the edges is preserved, on both sides of the comparison.
+
+.if ${:U \: \} \$ \\ \a \b \n } != " : } \$ \\ \\a \\b \\n "
+.error
+.endif
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varmod-unique.exp b/contrib/bmake/unit-tests/varmod-unique.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-unique.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-unique.mk b/contrib/bmake/unit-tests/varmod-unique.mk
new file mode 100644
index 000000000000..ea4698764947
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-unique.mk
@@ -0,0 +1,39 @@
+# $NetBSD: varmod-unique.mk,v 1.4 2020/08/31 17:41:38 rillig Exp $
+#
+# Tests for the :u variable modifier, which discards adjacent duplicate
+# words.
+
+.if ${:U1 2 1:u} != "1 2 1"
+. warning The :u modifier only merges _adjacent_ duplicate words.
+.endif
+
+.if ${:U1 2 2 3:u} != "1 2 3"
+. warning The :u modifier must merge adjacent duplicate words.
+.endif
+
+.if ${:U:u} != ""
+. warning The :u modifier must do nothing with an empty word list.
+.endif
+
+.if ${:U1:u} != "1"
+. warning The :u modifier must do nothing with a single-element word list.
+.endif
+
+.if ${:U1 1 1 1 1 1 1 1:u} != "1"
+. warning The :u modifier must merge _all_ adjacent duplicate words.
+.endif
+
+.if ${:U 1 2 1 1 :u} != "1 2 1"
+. warning The :u modifier must normalize whitespace between the words.
+.endif
+
+.if ${:U1 1 1 1 2:u} != "1 2"
+. warning Duplicate words at the beginning must be merged.
+.endif
+
+.if ${:U1 2 2 2 2:u} != "1 2"
+. warning Duplicate words at the end must be merged.
+.endif
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varmod.exp b/contrib/bmake/unit-tests/varmod.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod.mk b/contrib/bmake/unit-tests/varmod.mk
new file mode 100644
index 000000000000..68bf165bf72b
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varmod.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for variable modifiers, such as :Q, :S,from,to or :Ufallback.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-dollar.exp b/contrib/bmake/unit-tests/varname-dollar.exp
new file mode 100644
index 000000000000..c880e82f0170
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dollar.exp
@@ -0,0 +1,5 @@
+make: "varname-dollar.mk" line 16: dollar is $.
+make: "varname-dollar.mk" line 17: dollar in braces is .
+make: "varname-dollar.mk" line 25: dollar is $.
+make: "varname-dollar.mk" line 26: dollar in braces is dollar.
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dollar.mk b/contrib/bmake/unit-tests/varname-dollar.mk
new file mode 100644
index 000000000000..d1db9f833306
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dollar.mk
@@ -0,0 +1,29 @@
+# $NetBSD: varname-dollar.mk,v 1.3 2020/08/19 05:40:06 rillig Exp $
+#
+# Tests for the expression "$$", which looks as if it referred to a variable,
+# but simply expands to a single '$' sign.
+#
+# If there really were a special variable named '$', the expressions ${${DOLLAR}}
+# and $$ would always expand to the same value.
+
+# Using the dollar sign in variable names is tricky and not recommended.
+# To see that using this variable indeed affects the variable '$', run the
+# test individually with the -dv option.
+DOLLAR= $$
+
+# At this point, the variable '$' is not defined. Therefore the second line
+# returns an empty string.
+.info dollar is $$.
+.info dollar in braces is ${${DOLLAR}}.
+
+# Now overwrite the '$' variable to see whether '$$' really expands to that
+# variable, or whether '$$' is handled by the parser.
+${DOLLAR}= dollar
+
+# At this point, the variable '$' is defined, therefore its value is printed
+# in the second .info directive.
+.info dollar is $$.
+.info dollar in braces is ${${DOLLAR}}.
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-dot-alltargets.exp b/contrib/bmake/unit-tests/varname-dot-alltargets.exp
new file mode 100644
index 000000000000..992c2573b519
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-alltargets.exp
@@ -0,0 +1,4 @@
+
+first second source
+first second source all .END
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-alltargets.mk b/contrib/bmake/unit-tests/varname-dot-alltargets.mk
new file mode 100644
index 000000000000..0d16c8e6fef0
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-alltargets.mk
@@ -0,0 +1,25 @@
+# $NetBSD: varname-dot-alltargets.mk,v 1.3 2020/08/25 22:51:54 rillig Exp $
+#
+# Tests for the special .ALLTARGETS variable.
+
+.MAIN: all
+
+TARGETS_1:= ${.ALLTARGETS}
+
+first second: source
+
+TARGETS_2:= ${.ALLTARGETS}
+
+all:
+ # Since the tests are run with the -r option, no targets are
+ # defined at the beginning.
+ @echo ${TARGETS_1}
+
+ # Only first and second are "real" targets.
+ # The .ALLTARGETS variable is not about targets though, but
+ # about all nodes, therefore source is also included.
+ @echo ${TARGETS_2}
+
+ # Interestingly, the .END target is also implicitly defined at
+ # this point.
+ @echo ${.ALLTARGETS}
diff --git a/contrib/bmake/unit-tests/varname-dot-curdir.exp b/contrib/bmake/unit-tests/varname-dot-curdir.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-curdir.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-curdir.mk b/contrib/bmake/unit-tests/varname-dot-curdir.mk
new file mode 100644
index 000000000000..3795d87b030f
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-curdir.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-dot-curdir.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special .CURDIR variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-dot-includedfromdir.exp b/contrib/bmake/unit-tests/varname-dot-includedfromdir.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-includedfromdir.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-includedfromdir.mk b/contrib/bmake/unit-tests/varname-dot-includedfromdir.mk
new file mode 100644
index 000000000000..e5a61793baaf
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-includedfromdir.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-dot-includedfromdir.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special .INCLUDEDFROMDIR variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-dot-includedfromfile.exp b/contrib/bmake/unit-tests/varname-dot-includedfromfile.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-includedfromfile.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-includedfromfile.mk b/contrib/bmake/unit-tests/varname-dot-includedfromfile.mk
new file mode 100644
index 000000000000..403f562c27fb
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-includedfromfile.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-dot-includedfromfile.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special .INCLUDEDFROMFILE variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-dot-includes.exp b/contrib/bmake/unit-tests/varname-dot-includes.exp
new file mode 100755
index 000000000000..edc90f068899
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-includes.exp
@@ -0,0 +1,2 @@
+.INCLUDES= -I. -I..
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-includes.mk b/contrib/bmake/unit-tests/varname-dot-includes.mk
new file mode 100755
index 000000000000..33e8669c27be
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-includes.mk
@@ -0,0 +1,20 @@
+# $NetBSD: varname-dot-includes.mk,v 1.1 2020/08/28 03:51:06 rillig Exp $
+#
+# Tests for the special .INCLUDES variable, which is not documented in the
+# manual page.
+#
+# It is yet unclear in which situations this feature is useful.
+
+.SUFFIXES: .h
+
+.PATH.h: . ..
+
+.INCLUDES: .h
+
+# The .INCLUDES variable is not yet available.
+.if defined(${.INCLUDES:Q})
+.error
+.endif
+
+all:
+ @echo .INCLUDES=${.INCLUDES:Q}
diff --git a/contrib/bmake/unit-tests/varname-dot-libs.exp b/contrib/bmake/unit-tests/varname-dot-libs.exp
new file mode 100755
index 000000000000..97b7e720bdd4
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-libs.exp
@@ -0,0 +1,2 @@
+.LIBS= -L. -L..
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-libs.mk b/contrib/bmake/unit-tests/varname-dot-libs.mk
new file mode 100755
index 000000000000..80abeb50b48c
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-libs.mk
@@ -0,0 +1,20 @@
+# $NetBSD: varname-dot-libs.mk,v 1.1 2020/08/28 03:51:06 rillig Exp $
+#
+# Tests for the special .LIBS variable, which is not documented in the
+# manual page.
+#
+# It is yet unclear in which situations this feature is useful.
+
+.SUFFIXES: .a
+
+.PATH.a: . ..
+
+.LIBS: .a
+
+# The .LIBS variable is not yet available.
+.if defined(${.LIBS:Q})
+.error
+.endif
+
+all:
+ @echo .LIBS=${.LIBS:Q}
diff --git a/contrib/bmake/unit-tests/varname-dot-make-dependfile.exp b/contrib/bmake/unit-tests/varname-dot-make-dependfile.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-dependfile.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-make-dependfile.mk b/contrib/bmake/unit-tests/varname-dot-make-dependfile.mk
new file mode 100644
index 000000000000..882c5344d9be
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-dependfile.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-dot-make-dependfile.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special .MAKE.DEPENDFILE variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-dot-make-expand_variables.exp b/contrib/bmake/unit-tests/varname-dot-make-expand_variables.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-expand_variables.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-make-expand_variables.mk b/contrib/bmake/unit-tests/varname-dot-make-expand_variables.mk
new file mode 100644
index 000000000000..cff2db4b9a21
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-expand_variables.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-dot-make-expand_variables.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special .MAKE.EXPAND_VARIABLES variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-dot-make-exported.exp b/contrib/bmake/unit-tests/varname-dot-make-exported.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-exported.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-make-exported.mk b/contrib/bmake/unit-tests/varname-dot-make-exported.mk
new file mode 100644
index 000000000000..123762dea9d0
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-exported.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-dot-make-exported.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special .MAKE.EXPORTED variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-dot-make-jobs-prefix.exp b/contrib/bmake/unit-tests/varname-dot-make-jobs-prefix.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-jobs-prefix.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-make-jobs-prefix.mk b/contrib/bmake/unit-tests/varname-dot-make-jobs-prefix.mk
new file mode 100644
index 000000000000..bf445ab2d349
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-jobs-prefix.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-dot-make-jobs-prefix.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special .MAKE.JOBS.PREFIX variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-dot-make-jobs.exp b/contrib/bmake/unit-tests/varname-dot-make-jobs.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-jobs.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-make-jobs.mk b/contrib/bmake/unit-tests/varname-dot-make-jobs.mk
new file mode 100644
index 000000000000..1e99b3d28ea8
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-jobs.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-dot-make-jobs.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special .MAKE.JOBS variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-dot-make-level.exp b/contrib/bmake/unit-tests/varname-dot-make-level.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-level.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-make-level.mk b/contrib/bmake/unit-tests/varname-dot-make-level.mk
new file mode 100644
index 000000000000..c4f2c0db7da6
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-level.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-dot-make-level.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special .MAKE.LEVEL variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-dot-make-makefile_preference.exp b/contrib/bmake/unit-tests/varname-dot-make-makefile_preference.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-makefile_preference.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-make-makefile_preference.mk b/contrib/bmake/unit-tests/varname-dot-make-makefile_preference.mk
new file mode 100644
index 000000000000..58cc8a2bf516
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-makefile_preference.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-dot-make-makefile_preference.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special .MAKE.MAKEFILE_PREFERENCE variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-dot-make-makefiles.exp b/contrib/bmake/unit-tests/varname-dot-make-makefiles.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-makefiles.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-make-makefiles.mk b/contrib/bmake/unit-tests/varname-dot-make-makefiles.mk
new file mode 100644
index 000000000000..f7449bbfc729
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-makefiles.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-dot-make-makefiles.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special .MAKE.MAKEFILES variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-bailiwick.exp b/contrib/bmake/unit-tests/varname-dot-make-meta-bailiwick.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-meta-bailiwick.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-bailiwick.mk b/contrib/bmake/unit-tests/varname-dot-make-meta-bailiwick.mk
new file mode 100644
index 000000000000..130a7b483291
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-meta-bailiwick.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-dot-make-meta-bailiwick.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special .MAKE.META.BAILIWICK variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-created.exp b/contrib/bmake/unit-tests/varname-dot-make-meta-created.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-meta-created.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-created.mk b/contrib/bmake/unit-tests/varname-dot-make-meta-created.mk
new file mode 100644
index 000000000000..bc149b60ddf4
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-meta-created.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-dot-make-meta-created.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special .MAKE.META.CREATED variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-files.exp b/contrib/bmake/unit-tests/varname-dot-make-meta-files.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-meta-files.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-files.mk b/contrib/bmake/unit-tests/varname-dot-make-meta-files.mk
new file mode 100644
index 000000000000..e6fe9e92bfca
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-meta-files.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-dot-make-meta-files.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special .MAKE.META.FILES variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.exp b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.mk b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.mk
new file mode 100644
index 000000000000..c41aec4acdf8
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-dot-make-meta-ignore_filter.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special .MAKE.META.IGNORE_FILTER variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.exp b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.mk b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.mk
new file mode 100644
index 000000000000..4ae34f51608b
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-dot-make-meta-ignore_paths.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special .MAKE.META.IGNORE_PATHS variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.exp b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.mk b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.mk
new file mode 100644
index 000000000000..ea9fc49f1718
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-dot-make-meta-ignore_patterns.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special .MAKE.META.IGNORE_PATTERNS variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-prefix.exp b/contrib/bmake/unit-tests/varname-dot-make-meta-prefix.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-meta-prefix.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-prefix.mk b/contrib/bmake/unit-tests/varname-dot-make-meta-prefix.mk
new file mode 100644
index 000000000000..d4fdf64e6db3
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-meta-prefix.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-dot-make-meta-prefix.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special .MAKE.META.PREFIX variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-dot-make-mode.exp b/contrib/bmake/unit-tests/varname-dot-make-mode.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-mode.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-make-mode.mk b/contrib/bmake/unit-tests/varname-dot-make-mode.mk
new file mode 100644
index 000000000000..ee75a54ebd74
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-mode.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-dot-make-mode.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special .MAKE.MODE variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-dot-make-path_filemon.exp b/contrib/bmake/unit-tests/varname-dot-make-path_filemon.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-path_filemon.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-make-path_filemon.mk b/contrib/bmake/unit-tests/varname-dot-make-path_filemon.mk
new file mode 100644
index 000000000000..8c07529f1b4c
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-path_filemon.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-dot-make-path_filemon.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special .MAKE.PATH_FILEMON variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-dot-make-pid.exp b/contrib/bmake/unit-tests/varname-dot-make-pid.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-pid.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-make-pid.mk b/contrib/bmake/unit-tests/varname-dot-make-pid.mk
new file mode 100644
index 000000000000..bea114d33547
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-pid.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-dot-make-pid.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special .MAKE.PID variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-dot-make-ppid.exp b/contrib/bmake/unit-tests/varname-dot-make-ppid.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-ppid.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-make-ppid.mk b/contrib/bmake/unit-tests/varname-dot-make-ppid.mk
new file mode 100644
index 000000000000..c9471542ca35
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-ppid.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-dot-make-ppid.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special .MAKE.PPID variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-dot-make-save_dollars.exp b/contrib/bmake/unit-tests/varname-dot-make-save_dollars.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-save_dollars.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-make-save_dollars.mk b/contrib/bmake/unit-tests/varname-dot-make-save_dollars.mk
new file mode 100644
index 000000000000..97f37a646d2d
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-make-save_dollars.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-dot-make-save_dollars.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special .MAKE.SAVE_DOLLARS variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-dot-makeoverrides.exp b/contrib/bmake/unit-tests/varname-dot-makeoverrides.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-makeoverrides.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-makeoverrides.mk b/contrib/bmake/unit-tests/varname-dot-makeoverrides.mk
new file mode 100644
index 000000000000..a897f4667175
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-makeoverrides.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-dot-makeoverrides.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special .MAKE.MAKEOVERRIDES variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-dot-newline.exp b/contrib/bmake/unit-tests/varname-dot-newline.exp
new file mode 100644
index 000000000000..13943539bf3b
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-newline.exp
@@ -0,0 +1,4 @@
+make: "varname-dot-newline.mk" line 16: The .newline variable can be overwritten. Just don't do that.
+first
+second
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-newline.mk b/contrib/bmake/unit-tests/varname-dot-newline.mk
new file mode 100644
index 000000000000..fa9ac0759649
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-newline.mk
@@ -0,0 +1,23 @@
+# $NetBSD: varname-dot-newline.mk,v 1.3 2020/08/19 05:51:18 rillig Exp $
+#
+# Tests for the special .newline variable.
+#
+# Contrary to the special variable named "" that is used in expressions like
+# ${:Usome-value}, the variable ".newline" is not protected against
+# modification. Nobody exploits that though.
+
+NEWLINE:= ${.newline}
+
+.newline= overwritten
+
+.if ${.newline} == ${NEWLINE}
+.info The .newline variable cannot be overwritten. Good.
+.else
+.info The .newline variable can be overwritten. Just don't do that.
+.endif
+
+# Restore the original value.
+.newline= ${NEWLINE}
+
+all:
+ @echo 'first${.newline}second'
diff --git a/contrib/bmake/unit-tests/varname-dot-objdir.exp b/contrib/bmake/unit-tests/varname-dot-objdir.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-objdir.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-objdir.mk b/contrib/bmake/unit-tests/varname-dot-objdir.mk
new file mode 100644
index 000000000000..e662e8ac56fa
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-objdir.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-dot-objdir.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special .OBJDIR variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-dot-parsedir.exp b/contrib/bmake/unit-tests/varname-dot-parsedir.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-parsedir.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-parsedir.mk b/contrib/bmake/unit-tests/varname-dot-parsedir.mk
new file mode 100644
index 000000000000..6aeb5878a457
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-parsedir.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-dot-parsedir.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special .PARSEDIR variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-dot-parsefile.exp b/contrib/bmake/unit-tests/varname-dot-parsefile.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-parsefile.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-parsefile.mk b/contrib/bmake/unit-tests/varname-dot-parsefile.mk
new file mode 100644
index 000000000000..9deff34a70d0
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-parsefile.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-dot-parsefile.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special .PARSEFILE variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-dot-path.exp b/contrib/bmake/unit-tests/varname-dot-path.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-path.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-path.mk b/contrib/bmake/unit-tests/varname-dot-path.mk
new file mode 100644
index 000000000000..8f4e3a5dcfa4
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-path.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-dot-path.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special .PATH variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-dot-shell.exp b/contrib/bmake/unit-tests/varname-dot-shell.exp
new file mode 100755
index 000000000000..af4baaf44c0b
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-shell.exp
@@ -0,0 +1,19 @@
+ParseReadLine (8): 'ORIG_SHELL:= ${.SHELL}'
+Var_Parse: ${.SHELL} with VARE_WANTRES|VARE_ASSIGN
+Global:delete .SHELL (not found)
+Command:.SHELL = (details omitted)
+ParseReadLine (10): '.SHELL= overwritten'
+Global:.SHELL = overwritten
+Var_Parse: ${.SHELL} != ${ORIG_SHELL} with VARE_UNDEFERR|VARE_WANTRES
+ParseReadLine (18): '.undef .SHELL'
+Global:delete .SHELL
+ParseReadLine (19): '.SHELL= newly overwritten'
+Global:.SHELL = newly overwritten
+Var_Parse: ${.SHELL} != ${ORIG_SHELL} with VARE_UNDEFERR|VARE_WANTRES
+ParseReadLine (24): 'all:'
+ParseReadLine (25): ' @echo ${.SHELL:M*}'
+Var_Parse: ${.SHELL:M*} with VARE_WANTRES
+Applying ${.SHELL:M...} to "(details omitted)" (eflags = VARE_WANTRES, vflags = VAR_READONLY)
+Pattern[.SHELL] for [(details omitted)] is [*]
+Result of ${.SHELL:M*} is "(details omitted)" (eflags = VARE_WANTRES, vflags = VAR_READONLY)
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-shell.mk b/contrib/bmake/unit-tests/varname-dot-shell.mk
new file mode 100755
index 000000000000..4a2c52b39cbc
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-shell.mk
@@ -0,0 +1,25 @@
+# $NetBSD: varname-dot-shell.mk,v 1.2 2020/08/23 09:28:52 rillig Exp $
+#
+# Tests for the special .SHELL variable, which contains the shell used for
+# running the commands.
+#
+# This variable is read-only.
+
+ORIG_SHELL:= ${.SHELL}
+
+.SHELL= overwritten
+.if ${.SHELL} != ${ORIG_SHELL}
+.error
+.endif
+
+# Trying to delete the variable.
+# This has no effect since the variable is not defined in the global context,
+# but in the command-line context.
+.undef .SHELL
+.SHELL= newly overwritten
+.if ${.SHELL} != ${ORIG_SHELL}
+.error
+.endif
+
+all:
+ @echo ${.SHELL:M*}
diff --git a/contrib/bmake/unit-tests/varname-dot-targets.exp b/contrib/bmake/unit-tests/varname-dot-targets.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-targets.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-targets.mk b/contrib/bmake/unit-tests/varname-dot-targets.mk
new file mode 100644
index 000000000000..2d1665744040
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-targets.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-dot-targets.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special .TARGETS variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-empty.exp b/contrib/bmake/unit-tests/varname-empty.exp
new file mode 100644
index 000000000000..77cb7c517e9d
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-empty.exp
@@ -0,0 +1,11 @@
+Var_Set("${:U}", "cmdline-u", ...) name expands to empty string - ignored
+Var_Set("", "cmline-plain", ...) name expands to empty string - ignored
+Var_Set("", "default", ...) name expands to empty string - ignored
+Var_Set("", "assigned", ...) name expands to empty string - ignored
+Var_Set("", "appended", ...) name expands to empty string - ignored
+Var_Set("", "", ...) name expands to empty string - ignored
+Var_Set("", "subst", ...) name expands to empty string - ignored
+Var_Set("", "shell-output", ...) name expands to empty string - ignored
+out: fallback
+out: 1 2 3
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-empty.mk b/contrib/bmake/unit-tests/varname-empty.mk
new file mode 100755
index 000000000000..b4ce05c3017b
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-empty.mk
@@ -0,0 +1,26 @@
+# $NetBSD: varname-empty.mk,v 1.5 2020/08/22 21:22:24 rillig Exp $
+#
+# Tests for the special variable with the empty name.
+#
+# The variable "" is not supposed to be assigned any value.
+# This is because it is heavily used in the .for loop expansion,
+# as well as to generate arbitrary strings, as in ${:Ufallback}.
+
+# Until 2020-08-22 it was possible to assign a value to the variable with
+# the empty name, leading to all kinds of unexpected effects.
+?= default
+= assigned # undefined behavior until 2020-08-22
++= appended
+:= subst
+!= echo 'shell-output'
+
+# The .for loop expands the expression ${i} to ${:U1}, ${:U2} and so on.
+# This only works if the variable with the empty name is guaranteed to
+# be undefined.
+.for i in 1 2 3
+NUMBERS+= ${i}
+.endfor
+
+all:
+ @echo out: ${:Ufallback}
+ @echo out: ${NUMBERS}
diff --git a/contrib/bmake/unit-tests/varname-make.exp b/contrib/bmake/unit-tests/varname-make.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-make.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-make.mk b/contrib/bmake/unit-tests/varname-make.mk
new file mode 100644
index 000000000000..049f00ef741b
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-make.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-make.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special MAKE variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-make_print_var_on_error.exp b/contrib/bmake/unit-tests/varname-make_print_var_on_error.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-make_print_var_on_error.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-make_print_var_on_error.mk b/contrib/bmake/unit-tests/varname-make_print_var_on_error.mk
new file mode 100644
index 000000000000..757d55780be1
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-make_print_var_on_error.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-make_print_var_on_error.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special .MAKE.PRINT_VAR_ON_ERROR variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-makeflags.exp b/contrib/bmake/unit-tests/varname-makeflags.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-makeflags.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-makeflags.mk b/contrib/bmake/unit-tests/varname-makeflags.mk
new file mode 100644
index 000000000000..b2e5f68b4e08
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-makeflags.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-makeflags.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special MAKEFLAGS variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-pwd.exp b/contrib/bmake/unit-tests/varname-pwd.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-pwd.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-pwd.mk b/contrib/bmake/unit-tests/varname-pwd.mk
new file mode 100644
index 000000000000..e11ee828117b
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-pwd.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-pwd.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special PWD variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname-vpath.exp b/contrib/bmake/unit-tests/varname-vpath.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-vpath.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-vpath.mk b/contrib/bmake/unit-tests/varname-vpath.mk
new file mode 100644
index 000000000000..8924647a5072
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-vpath.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname-vpath.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for the special VPATH variable.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varname.exp b/contrib/bmake/unit-tests/varname.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname.mk b/contrib/bmake/unit-tests/varname.mk
new file mode 100644
index 000000000000..9dd965083f3f
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname.mk
@@ -0,0 +1,8 @@
+# $NetBSD: varname.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+#
+# Tests for special variables, such as .MAKE or .PARSEDIR.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varparse-dynamic.exp b/contrib/bmake/unit-tests/varparse-dynamic.exp
new file mode 100644
index 000000000000..a2ff29413167
--- /dev/null
+++ b/contrib/bmake/unit-tests/varparse-dynamic.exp
@@ -0,0 +1,5 @@
+make: "varparse-dynamic.mk" line 8: Malformed conditional (${.TARGEX})
+make: "varparse-dynamic.mk" line 10: Malformed conditional (${.TARGXX})
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/varparse-dynamic.mk b/contrib/bmake/unit-tests/varparse-dynamic.mk
new file mode 100644
index 000000000000..724cca3a5035
--- /dev/null
+++ b/contrib/bmake/unit-tests/varparse-dynamic.mk
@@ -0,0 +1,14 @@
+# $NetBSD: varparse-dynamic.mk,v 1.1 2020/07/26 22:15:36 rillig Exp $
+
+# Before 2020-07-27, there was an off-by-one error in Var_Parse that skipped
+# the last character in the variable name.
+# To trigger the bug, the variable must not be defined.
+.if ${.TARGET} # exact match, may be undefined
+.endif
+.if ${.TARGEX} # 1 character difference, must be defined
+.endif
+.if ${.TARGXX} # 2 characters difference, must be defined
+.endif
+
+all:
+ @:
diff --git a/contrib/bmake/util.c b/contrib/bmake/util.c
index d674a6c21aac..2698b91678e4 100644
--- a/contrib/bmake/util.c
+++ b/contrib/bmake/util.c
@@ -1,9 +1,9 @@
-/* $NetBSD: util.c,v 1.57 2020/07/03 08:13:23 rillig Exp $ */
+/* $NetBSD: util.c,v 1.58 2020/08/01 14:47:49 rillig Exp $ */
/*
* Missing stuff from OS's
*
- * $Id: util.c,v 1.35 2020/07/04 18:16:55 sjg Exp $
+ * $Id: util.c,v 1.36 2020/08/01 23:08:14 sjg Exp $
*/
#if defined(__MINT__) || defined(__linux__)
#include <signal.h>
@@ -12,10 +12,10 @@
#include "make.h"
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: util.c,v 1.57 2020/07/03 08:13:23 rillig Exp $";
+static char rcsid[] = "$NetBSD: util.c,v 1.58 2020/08/01 14:47:49 rillig Exp $";
#else
#ifndef lint
-__RCSID("$NetBSD: util.c,v 1.57 2020/07/03 08:13:23 rillig Exp $");
+__RCSID("$NetBSD: util.c,v 1.58 2020/08/01 14:47:49 rillig Exp $");
#endif
#endif
@@ -178,39 +178,39 @@ strrcpy(char *ptr, char *str)
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 */
+ "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 */
diff --git a/contrib/bmake/var.c b/contrib/bmake/var.c
index c7e6b1b34fd4..173bb4f099c9 100644
--- a/contrib/bmake/var.c
+++ b/contrib/bmake/var.c
@@ -1,4 +1,4 @@
-/* $NetBSD: var.c,v 1.255 2020/07/04 17:41:04 rillig Exp $ */
+/* $NetBSD: var.c,v 1.484 2020/09/02 06:25:48 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -69,14 +69,14 @@
*/
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: var.c,v 1.255 2020/07/04 17:41:04 rillig Exp $";
+static char rcsid[] = "$NetBSD: var.c,v 1.484 2020/09/02 06:25:48 rillig Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 3/19/94";
#else
-__RCSID("$NetBSD: var.c,v 1.255 2020/07/04 17:41:04 rillig Exp $");
+__RCSID("$NetBSD: var.c,v 1.484 2020/09/02 06:25:48 rillig Exp $");
#endif
#endif /* not lint */
#endif
@@ -88,8 +88,7 @@ __RCSID("$NetBSD: var.c,v 1.255 2020/07/04 17:41:04 rillig Exp $");
* 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.
+ * yet exist.
*
* Var_Append Append more characters to an existing variable
* in the given context. The variable needn't
@@ -99,12 +98,11 @@ __RCSID("$NetBSD: var.c,v 1.255 2020/07/04 17:41:04 rillig Exp $");
*
* 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_Value Return the unexpanded value of a variable in a
+ * context or NULL if the variable is undefined.
*
* Var_Subst Substitute either a single variable or all
- * variables in a string, using the given context as
- * the top-most one.
+ * variables in a string, using the given context.
*
* Var_Parse Parse a variable expansion from a string and
* return the result and the number of characters
@@ -126,23 +124,32 @@ __RCSID("$NetBSD: var.c,v 1.255 2020/07/04 17:41:04 rillig Exp $");
#ifndef NO_REGEX
#include <regex.h>
#endif
-#include <ctype.h>
-#include <stdlib.h>
-#include <limits.h>
#include <time.h>
#include "make.h"
-#ifdef HAVE_STDINT_H
-#include <stdint.h>
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#elif defined(HAVE_STDINT_H)
+#include <stdint.h>
#endif
-#include "buf.h"
+#include "enum.h"
#include "dir.h"
#include "job.h"
#include "metachar.h"
-extern int makelevel;
+#define VAR_DEBUG_IF(cond, fmt, ...) \
+ if (!(DEBUG(VAR) && (cond))) \
+ (void) 0; \
+ else \
+ fprintf(debug_file, fmt, __VA_ARGS__)
+
+#define VAR_DEBUG(fmt, ...) VAR_DEBUG_IF(TRUE, fmt, __VA_ARGS__)
+
+ENUM_FLAGS_RTTI_3(VarEvalFlags,
+ VARE_UNDEFERR, VARE_WANTRES, VARE_ASSIGN);
+
/*
* This lets us tell if we have replaced the original environ
* (which we cannot free).
@@ -157,9 +164,11 @@ char **savedEnv = NULL;
char var_Error[] = "";
/*
- * Similar to var_Error, but returned when the 'VARF_UNDEFERR' flag for
- * Var_Parse is not set. Why not just use a constant? Well, gcc likes
- * to condense identical string instances...
+ * Similar to var_Error, but returned when the 'VARE_UNDEFERR' flag for
+ * Var_Parse is not set.
+ *
+ * Why not just use a constant? Well, GCC likes to condense identical string
+ * instances...
*/
static char varNoError[] = "";
@@ -167,139 +176,107 @@ static char varNoError[] = "";
* Traditionally we consume $$ during := like any other expansion.
* Other make's do not.
* This knob allows controlling the behavior.
- * FALSE for old behavior.
- * TRUE for new compatible.
+ * FALSE to consume $$ during := assignment.
+ * TRUE to preserve $$ during := assignment.
*/
#define SAVE_DOLLARS ".MAKE.SAVE_DOLLARS"
static Boolean save_dollars = FALSE;
/*
* 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
+ * 1) the environment. They cannot 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.
+ * the global context.
* 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.
+ * listed (but see checkEnvFirst).
*/
GNode *VAR_INTERNAL; /* variables from make itself */
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 enum {
+ FIND_CMD = 0x01, /* look in VAR_CMD when searching */
+ FIND_GLOBAL = 0x02, /* look in VAR_GLOBAL as well */
+ FIND_ENV = 0x04 /* look in the environment also */
+} VarFindFlags;
typedef enum {
- VAR_IN_USE = 0x01, /* Variable's value is currently being used.
- * Used to avoid endless recursion */
- VAR_FROM_ENV = 0x02, /* Variable comes from the environment */
- VAR_JUNK = 0x04, /* Variable is a junk variable that
- * should be destroyed when done with
- * it. Used by Var_Parse for undefined,
- * modified variables */
- VAR_KEEP = 0x08, /* Variable is VAR_JUNK, but we found
- * a use for it in some modifier and
- * the value is therefore valid */
- VAR_EXPORTED = 0x10, /* Variable is exported */
- VAR_REEXPORT = 0x20, /* Indicate if var needs re-export.
- * This would be true if it contains $'s */
- VAR_FROM_CMD = 0x40 /* Variable came from command line */
-} Var_Flags;
+ /* The variable's value is currently being used by Var_Parse or Var_Subst.
+ * This marker is used to avoid endless recursion. */
+ VAR_IN_USE = 0x01,
+ /* The variable comes from the environment.
+ * These variables are not registered in any GNode, therefore they must
+ * be freed as soon as they are not used anymore. */
+ VAR_FROM_ENV = 0x02,
+ /* The variable is a junk variable that should be destroyed when done with
+ * it. Used by Var_Parse for undefined, modified variables. */
+ VAR_JUNK = 0x04,
+ /* Variable is VAR_JUNK, but we found a use for it in some modifier and
+ * the value is therefore valid. */
+ VAR_KEEP = 0x08,
+ /* The variable is exported to the environment, to be used by child
+ * processes. */
+ VAR_EXPORTED = 0x10,
+ /* At the point where this variable was exported, it contained an
+ * unresolved reference to another variable. Before any child process is
+ * started, it needs to be exported again, in the hope that the referenced
+ * variable can then be resolved. */
+ VAR_REEXPORT = 0x20,
+ /* The variable came from command line. */
+ VAR_FROM_CMD = 0x40,
+ VAR_READONLY = 0x80
+} VarFlags;
+
+ENUM_FLAGS_RTTI_8(VarFlags,
+ VAR_IN_USE, VAR_FROM_ENV, VAR_JUNK, VAR_KEEP,
+ VAR_EXPORTED, VAR_REEXPORT, VAR_FROM_CMD, VAR_READONLY);
typedef struct Var {
- char *name; /* the variable's name */
+ char *name; /* the variable's name; it is allocated for
+ * environment variables and aliased to the
+ * Hash_Entry name for all other variables,
+ * and thus must not be modified */
Buffer val; /* its value */
- Var_Flags flags; /* miscellaneous status flags */
-} Var;
+ VarFlags flags; /* miscellaneous status flags */
+} 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
-/*
- * We pass this to Var_Export1 to tell it to leave the value alone.
- */
-#define VAR_EXPORT_LITERAL 2
-
typedef enum {
- VAR_SUB_GLOBAL = 0x01, /* Apply substitution globally */
- VAR_SUB_ONE = 0x02, /* Apply substitution to one word */
- VAR_SUB_MATCHED = 0x04, /* There was a match */
- VAR_MATCH_START = 0x08, /* Match at start of word */
- VAR_MATCH_END = 0x10, /* Match at end of word */
- VAR_NOSUBST = 0x20 /* don't expand vars in VarGetPattern */
-} VarPattern_Flags;
+ VAR_EXPORTED_NONE,
+ VAR_EXPORTED_YES,
+ VAR_EXPORTED_ALL
+} VarExportedMode;
-typedef enum {
- VAR_NO_EXPORT = 0x01 /* do not export */
-} VarSet_Flags;
+static VarExportedMode var_exportedVars = VAR_EXPORTED_NONE;
-typedef struct {
+typedef enum {
/*
- * 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.
+ * We pass this to Var_Export when doing the initial export
+ * or after updating an exported var.
*/
- 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 */
- VarPattern_Flags 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;
- Varf_Flags flags;
-} VarLoop;
-
-#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
+ VAR_EXPORT_PARENT = 0x01,
+ /*
+ * We pass this to Var_Export1 to tell it to leave the value alone.
+ */
+ VAR_EXPORT_LITERAL = 0x02
+} VarExportFlags;
-/* struct passed to VarSelectWords() for ":[start..end]" */
-typedef struct {
- int start; /* first word to select */
- int end; /* last word to select */
-} VarSelectWords_t;
+/* Flags for pattern matching in the :S and :C modifiers */
+typedef enum {
+ VARP_SUB_GLOBAL = 0x01, /* Apply substitution globally */
+ VARP_SUB_ONE = 0x02, /* Apply substitution to one word */
+ VARP_ANCHOR_START = 0x04, /* Match at start of word */
+ VARP_ANCHOR_END = 0x08 /* Match at end of word */
+} VarPatternFlags;
#define BROPEN '{'
#define BRCLOSE '}'
@@ -315,24 +292,19 @@ typedef struct {
* 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
+ * flags FIND_GLOBAL look in VAR_GLOBAL as well
+ * FIND_CMD look in VAR_CMD as well
+ * FIND_ENV look in the environment as well
*
* 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)
+VarFind(const char *name, GNode *ctxt, VarFindFlags flags)
{
- Hash_Entry *var;
- Var *v;
+ Hash_Entry *var;
/*
* If the variable name begins with a '.', it could very well be one of
@@ -340,32 +312,38 @@ VarFind(const char *name, GNode *ctxt, int flags)
* and substitute the short version in for 'name' if it matches one of
* them.
*/
- if (*name == '.' && isupper((unsigned char) name[1])) {
+ if (*name == '.' && isupper((unsigned char)name[1])) {
switch (name[1]) {
case 'A':
- if (!strcmp(name, ".ALLSRC"))
+ if (strcmp(name, ".ALLSRC") == 0)
name = ALLSRC;
- if (!strcmp(name, ".ARCHIVE"))
+ if (strcmp(name, ".ARCHIVE") == 0)
name = ARCHIVE;
break;
case 'I':
- if (!strcmp(name, ".IMPSRC"))
+ if (strcmp(name, ".IMPSRC") == 0)
name = IMPSRC;
break;
case 'M':
- if (!strcmp(name, ".MEMBER"))
+ if (strcmp(name, ".MEMBER") == 0)
name = MEMBER;
break;
case 'O':
- if (!strcmp(name, ".OODATE"))
+ if (strcmp(name, ".OODATE") == 0)
name = OODATE;
break;
case 'P':
- if (!strcmp(name, ".PREFIX"))
+ if (strcmp(name, ".PREFIX") == 0)
name = PREFIX;
break;
+ case 'S':
+ if (strcmp(name, ".SHELL") == 0 ) {
+ if (!shellPath)
+ Shell_Init();
+ }
+ break;
case 'T':
- if (!strcmp(name, ".TARGET"))
+ if (strcmp(name, ".TARGET") == 0)
name = TARGET;
break;
}
@@ -384,9 +362,9 @@ VarFind(const char *name, GNode *ctxt, int flags)
*/
var = Hash_FindEntry(&ctxt->context, name);
- if (var == NULL && (flags & FIND_CMD) && ctxt != VAR_CMD) {
+ 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)
{
@@ -396,42 +374,40 @@ VarFind(const char *name, GNode *ctxt, int flags)
var = Hash_FindEntry(&VAR_INTERNAL->context, name);
}
}
+
if (var == NULL && (flags & FIND_ENV)) {
char *env;
if ((env = getenv(name)) != NULL) {
- int len;
-
- v = bmake_malloc(sizeof(Var));
+ Var *v = bmake_malloc(sizeof(Var));
+ size_t len;
v->name = bmake_strdup(name);
len = strlen(env);
-
Buf_Init(&v->val, len + 1);
- Buf_AddBytes(&v->val, len, env);
+ Buf_AddBytes(&v->val, env, len);
v->flags = VAR_FROM_ENV;
return v;
- } else if (checkEnvFirst && (flags & FIND_GLOBAL) &&
- ctxt != VAR_GLOBAL)
- {
+ }
+
+ if (checkEnvFirst && (flags & FIND_GLOBAL) && ctxt != VAR_GLOBAL) {
var = Hash_FindEntry(&VAR_GLOBAL->context, name);
- if (var == NULL && ctxt != VAR_INTERNAL) {
+ if (var == NULL && ctxt != VAR_INTERNAL)
var = Hash_FindEntry(&VAR_INTERNAL->context, name);
- }
- if (var == NULL) {
+ if (var == NULL)
return NULL;
- } else {
+ else
return (Var *)Hash_GetValue(var);
- }
- } else {
- return NULL;
}
- } else if (var == NULL) {
+
return NULL;
- } else {
- return (Var *)Hash_GetValue(var);
}
+
+ if (var == NULL)
+ return NULL;
+ else
+ return (Var *)Hash_GetValue(var);
}
/*-
@@ -444,16 +420,13 @@ VarFind(const char *name, GNode *ctxt, int flags)
* 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.
+ * TRUE if it is an environment variable, FALSE otherwise.
*-----------------------------------------------------------------------
*/
static Boolean
VarFreeEnv(Var *v, Boolean destroy)
{
- if ((v->flags & VAR_FROM_ENV) == 0)
+ if (!(v->flags & VAR_FROM_ENV))
return FALSE;
free(v->name);
Buf_Destroy(&v->val, destroy);
@@ -461,87 +434,52 @@ VarFreeEnv(Var *v, Boolean destroy)
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
- *
- * 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.
- *-----------------------------------------------------------------------
- */
+/* Add a new variable of the given name and value to 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)
+VarAdd(const char *name, const char *val, GNode *ctxt, VarSet_Flags flags)
{
- Var *v;
- int len;
- Hash_Entry *h;
+ Var *v = bmake_malloc(sizeof(Var));
+ size_t len = strlen(val);
+ Hash_Entry *he;
- v = bmake_malloc(sizeof(Var));
-
- len = val ? strlen(val) : 0;
Buf_Init(&v->val, len + 1);
- Buf_AddBytes(&v->val, len, val);
+ Buf_AddBytes(&v->val, val, len);
v->flags = 0;
-
- h = Hash_CreateEntry(&ctxt->context, name, NULL);
- Hash_SetValue(h, v);
- v->name = h->name;
- if (DEBUG(VAR) && (ctxt->flags & INTERNAL) == 0) {
- fprintf(debug_file, "%s:%s = %s\n", ctxt->name, name, val);
- }
+ if (flags & VAR_SET_READONLY)
+ v->flags |= VAR_READONLY;
+
+ he = Hash_CreateEntry(&ctxt->context, name, NULL);
+ Hash_SetValue(he, v);
+ v->name = he->name;
+ VAR_DEBUG_IF(!(ctxt->flags & INTERNAL),
+ "%s:%s = %s\n", ctxt->name, name, val);
}
-/*-
- *-----------------------------------------------------------------------
- * Var_Delete --
- * Remove a variable from a context.
- *
- * Side Effects:
- * The Var structure is removed and freed.
- *
- *-----------------------------------------------------------------------
- */
+/* Remove a variable from a context, freeing the Var structure as well. */
void
Var_Delete(const char *name, GNode *ctxt)
{
- Hash_Entry *ln;
- char *cp;
-
- if (strchr(name, '$')) {
- cp = Var_Subst(NULL, name, VAR_GLOBAL, VARF_WANTRES);
- } else {
- cp = (char *)name;
- }
- ln = Hash_FindEntry(&ctxt->context, cp);
- if (DEBUG(VAR)) {
- fprintf(debug_file, "%s:delete %s%s\n",
- ctxt->name, cp, ln ? "" : " (not found)");
- }
- if (cp != name) {
- free(cp);
- }
- if (ln != NULL) {
- Var *v;
-
- v = (Var *)Hash_GetValue(ln);
- if ((v->flags & VAR_EXPORTED)) {
+ char *name_freeIt = NULL;
+ Hash_Entry *he;
+
+ if (strchr(name, '$') != NULL)
+ name = name_freeIt = Var_Subst(name, VAR_GLOBAL, VARE_WANTRES);
+ he = Hash_FindEntry(&ctxt->context, name);
+ VAR_DEBUG("%s:delete %s%s\n",
+ ctxt->name, name, he != NULL ? "" : " (not found)");
+ free(name_freeIt);
+
+ if (he != NULL) {
+ Var *v = (Var *)Hash_GetValue(he);
+ if (v->flags & VAR_EXPORTED)
unsetenv(v->name);
- }
- if (strcmp(MAKE_EXPORTED, v->name) == 0) {
+ if (strcmp(v->name, MAKE_EXPORTED) == 0)
var_exportedVars = VAR_EXPORTED_NONE;
- }
- if (v->name != ln->name)
+ if (v->name != he->name)
free(v->name);
- Hash_DeleteEntry(&ctxt->context, ln);
+ Hash_DeleteEntry(&ctxt->context, he);
Buf_Destroy(&v->val, TRUE);
free(v);
}
@@ -549,24 +487,22 @@ Var_Delete(const char *name, GNode *ctxt)
/*
- * Export a var.
- * We ignore make internal variables (those which start with '.')
+ * Export a single variable.
+ * 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 flags)
+static Boolean
+Var_Export1(const char *name, VarExportFlags flags)
{
- char tmp[BUFSIZ];
+ VarExportFlags parent = flags & VAR_EXPORT_PARENT;
Var *v;
- char *val = NULL;
- int n;
- int parent = (flags & VAR_EXPORT_PARENT);
+ char *val;
- if (*name == '.')
- return 0; /* skip internals */
- if (!name[1]) {
+ if (name[0] == '.')
+ return FALSE; /* skip internals */
+ if (name[1] == '\0') {
/*
* A single char.
* If it is one of the vars that should only appear in
@@ -578,48 +514,48 @@ Var_Export1(const char *name, int flags)
case '%':
case '*':
case '!':
- return 0;
+ return FALSE;
}
}
+
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 */
- }
+ if (v == NULL)
+ return FALSE;
+
+ if (!parent && (v->flags & VAR_EXPORTED) && !(v->flags & VAR_REEXPORT))
+ return FALSE; /* nothing to do */
+
val = Buf_GetAll(&v->val, NULL);
- if ((flags & VAR_EXPORT_LITERAL) == 0 && strchr(val, '$')) {
+ if (!(flags & VAR_EXPORT_LITERAL) && strchr(val, '$') != NULL) {
+ char *expr;
+
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;
+ v->flags |= VAR_EXPORTED | VAR_REEXPORT;
+ return TRUE;
}
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, VARF_WANTRES);
- setenv(name, val, 1);
- free(val);
+ return FALSE;
}
+
+ expr = str_concat3("${", name, "}");
+ val = Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES);
+ setenv(name, val, 1);
+ free(val);
+ free(expr);
} else {
- if (parent) {
- v->flags &= ~VAR_REEXPORT; /* once will do */
- }
- if (parent || !(v->flags & VAR_EXPORTED)) {
+ if (parent)
+ v->flags &= ~(unsigned)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...
@@ -627,7 +563,7 @@ Var_Export1(const char *name, int flags)
if (parent) {
v->flags |= VAR_EXPORTED;
}
- return 1;
+ return TRUE;
}
static void
@@ -643,9 +579,7 @@ Var_ExportVars_callback(void *entry, void *unused MAKE_ATTR_UNUSED)
void
Var_ExportVars(void)
{
- char tmp[BUFSIZ];
char *val;
- int n;
/*
* Several make's support this sort of mechanism for tracking
@@ -653,57 +587,46 @@ Var_ExportVars(void)
* We allow the makefiles to update MAKELEVEL and ensure
* children see a correctly incremented value.
*/
+ char tmp[BUFSIZ];
snprintf(tmp, sizeof(tmp), "%d", makelevel + 1);
setenv(MAKE_LEVEL_ENV, tmp, 1);
- if (VAR_EXPORTED_NONE == var_exportedVars)
+ if (var_exportedVars == VAR_EXPORTED_NONE)
return;
- if (VAR_EXPORTED_ALL == var_exportedVars) {
+ if (var_exportedVars == VAR_EXPORTED_ALL) {
/* Ouch! This is crazy... */
Hash_ForEach(&VAR_GLOBAL->context, Var_ExportVars_callback, NULL);
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, VARF_WANTRES);
- if (*val) {
- av = brk_string(val, &ac, FALSE, &as);
- for (i = 0; i < ac; i++) {
- Var_Export1(av[i], 0);
- }
- free(as);
- free(av);
- }
- free(val);
+
+ val = Var_Subst("${" MAKE_EXPORTED ":O:u}", VAR_GLOBAL, VARE_WANTRES);
+ if (*val) {
+ Words words = Str_Words(val, FALSE);
+ size_t i;
+
+ for (i = 0; i < words.len; i++)
+ Var_Export1(words.words[i], 0);
+ Words_Free(words);
}
+ free(val);
}
/*
- * This is called when .export is seen or
- * .MAKE.EXPORTED is modified.
- * It is also called when any exported var is modified.
+ * This is called when .export is seen or .MAKE.EXPORTED is modified.
+ *
+ * It is also called when any exported variable is modified.
+ * XXX: Is it really?
+ *
+ * str has the format "[-env|-literal] varname...".
*/
void
-Var_Export(char *str, int isExport)
+Var_Export(const char *str, Boolean isExport)
{
- char *name;
+ VarExportFlags flags;
char *val;
- char **av;
- char *as;
- int flags;
- int ac;
- int i;
- if (isExport && (!str || !str[0])) {
+ if (isExport && str[0] == '\0') {
var_exportedVars = VAR_EXPORTED_ALL; /* use with caution! */
return;
}
@@ -717,64 +640,49 @@ Var_Export(char *str, int isExport)
} else {
flags |= VAR_EXPORT_PARENT;
}
- val = Var_Subst(NULL, str, VAR_GLOBAL, VARF_WANTRES);
- if (*val) {
- 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;
- }
- }
+
+ val = Var_Subst(str, VAR_GLOBAL, VARE_WANTRES);
+ if (val[0] != '\0') {
+ Words words = Str_Words(val, FALSE);
+
+ size_t i;
+ for (i = 0; i < words.len; i++) {
+ const char *name = words.words[i];
if (Var_Export1(name, flags)) {
- if (VAR_EXPORTED_ALL != var_exportedVars)
+ if (var_exportedVars != VAR_EXPORTED_ALL)
var_exportedVars = VAR_EXPORTED_YES;
if (isExport && (flags & VAR_EXPORT_PARENT)) {
Var_Append(MAKE_EXPORTED, name, VAR_GLOBAL);
}
}
}
- free(as);
- free(av);
+ Words_Free(words);
}
free(val);
}
+extern char **environ;
+
/*
* This is called when .unexport[-env] is seen.
+ *
+ * str must have the form "unexport[-env] varname...".
*/
-extern char **environ;
-
void
-Var_UnExport(char *str)
+Var_UnExport(const char *str)
{
- char tmp[BUFSIZ];
- char *vlist;
- char *cp;
+ const char *varnames;
+ char *varnames_freeIt;
Boolean unexport_env;
- int n;
- if (!str || !str[0]) {
- return; /* assert? */
- }
+ varnames = NULL;
+ varnames_freeIt = NULL;
- vlist = NULL;
-
- str += 8;
- unexport_env = (strncmp(str, "-env", 4) == 0);
+ str += strlen("unexport");
+ unexport_env = strncmp(str, "-env", 4) == 0;
if (unexport_env) {
+ const char *cp;
char **newenv;
cp = getenv(MAKE_LEVEL_ENV); /* we should preserve this */
@@ -788,8 +696,7 @@ Var_UnExport(char *str)
}
newenv = bmake_malloc(2 * sizeof(char *));
}
- if (!newenv)
- return;
+
/* Note: we cannot safely free() the original environ. */
environ = savedEnv = newenv;
newenv[0] = NULL;
@@ -797,102 +704,100 @@ Var_UnExport(char *str)
if (cp && *cp)
setenv(MAKE_LEVEL_ENV, cp, 1);
} else {
- for (; *str != '\n' && isspace((unsigned char) *str); str++)
+ for (; isspace((unsigned char)*str); str++)
continue;
- if (str[0] && str[0] != '\n') {
- vlist = str;
- }
+ if (str[0] != '\0')
+ varnames = str;
}
- if (!vlist) {
+ if (varnames == NULL) {
/* Using .MAKE.EXPORTED */
- n = snprintf(tmp, sizeof(tmp), "${" MAKE_EXPORTED ":O:u}");
- if (n < (int)sizeof(tmp)) {
- vlist = Var_Subst(NULL, tmp, VAR_GLOBAL, VARF_WANTRES);
- }
+ varnames = varnames_freeIt = Var_Subst("${" MAKE_EXPORTED ":O:u}",
+ VAR_GLOBAL, VARE_WANTRES);
}
- 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)
+ size_t i;
+
+ Words words = Str_Words(varnames, FALSE);
+ for (i = 0; i < words.len; i++) {
+ const char *varname = words.words[i];
+ v = VarFind(varname, VAR_GLOBAL, 0);
+ if (v == NULL) {
+ VAR_DEBUG("Not unexporting \"%s\" (not found)\n", varname);
continue;
- if (!unexport_env &&
- (v->flags & (VAR_EXPORTED|VAR_REEXPORT)) == VAR_EXPORTED) {
- unsetenv(v->name);
}
- v->flags &= ~(VAR_EXPORTED|VAR_REEXPORT);
+
+ VAR_DEBUG("Unexporting \"%s\"\n", varname);
+ if (!unexport_env && (v->flags & VAR_EXPORTED) &&
+ !(v->flags & VAR_REEXPORT))
+ unsetenv(v->name);
+ v->flags &= ~(unsigned)(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, VARF_WANTRES);
- Var_Set(MAKE_EXPORTED, cp, VAR_GLOBAL);
- free(cp);
- }
+ if (varnames == str) {
+ char *expr = str_concat3("${" MAKE_EXPORTED ":N", v->name, "}");
+ char *cp = Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES);
+ Var_Set(MAKE_EXPORTED, cp, VAR_GLOBAL);
+ free(cp);
+ free(expr);
}
}
- free(as);
- free(av);
- if (vlist != str) {
+ Words_Free(words);
+ if (varnames != str) {
Var_Delete(MAKE_EXPORTED, VAR_GLOBAL);
- free(vlist);
+ free(varnames_freeIt);
}
}
}
-static void
+/* See Var_Set for documentation. */
+void
Var_Set_with_flags(const char *name, const char *val, GNode *ctxt,
VarSet_Flags flags)
{
+ const char *unexpanded_name = name;
+ char *name_freeIt = NULL;
Var *v;
- char *expanded_name = NULL;
+
+ assert(val != 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, VARF_WANTRES);
- 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 (strchr(name, '$') != NULL)
+ name = name_freeIt = Var_Subst(name, ctxt, VARE_WANTRES);
+
+ if (name[0] == '\0') {
+ VAR_DEBUG("Var_Set(\"%s\", \"%s\", ...) "
+ "name expands to empty string - ignored\n",
+ unexpanded_name, val);
+ free(name_freeIt);
+ return;
}
+
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);
- }
+ if (v->flags & VAR_FROM_CMD) {
+ VAR_DEBUG("%s:%s = %s ignored!\n", ctxt->name, name, val);
goto out;
}
VarFreeEnv(v, TRUE);
}
}
+
v = VarFind(name, ctxt, 0);
if (v == NULL) {
- if (ctxt == VAR_CMD && (flags & VAR_NO_EXPORT) == 0) {
+ if (ctxt == VAR_CMD && !(flags & VAR_NO_EXPORT)) {
/*
* This var would normally prevent the same name being added
* to VAR_GLOBAL, so delete it from there if needed.
@@ -900,24 +805,28 @@ Var_Set_with_flags(const char *name, const char *val, GNode *ctxt,
*/
Var_Delete(name, VAR_GLOBAL);
}
- VarAdd(name, val, ctxt);
+ VarAdd(name, val, ctxt, flags);
} else {
+ if ((v->flags & VAR_READONLY) && !(flags & VAR_SET_READONLY)) {
+ VAR_DEBUG("%s:%s = %s ignored (read-only)\n",
+ ctxt->name, name, val);
+ goto out;
+ }
Buf_Empty(&v->val);
if (val)
- Buf_AddBytes(&v->val, strlen(val), val);
+ Buf_AddStr(&v->val, val);
- if (DEBUG(VAR)) {
- fprintf(debug_file, "%s:%s = %s\n", ctxt->name, name, val);
- }
- if ((v->flags & VAR_EXPORTED)) {
+ VAR_DEBUG("%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)
+ * Other than internals.
*/
- if (ctxt == VAR_CMD && (flags & VAR_NO_EXPORT) == 0) {
+ if (ctxt == VAR_CMD && !(flags & VAR_NO_EXPORT) && name[0] != '.') {
if (v == NULL) {
/* we just added it */
v = VarFind(name, ctxt, 0);
@@ -930,18 +839,16 @@ Var_Set_with_flags(const char *name, const char *val, GNode *ctxt,
* that the command-line settings continue to override
* Makefile settings.
*/
- if (varNoExportEnv != TRUE)
+ if (!varNoExportEnv)
setenv(name, val ? val : "", 1);
Var_Append(MAKEOVERRIDES, name, VAR_GLOBAL);
}
- if (*name == '.') {
- if (strcmp(name, SAVE_DOLLARS) == 0)
- save_dollars = s2Boolean(val, save_dollars);
- }
+ if (name[0] == '.' && strcmp(name, SAVE_DOLLARS) == 0)
+ save_dollars = s2Boolean(val, save_dollars);
out:
- free(expanded_name);
+ free(name_freeIt);
if (v != NULL)
VarFreeEnv(v, TRUE);
}
@@ -951,15 +858,14 @@ out:
* Var_Set --
* Set the variable name to the value val in the given context.
*
+ * If the variable doesn't yet exist, it is created.
+ * Otherwise the new value overwrites and replaces the old value.
+ *
* Input:
* name name of variable to set
* val value to give to the variable
* ctxt context in which to set it
*
- * 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,
@@ -983,14 +889,13 @@ Var_Set(const char *name, const char *val, GNode *ctxt)
* The variable of the given name has the given value appended to it in
* the given context.
*
+ * If the variable doesn't exist, it is created. Otherwise the strings
+ * are concatenated, with a space in between.
+ *
* Input:
* name name of variable to modify
- * val String to append to it
- * ctxt Context in which this should occur
- *
- * Side Effects:
- * If the variable doesn't exist, it is created. Else the strings
- * are concatenated (with a space in between).
+ * val string to append to it
+ * ctxt context in which this should occur
*
* Notes:
* Only if the variable is being sought in the global context is the
@@ -1004,83 +909,71 @@ Var_Set(const char *name, const char *val, GNode *ctxt)
void
Var_Append(const char *name, const char *val, GNode *ctxt)
{
+ char *name_freeIt = NULL;
Var *v;
- Hash_Entry *h;
- char *expanded_name = NULL;
+
+ assert(val != NULL);
if (strchr(name, '$') != NULL) {
- expanded_name = Var_Subst(NULL, name, ctxt, VARF_WANTRES);
- 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);
+ const char *unexpanded_name = name;
+ name = name_freeIt = Var_Subst(name, ctxt, VARE_WANTRES);
+ if (name[0] == '\0') {
+ VAR_DEBUG("Var_Append(\"%s\", \"%s\", ...) "
+ "name expands to empty string - ignored\n",
+ unexpanded_name, val);
+ free(name_freeIt);
return;
}
- name = expanded_name;
}
- v = VarFind(name, ctxt, ctxt == VAR_GLOBAL ? (FIND_CMD|FIND_ENV) : 0);
+ v = VarFind(name, ctxt, ctxt == VAR_GLOBAL ? (FIND_CMD | FIND_ENV) : 0);
if (v == NULL) {
Var_Set(name, val, ctxt);
} else if (ctxt == VAR_CMD || !(v->flags & VAR_FROM_CMD)) {
Buf_AddByte(&v->val, ' ');
- Buf_AddBytes(&v->val, strlen(val), val);
+ Buf_AddStr(&v->val, val);
- if (DEBUG(VAR)) {
- fprintf(debug_file, "%s:%s = %s\n", ctxt->name, name,
- Buf_GetAll(&v->val, NULL));
- }
+ VAR_DEBUG("%s:%s = %s\n", ctxt->name, name,
+ Buf_GetAll(&v->val, NULL));
if (v->flags & VAR_FROM_ENV) {
+ Hash_Entry *h;
+
/*
* 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;
+ v->flags &= ~(unsigned)VAR_FROM_ENV;
h = Hash_CreateEntry(&ctxt->context, name, NULL);
Hash_SetValue(h, v);
}
}
- free(expanded_name);
+ free(name_freeIt);
}
-/*-
- *-----------------------------------------------------------------------
- * Var_Exists --
- * See if the given variable exists.
+/* See if the given variable exists, in the given context or in other
+ * fallback contexts.
*
* 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;
+ char *name_freeIt = NULL;
+ Var *v;
- if ((cp = strchr(name, '$')) != NULL) {
- cp = Var_Subst(NULL, name, ctxt, VARF_WANTRES);
- }
- v = VarFind(cp ? cp : name, ctxt, FIND_CMD|FIND_GLOBAL|FIND_ENV);
- free(cp);
- if (v == NULL) {
+ if (strchr(name, '$') != NULL)
+ name = name_freeIt = Var_Subst(name, ctxt, VARE_WANTRES);
+
+ v = VarFind(name, ctxt, FIND_CMD | FIND_GLOBAL | FIND_ENV);
+ free(name_freeIt);
+ if (v == NULL)
return FALSE;
- }
(void)VarFreeEnv(v, TRUE);
return TRUE;
@@ -1089,614 +982,552 @@ Var_Exists(const char *name, GNode *ctxt)
/*-
*-----------------------------------------------------------------------
* Var_Value --
- * Return the value of the named variable in the given context
+ * Return the unexpanded value of the given variable in the given
+ * context, or the usual contexts.
*
* 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
+ * The value if the variable exists, NULL if it doesn't.
+ * If the returned value is not NULL, the caller must free *freeIt
+ * as soon as the returned value is no longer needed.
*-----------------------------------------------------------------------
*/
-char *
-Var_Value(const char *name, GNode *ctxt, char **frp)
+const char *
+Var_Value(const char *name, GNode *ctxt, char **freeIt)
{
- Var *v;
+ Var *v = VarFind(name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD);
+ char *p;
- v = VarFind(name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD);
- *frp = NULL;
+ *freeIt = NULL;
if (v == NULL)
return NULL;
- char *p = (Buf_GetAll(&v->val, NULL));
+ p = Buf_GetAll(&v->val, NULL);
if (VarFreeEnv(v, FALSE))
- *frp = p;
+ *freeIt = p;
return p;
}
-/* This callback for VarModify gets a single word from an expression and
+/* SepBuf is a string being built from "words", interleaved with separators. */
+typedef struct {
+ Buffer buf;
+ Boolean needSep;
+ char sep; /* usually ' ', but see the :ts modifier */
+} SepBuf;
+
+static void
+SepBuf_Init(SepBuf *buf, char sep)
+{
+ Buf_Init(&buf->buf, 32 /* bytes */);
+ buf->needSep = FALSE;
+ buf->sep = sep;
+}
+
+static void
+SepBuf_Sep(SepBuf *buf)
+{
+ buf->needSep = TRUE;
+}
+
+static void
+SepBuf_AddBytes(SepBuf *buf, const char *mem, size_t mem_size)
+{
+ if (mem_size == 0)
+ return;
+ if (buf->needSep && buf->sep != '\0') {
+ Buf_AddByte(&buf->buf, buf->sep);
+ buf->needSep = FALSE;
+ }
+ Buf_AddBytes(&buf->buf, mem, mem_size);
+}
+
+static void
+SepBuf_AddBytesBetween(SepBuf *buf, const char *start, const char *end)
+{
+ SepBuf_AddBytes(buf, start, (size_t)(end - start));
+}
+
+static void
+SepBuf_AddStr(SepBuf *buf, const char *str)
+{
+ SepBuf_AddBytes(buf, str, strlen(str));
+}
+
+static char *
+SepBuf_Destroy(SepBuf *buf, Boolean free_buf)
+{
+ return Buf_Destroy(&buf->buf, free_buf);
+}
+
+
+/* This callback for ModifyWords gets a single word from an expression and
* typically adds a modification of this word to the buffer. It may also do
- * nothing or add several words.
- *
- * If addSpaces is TRUE, it must add a space before adding anything else to
- * the buffer.
- *
- * It returns the addSpace value for the next call of this callback. Typical
- * return values are the current addSpaces or TRUE. */
-typedef Boolean (*VarModifyCallback)(GNode *ctxt, Var_Parse_State *vpstate,
- const char *word, Boolean addSpace, Buffer *buf, void *data);
+ * nothing or add several words. */
+typedef void (*ModifyWordsCallback)(const char *word, SepBuf *buf, void *data);
-/* Callback function for VarModify to implement the :H modifier.
+/* Callback for ModifyWords to implement the :H modifier.
* Add the dirname of the given word to the buffer. */
-static Boolean
-VarHead(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate,
- const char *word, Boolean addSpace, Buffer *buf,
- void *dummy MAKE_ATTR_UNUSED)
+static void
+ModifyWord_Head(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
{
const char *slash = strrchr(word, '/');
-
- if (addSpace && vpstate->varSpace)
- Buf_AddByte(buf, vpstate->varSpace);
if (slash != NULL)
- Buf_AddBytes(buf, slash - word, word);
+ SepBuf_AddBytesBetween(buf, word, slash);
else
- Buf_AddByte(buf, '.');
-
- return TRUE;
+ SepBuf_AddStr(buf, ".");
}
-/* Callback function for VarModify to implement the :T modifier.
+/* Callback for ModifyWords to implement the :T modifier.
* Add the basename of the given word to the buffer. */
-static Boolean
-VarTail(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate,
- const char *word, Boolean addSpace, Buffer *buf,
- void *dummy MAKE_ATTR_UNUSED)
+static void
+ModifyWord_Tail(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
{
const char *slash = strrchr(word, '/');
const char *base = slash != NULL ? slash + 1 : word;
-
- if (addSpace && vpstate->varSpace)
- Buf_AddByte(buf, vpstate->varSpace);
- Buf_AddBytes(buf, strlen(base), base);
- return TRUE;
+ SepBuf_AddStr(buf, base);
}
-/* Callback function for VarModify to implement the :E modifier.
+/* Callback for ModifyWords to implement the :E modifier.
* Add the filename suffix of the given word to the buffer, if it exists. */
-static Boolean
-VarSuffix(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate,
- const char *word, Boolean addSpace, Buffer *buf,
- void *dummy MAKE_ATTR_UNUSED)
+static void
+ModifyWord_Suffix(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
{
const char *dot = strrchr(word, '.');
- if (dot == NULL)
- return addSpace;
-
- if (addSpace && vpstate->varSpace)
- Buf_AddByte(buf, vpstate->varSpace);
- Buf_AddBytes(buf, strlen(dot + 1), dot + 1);
- return TRUE;
+ if (dot != NULL)
+ SepBuf_AddStr(buf, dot + 1);
}
-/* Callback function for VarModify to implement the :R modifier.
- * Add the filename basename of the given word to the buffer. */
-static Boolean
-VarRoot(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate,
- const char *word, Boolean addSpace, Buffer *buf,
- void *dummy MAKE_ATTR_UNUSED)
+/* Callback for ModifyWords to implement the :R modifier.
+ * Add the basename of the given word to the buffer. */
+static void
+ModifyWord_Root(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
{
- char *dot = strrchr(word, '.');
+ const char *dot = strrchr(word, '.');
size_t len = dot != NULL ? (size_t)(dot - word) : strlen(word);
-
- if (addSpace && vpstate->varSpace)
- Buf_AddByte(buf, vpstate->varSpace);
- Buf_AddBytes(buf, len, word);
- return TRUE;
+ SepBuf_AddBytes(buf, word, len);
}
-/* Callback function for VarModify to implement the :M modifier.
+/* Callback for ModifyWords to implement the :M modifier.
* Place the word in the buffer if it matches the given pattern. */
-static Boolean
-VarMatch(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate,
- const char *word, Boolean addSpace, Buffer *buf,
- void *data)
+static void
+ModifyWord_Match(const char *word, SepBuf *buf, void *data)
+{
+ const char *pattern = data;
+ VAR_DEBUG("VarMatch [%s] [%s]\n", word, pattern);
+ if (Str_Match(word, pattern))
+ SepBuf_AddStr(buf, word);
+}
+
+/* Callback for ModifyWords to implement the :N modifier.
+ * Place the word in the buffer if it doesn't match the given pattern. */
+static void
+ModifyWord_NoMatch(const char *word, SepBuf *buf, void *data)
{
const char *pattern = data;
- if (DEBUG(VAR))
- fprintf(debug_file, "VarMatch [%s] [%s]\n", word, pattern);
if (!Str_Match(word, pattern))
- return addSpace;
- if (addSpace && vpstate->varSpace)
- Buf_AddByte(buf, vpstate->varSpace);
- Buf_AddBytes(buf, strlen(word), word);
- return TRUE;
+ SepBuf_AddStr(buf, word);
}
#ifdef SYSVVARSUB
-/* Callback function for VarModify to implement the :%.from=%.to modifier. */
-static Boolean
-VarSYSVMatch(GNode *ctx, Var_Parse_State *vpstate,
- const char *word, Boolean addSpace, Buffer *buf,
- void *data)
+/*-
+ *-----------------------------------------------------------------------
+ * Str_SYSVMatch --
+ * Check word against pattern for a match (% is wild),
+ *
+ * Input:
+ * word Word to examine
+ * pattern Pattern to examine against
+ *
+ * Results:
+ * Returns the start of the match, or NULL.
+ * *match_len returns the length of the match, if any.
+ * *hasPercent returns whether the pattern contains a percent.
+ *-----------------------------------------------------------------------
+ */
+static const char *
+Str_SYSVMatch(const char *word, const char *pattern, size_t *match_len,
+ Boolean *hasPercent)
{
- size_t len;
- char *ptr;
- Boolean hasPercent;
- VarPattern *pat = data;
-
- if (addSpace && vpstate->varSpace)
- Buf_AddByte(buf, vpstate->varSpace);
+ const char *p = pattern;
+ const char *w = word;
+ const char *percent;
+ size_t w_len;
+ size_t p_len;
+ const char *w_tail;
+
+ *hasPercent = FALSE;
+ if (*p == '\0') { /* ${VAR:=suffix} */
+ *match_len = strlen(w); /* Null pattern is the whole string */
+ return w;
+ }
+
+ percent = strchr(p, '%');
+ if (percent != NULL) { /* ${VAR:...%...=...} */
+ *hasPercent = TRUE;
+ if (*w == '\0')
+ return NULL; /* empty word does not match pattern */
+
+ /* check that the prefix matches */
+ for (; p != percent && *w != '\0' && *w == *p; w++, p++)
+ continue;
+ if (p != percent)
+ return NULL; /* No match */
- if ((ptr = Str_SYSVMatch(word, pat->lhs, &len, &hasPercent)) != NULL) {
- char *varexp = Var_Subst(NULL, pat->rhs, ctx, VARF_WANTRES);
- Str_SYSVSubst(buf, varexp, ptr, len, hasPercent);
- free(varexp);
- } else {
- Buf_AddBytes(buf, strlen(word), word);
+ p++; /* Skip the percent */
+ if (*p == '\0') {
+ /* No more pattern, return the rest of the string */
+ *match_len = strlen(w);
+ return w;
+ }
}
- return TRUE;
+ /* Test whether the tail matches */
+ w_len = strlen(w);
+ p_len = strlen(p);
+ if (w_len < p_len)
+ return NULL;
+
+ w_tail = w + w_len - p_len;
+ if (memcmp(p, w_tail, p_len) != 0)
+ return NULL;
+
+ *match_len = (size_t)(w_tail - w);
+ return w;
}
-#endif
-/* Callback function for VarModify to implement the :N modifier.
- * Place the word in the buffer if it doesn't match the given pattern. */
-static Boolean
-VarNoMatch(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate,
- const char *word, Boolean addSpace, Buffer *buf,
- void *data)
+typedef struct {
+ GNode *ctx;
+ const char *lhs;
+ const char *rhs;
+} ModifyWord_SYSVSubstArgs;
+
+/* Callback for ModifyWords to implement the :%.from=%.to modifier. */
+static void
+ModifyWord_SYSVSubst(const char *word, SepBuf *buf, void *data)
{
- const char *pattern = data;
- if (Str_Match(word, pattern))
- return addSpace;
- if (addSpace && vpstate->varSpace)
- Buf_AddByte(buf, vpstate->varSpace);
- Buf_AddBytes(buf, strlen(word), word);
- return TRUE;
+ const ModifyWord_SYSVSubstArgs *args = data;
+ char *rhs_expanded;
+ const char *rhs;
+ const char *percent;
+
+ size_t match_len;
+ Boolean lhsPercent;
+ const char *match = Str_SYSVMatch(word, args->lhs, &match_len, &lhsPercent);
+ if (match == NULL) {
+ SepBuf_AddStr(buf, word);
+ return;
+ }
+
+ /* Append rhs to the buffer, substituting the first '%' with the
+ * match, but only if the lhs had a '%' as well. */
+
+ rhs_expanded = Var_Subst(args->rhs, args->ctx, VARE_WANTRES);
+
+ rhs = rhs_expanded;
+ percent = strchr(rhs, '%');
+
+ if (percent != NULL && lhsPercent) {
+ /* Copy the prefix of the replacement pattern */
+ SepBuf_AddBytesBetween(buf, rhs, percent);
+ rhs = percent + 1;
+ }
+ if (percent != NULL || !lhsPercent)
+ SepBuf_AddBytes(buf, match, match_len);
+
+ /* Append the suffix of the replacement pattern */
+ SepBuf_AddStr(buf, rhs);
+
+ free(rhs_expanded);
}
+#endif
+
-/* Callback function for VarModify to implement the :S,from,to, modifier.
+typedef struct {
+ const char *lhs;
+ size_t lhsLen;
+ const char *rhs;
+ size_t rhsLen;
+ VarPatternFlags pflags;
+ Boolean matched;
+} ModifyWord_SubstArgs;
+
+/* Callback for ModifyWords to implement the :S,from,to, modifier.
* Perform a string substitution on the given word. */
-static Boolean
-VarSubstitute(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate,
- const char *word, Boolean addSpace, Buffer *buf,
- void *data)
+static void
+ModifyWord_Subst(const char *word, SepBuf *buf, void *data)
{
- int wordLen = strlen(word);
- const char *cp; /* General pointer */
- VarPattern *pattern = data;
+ size_t wordLen = strlen(word);
+ ModifyWord_SubstArgs *args = data;
+ const char *match;
- 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.
- */
+ if ((args->pflags & VARP_SUB_ONE) && args->matched)
+ goto nosub;
+
+ if (args->pflags & VARP_ANCHOR_START) {
+ if (wordLen < args->lhsLen ||
+ memcmp(word, args->lhs, args->lhsLen) != 0)
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.
- */
+
+ if (args->pflags & VARP_ANCHOR_END) {
+ if (wordLen != args->lhsLen)
goto nosub;
- }
+
+ /* :S,^whole$,replacement, */
+ SepBuf_AddBytes(buf, args->rhs, args->rhsLen);
+ args->matched = TRUE;
} 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;
+ /* :S,^prefix,replacement, */
+ SepBuf_AddBytes(buf, args->rhs, args->rhsLen);
+ SepBuf_AddBytes(buf, word + args->lhsLen, wordLen - args->lhsLen);
+ args->matched = TRUE;
}
- return addSpace;
+ return;
}
-nosub:
- if (addSpace && vpstate->varSpace) {
- Buf_AddByte(buf, vpstate->varSpace);
+
+ if (args->pflags & VARP_ANCHOR_END) {
+ const char *start;
+
+ if (wordLen < args->lhsLen)
+ goto nosub;
+
+ start = word + (wordLen - args->lhsLen);
+ if (memcmp(start, args->lhs, args->lhsLen) != 0)
+ goto nosub;
+
+ /* :S,suffix$,replacement, */
+ SepBuf_AddBytesBetween(buf, word, start);
+ SepBuf_AddBytes(buf, args->rhs, args->rhsLen);
+ args->matched = TRUE;
+ return;
}
- Buf_AddBytes(buf, wordLen, word);
- return TRUE;
+
+ /* unanchored case, may match more than once */
+ while ((match = Str_FindSubstring(word, args->lhs)) != NULL) {
+ SepBuf_AddBytesBetween(buf, word, match);
+ SepBuf_AddBytes(buf, args->rhs, args->rhsLen);
+ args->matched = TRUE;
+ wordLen -= (size_t)(match - word) + args->lhsLen;
+ word += (size_t)(match - word) + args->lhsLen;
+ if (wordLen == 0 || !(args->pflags & VARP_SUB_GLOBAL))
+ break;
+ }
+nosub:
+ SepBuf_AddBytes(buf, word, wordLen);
}
#ifndef NO_REGEX
-/*-
- *-----------------------------------------------------------------------
- * VarREError --
- * Print the error caused by a regcomp or regexec call.
- *
- * Side Effects:
- * An error gets printed.
- *
- *-----------------------------------------------------------------------
- */
+/* Print the error caused by a regcomp or regexec call. */
static void
VarREError(int reerr, regex_t *pat, const char *str)
{
- char *errbuf;
- int errlen;
-
- errlen = regerror(reerr, pat, 0, 0);
- errbuf = bmake_malloc(errlen);
+ size_t errlen = regerror(reerr, pat, 0, 0);
+ char *errbuf = bmake_malloc(errlen);
regerror(reerr, pat, errbuf, errlen);
Error("%s: %s", str, errbuf);
free(errbuf);
}
-/* Callback function for VarModify to implement the :C/from/to/ modifier.
+typedef struct {
+ regex_t re;
+ size_t nsub;
+ char *replace;
+ VarPatternFlags pflags;
+ Boolean matched;
+} ModifyWord_SubstRegexArgs;
+
+/* Callback for ModifyWords to implement the :C/from/to/ modifier.
* Perform a regex substitution on the given word. */
-static Boolean
-VarRESubstitute(GNode *ctx MAKE_ATTR_UNUSED,
- Var_Parse_State *vpstate MAKE_ATTR_UNUSED,
- const char *word, Boolean addSpace, Buffer *buf,
- void *data)
+static void
+ModifyWord_SubstRegex(const char *word, SepBuf *buf, void *data)
{
- VarREPattern *pat = data;
+ ModifyWord_SubstRegexArgs *args = data;
int xrv;
const char *wp = word;
char *rp;
- int added = 0;
int flags = 0;
+ regmatch_t m[10];
-#define MAYBE_ADD_SPACE() \
- if (addSpace && !added) \
- Buf_AddByte(buf, ' '); \
- added = 1
-
- 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);
- }
+ if ((args->pflags & VARP_SUB_ONE) && args->matched)
+ goto nosub;
+
+tryagain:
+ xrv = regexec(&args->re, wp, args->nsub, m, 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);
- }
+ args->matched = TRUE;
+ SepBuf_AddBytes(buf, wp, (size_t)m[0].rm_so);
- for (rp = pat->replace; *rp; rp++) {
- if ((*rp == '\\') && ((rp[1] == '&') || (rp[1] == '\\'))) {
- MAYBE_ADD_SPACE();
- Buf_AddByte(buf, rp[1]);
+ for (rp = args->replace; *rp; rp++) {
+ if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) {
+ SepBuf_AddBytes(buf, rp + 1, 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++;
- }
+ continue;
+ }
- 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 (*rp == '&') {
+ SepBuf_AddBytesBetween(buf, wp + m[0].rm_so, wp + m[0].rm_eo);
+ continue;
+ }
+
+ if (*rp != '\\' || !isdigit((unsigned char)rp[1])) {
+ SepBuf_AddBytes(buf, rp, 1);
+ continue;
+ }
+
+ { /* \0 to \9 backreference */
+ size_t n = (size_t)(rp[1] - '0');
+ rp++;
- if (sublen > 0) {
- MAYBE_ADD_SPACE();
- Buf_AddBytes(buf, sublen, subbuf);
+ if (n >= args->nsub) {
+ Error("No subexpression \\%zu", n);
+ } else if (m[n].rm_so == -1 && m[n].rm_eo == -1) {
+ Error("No match for subexpression \\%zu", n);
+ } else {
+ SepBuf_AddBytesBetween(buf, wp + m[n].rm_so,
+ wp + m[n].rm_eo);
}
- } else {
- MAYBE_ADD_SPACE();
- Buf_AddByte(buf, *rp);
}
}
- wp += pat->matches[0].rm_eo;
- if (pat->flags & VAR_SUB_GLOBAL) {
+
+ wp += m[0].rm_eo;
+ if (args->pflags & VARP_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);
+ if (m[0].rm_so == 0 && m[0].rm_eo == 0) {
+ SepBuf_AddBytes(buf, wp, 1);
wp++;
-
}
if (*wp)
goto tryagain;
}
if (*wp) {
- MAYBE_ADD_SPACE();
- Buf_AddBytes(buf, strlen(wp), wp);
+ SepBuf_AddStr(buf, wp);
}
break;
default:
- VarREError(xrv, &pat->re, "Unexpected regex error");
+ VarREError(xrv, &args->re, "Unexpected regex error");
/* fall through */
case REG_NOMATCH:
- if (*wp) {
- MAYBE_ADD_SPACE();
- Buf_AddBytes(buf, strlen(wp), wp);
- }
+ nosub:
+ SepBuf_AddStr(buf, wp);
break;
}
- return addSpace || added;
}
#endif
-/* Callback function for VarModify to implement the :@var@...@ modifier of
- * ODE make. We set the temp variable named in pattern.lhs to word and
- * expand pattern.rhs. */
-static Boolean
-VarLoopExpand(GNode *ctx MAKE_ATTR_UNUSED,
- Var_Parse_State *vpstate MAKE_ATTR_UNUSED,
- const char *word, Boolean addSpace, Buffer *buf,
- void *data)
+typedef struct {
+ GNode *ctx;
+ char *tvar; /* name of temporary variable */
+ char *str; /* string to expand */
+ VarEvalFlags eflags;
+} ModifyWord_LoopArgs;
+
+/* Callback for ModifyWords to implement the :@var@...@ modifier of ODE make. */
+static void
+ModifyWord_Loop(const char *word, SepBuf *buf, void *data)
{
- VarLoop *loop = data;
+ const ModifyWord_LoopArgs *args;
char *s;
- int slen;
- if (*word) {
- Var_Set_with_flags(loop->tvar, word, loop->ctxt, VAR_NO_EXPORT);
- s = Var_Subst(NULL, loop->str, loop->ctxt, loop->flags);
- if (DEBUG(VAR)) {
- fprintf(debug_file,
- "VarLoopExpand: in \"%s\", replace \"%s\" with \"%s\" "
- "to \"%s\"\n",
- word, loop->tvar, loop->str, s ? s : "(null)");
- }
- 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;
+ if (word[0] == '\0')
+ return;
+
+ args = data;
+ Var_Set_with_flags(args->tvar, word, args->ctx, VAR_NO_EXPORT);
+ s = Var_Subst(args->str, args->ctx, args->eflags);
+
+ VAR_DEBUG("ModifyWord_Loop: in \"%s\", replace \"%s\" with \"%s\" "
+ "to \"%s\"\n",
+ word, args->tvar, args->str, s);
+
+ if (s[0] == '\n' || (buf->buf.count > 0 &&
+ buf->buf.buffer[buf->buf.count - 1] == '\n'))
+ buf->needSep = FALSE;
+ SepBuf_AddStr(buf, s);
+ free(s);
}
/*-
- *-----------------------------------------------------------------------
- * 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.
- *
- *-----------------------------------------------------------------------
+ * Implements the :[first..last] modifier.
+ * This is a special case of ModifyWords since we want to be able
+ * to scan the list backwards if first > last.
*/
static char *
-VarSelectWords(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate,
- const char *str, VarSelectWords_t *seldata)
+VarSelectWords(char sep, Boolean oneBigWord, const char *str, int first,
+ int last)
{
- 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;
+ Words words;
int start, end, step;
+ int 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;
+ SepBuf buf;
+ SepBuf_Init(&buf, sep);
+
+ if (oneBigWord) {
+ /* fake what Str_Words() would do if there were only one word */
+ words.len = 1;
+ words.words = bmake_malloc((words.len + 1) * sizeof(char *));
+ words.freeIt = bmake_strdup(str);
+ words.words[0] = words.freeIt;
+ words.words[1] = NULL;
} else {
- av = brk_string(str, &ac, FALSE, &as);
+ words = Str_Words(str, FALSE);
}
/*
- * 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.).
+ * Now sanitize the given range.
+ * If first or last are negative, convert them to the positive equivalents
+ * (-1 gets converted to ac, -2 gets converted to (ac - 1), etc.).
*/
- if (seldata->start < 0)
- seldata->start = ac + seldata->start + 1;
- if (seldata->end < 0)
- seldata->end = ac + seldata->end + 1;
+ if (first < 0)
+ first += (int)words.len + 1;
+ if (last < 0)
+ last += (int)words.len + 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);
+ if (first > last) {
+ start = MIN((int)words.len, first) - 1;
+ end = MAX(0, last - 1);
step = -1;
} else {
- start = MAX(0, seldata->start - 1);
- end = MIN(ac, seldata->end);
+ start = MAX(0, first - 1);
+ end = MIN((int)words.len, last);
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;
- }
+ for (i = start; (step < 0) == (i >= end); i += step) {
+ SepBuf_AddStr(&buf, words.words[i]);
+ SepBuf_Sep(&buf);
}
- free(as);
- free(av);
+ Words_Free(words);
- return Buf_Destroy(&buf, FALSE);
+ return SepBuf_Destroy(&buf, FALSE);
}
-/* Callback function for VarModify to implement the :tA modifier.
+/* Callback for ModifyWords to implement the :tA modifier.
* Replace each word with the result of realpath() if successful. */
-static Boolean
-VarRealpath(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate,
- const char *word, Boolean addSpace, Buffer *buf,
- void *patternp MAKE_ATTR_UNUSED)
+static void
+ModifyWord_Realpath(const char *word, SepBuf *buf, void *data MAKE_ATTR_UNUSED)
{
struct stat st;
char rbuf[MAXPATHLEN];
- char *rp;
- if (addSpace && vpstate->varSpace)
- Buf_AddByte(buf, vpstate->varSpace);
- rp = cached_realpath(word, rbuf);
- if (rp && *rp == '/' && stat(rp, &st) == 0)
+ const char *rp = cached_realpath(word, rbuf);
+ if (rp != NULL && *rp == '/' && stat(rp, &st) == 0)
word = rp;
- Buf_AddBytes(buf, strlen(word), word);
- return TRUE;
+ SepBuf_AddStr(buf, word);
}
/*-
@@ -1704,273 +1535,116 @@ VarRealpath(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate,
* Modify each of the words of the passed string using the given function.
*
* Input:
- * str String whose words should be trimmed
- * modProc Function to use to modify them
- * data Custom data for the modProc
+ * str String whose words should be modified
+ * modifyWord Function that modifies a single word
+ * modifyWord_args Custom arguments for modifyWord
*
* Results:
* A string of all the words modified appropriately.
- *
- * Side Effects:
- * None.
- *
*-----------------------------------------------------------------------
*/
static char *
-VarModify(GNode *ctx, Var_Parse_State *vpstate,
- const char *str, VarModifyCallback modProc, void *datum)
+ModifyWords(GNode *ctx, char sep, Boolean oneBigWord, const char *str,
+ ModifyWordsCallback modifyWord, void *modifyWord_args)
{
- 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);
- }
+ SepBuf result;
+ Words words;
+ size_t i;
- if (DEBUG(VAR)) {
- fprintf(debug_file, "VarModify: split \"%s\" into %d words\n",
- str, ac);
+ if (oneBigWord) {
+ SepBuf_Init(&result, sep);
+ modifyWord(str, &result, modifyWord_args);
+ return SepBuf_Destroy(&result, FALSE);
}
- for (i = 0; i < ac; i++)
- addSpace = modProc(ctx, vpstate, av[i], addSpace, &buf, datum);
-
- free(as);
- free(av);
+ SepBuf_Init(&result, sep);
- return Buf_Destroy(&buf, FALSE);
-}
+ words = Str_Words(str, FALSE);
+ VAR_DEBUG("ModifyWords: split \"%s\" into %zu words\n", str, words.len);
-static int
-VarWordCompare(const void *a, const void *b)
-{
- int r = strcmp(*(const char * const *)a, *(const char * const *)b);
- return r;
-}
-
-static int
-VarWordCompareReverse(const void *a, const void *b)
-{
- int r = strcmp(*(const char * const *)b, *(const char * const *)a);
- 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 'r': /* reverse sort alphabetically */
- qsort(av, ac, sizeof(char *), VarWordCompareReverse);
- break;
- case 's': /* sort alphabetically */
- qsort(av, ac, sizeof(char *), VarWordCompare);
- break;
- case 'x': /* randomize */
- {
- /*
- * 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--) {
- int rndidx = random() % (i + 1);
- char *t = av[i];
- av[i] = av[rndidx];
- av[rndidx] = t;
- }
- }
- }
+ for (i = 0; i < words.len; i++) {
+ modifyWord(words.words[i], &result, modifyWord_args);
+ if (result.buf.count > 0)
+ SepBuf_Sep(&result);
}
- for (i = 0; i < ac; i++) {
- Buf_AddBytes(&buf, strlen(av[i]), av[i]);
- if (i != ac - 1)
- Buf_AddByte(&buf, ' ');
- }
+ Words_Free(words);
- free(as);
- free(av);
-
- return Buf_Destroy(&buf, FALSE);
+ return SepBuf_Destroy(&result, 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)
+Words_JoinFree(Words words)
{
- Buffer buf; /* Buffer for new string */
- char **av; /* List of words to affect */
- char *as; /* Word list memory */
- int ac, i, j;
+ Buffer buf;
+ size_t i;
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, ' ');
+ for (i = 0; i < words.len; i++) {
+ if (i != 0)
+ Buf_AddByte(&buf, ' '); /* XXX: st->sep, for consistency */
+ Buf_AddStr(&buf, words.words[i]);
}
- free(as);
- free(av);
+ Words_Free(words);
return Buf_Destroy(&buf, FALSE);
}
-/*-
- *-----------------------------------------------------------------------
- * VarRange --
- * Return an integer sequence
- *
- * Input:
- * str String whose words provide default range
- * ac range length, if 0 use str words
- *
- * Side Effects:
- * None.
- *
- *-----------------------------------------------------------------------
- */
+/* Remove adjacent duplicate words. */
static char *
-VarRange(const char *str, int ac)
+VarUniq(const char *str)
{
- Buffer buf; /* Buffer for new string */
- char tmp[32]; /* each element */
- char **av; /* List of words to affect */
- char *as; /* Word list memory */
- int i, n;
+ Words words = Str_Words(str, FALSE);
- Buf_Init(&buf, 0);
- if (ac > 0) {
- as = NULL;
- av = NULL;
- } else {
- av = brk_string(str, &ac, FALSE, &as);
+ if (words.len > 1) {
+ size_t i, j;
+ for (j = 0, i = 1; i < words.len; i++)
+ if (strcmp(words.words[i], words.words[j]) != 0 && (++j != i))
+ words.words[j] = words.words[i];
+ words.len = j + 1;
}
- for (i = 0; i < ac; i++) {
- n = snprintf(tmp, sizeof(tmp), "%d", 1 + i);
- if (n >= (int)sizeof(tmp))
- break;
- Buf_AddBytes(&buf, n, tmp);
- if (i != ac - 1)
- Buf_AddByte(&buf, ' ');
- }
-
- free(as);
- free(av);
- return Buf_Destroy(&buf, FALSE);
+ return Words_JoinFree(words);
}
/*-
- *-----------------------------------------------------------------------
- * VarGetPattern --
- * During the parsing of a part of a modifier such as :S or :@,
- * 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.
- *-----------------------------------------------------------------------
+ * Parse a part of a modifier such as the "from" and "to" in :S/from/to/
+ * or the "var" or "replacement" in :@var@replacement+${var}@, up to and
+ * including the next unescaped delimiter. The delimiter, as well as the
+ * backslash or the dollar, can be escaped with a backslash.
+ *
+ * Return the parsed (and possibly expanded) string, or NULL if no delimiter
+ * was found. On successful return, the parsing position pp points right
+ * after the delimiter. The delimiter is not included in the returned
+ * value though.
*/
static char *
-VarGetPattern(GNode *ctxt, Var_Parse_State *vpstate MAKE_ATTR_UNUSED,
- VarPattern_Flags flags, const char **tstr, int delim,
- VarPattern_Flags *vflags, int *length, VarPattern *pattern)
-{
- const char *cp;
- char *rstr;
+ParseModifierPart(
+ const char **pp, /* The parsing position, updated upon return */
+ int delim, /* Parsing stops at this delimiter */
+ VarEvalFlags eflags, /* Flags for evaluating nested variables;
+ * if VARE_WANTRES is not set, the text is
+ * only parsed */
+ GNode *ctxt, /* For looking up nested variables */
+ size_t *out_length, /* Optionally stores the length of the returned
+ * string, just to save another strlen call. */
+ VarPatternFlags *out_pflags,/* For the first part of the :S modifier,
+ * sets the VARP_ANCHOR_END flag if the last
+ * character of the pattern is a $. */
+ ModifyWord_SubstArgs *subst /* For the second part of the :S modifier,
+ * allow ampersands to be escaped and replace
+ * unescaped ampersands with subst->lhs. */
+) {
Buffer buf;
- int junk;
- int errnum = flags & VARF_UNDEFERR;
+ const char *p;
+ char *rstr;
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;
@@ -1978,162 +1652,147 @@ VarGetPattern(GNode *ctxt, Var_Parse_State *vpstate MAKE_ATTR_UNUSED,
* 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 (vflags == NULL)
- Buf_AddByte(&buf, *cp);
- else
- /*
- * Unescaped $ at end of pattern => anchor
- * pattern at end.
- */
- *vflags |= VAR_MATCH_END;
- } else {
- if (vflags == NULL || (*vflags & VAR_NOSUBST) == 0) {
- char *cp2;
- int len;
- void *freeIt;
+ p = *pp;
+ while (*p != '\0' && *p != delim) {
+ const char *varstart;
+
+ Boolean is_escaped = p[0] == '\\' && (
+ p[1] == delim || p[1] == '\\' || p[1] == '$' ||
+ (p[1] == '&' && subst != NULL));
+ if (is_escaped) {
+ Buf_AddByte(&buf, p[1]);
+ p += 2;
+ continue;
+ }
- /*
- * If unescaped dollar sign not before the
- * delimiter, assume it's a variable
- * substitution and recurse.
- */
- cp2 = Var_Parse(cp, ctxt, errnum | (flags & VARF_WANTRES),
- &len, &freeIt);
- Buf_AddBytes(&buf, strlen(cp2), cp2);
- 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 interpreted 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);
+ if (*p != '$') { /* Unescaped, simple text */
+ if (subst != NULL && *p == '&')
+ Buf_AddBytes(&buf, subst->lhs, subst->lhsLen);
+ else
+ Buf_AddByte(&buf, *p);
+ p++;
+ continue;
+ }
+
+ if (p[1] == delim) { /* Unescaped $ at end of pattern */
+ if (out_pflags != NULL)
+ *out_pflags |= VARP_ANCHOR_END;
+ else
+ Buf_AddByte(&buf, *p);
+ p++;
+ continue;
+ }
+
+ if (eflags & VARE_WANTRES) { /* Nested variable, evaluated */
+ const char *cp2;
+ int len;
+ void *freeIt;
+ VarEvalFlags nested_eflags = eflags & ~(unsigned)VARE_ASSIGN;
+
+ cp2 = Var_Parse(p, ctxt, nested_eflags, &len, &freeIt);
+ Buf_AddStr(&buf, cp2);
+ free(freeIt);
+ p += len;
+ continue;
+ }
+
+ /* XXX: This whole block is very similar to Var_Parse without
+ * VARE_WANTRES. There may be subtle edge cases though that are
+ * not yet covered in the unit tests and that are parsed differently,
+ * depending on whether they are evaluated or not.
+ *
+ * This subtle difference is not documented in the manual page,
+ * neither is the difference between parsing :D and :M documented.
+ * No code should ever depend on these details, but who knows. */
+
+ varstart = p; /* Nested variable, only parsed */
+ if (p[1] == PROPEN || p[1] == BROPEN) {
+ /*
+ * Find the end of this variable reference
+ * and suck it in without further ado.
+ * It will be interpreted later.
+ */
+ int have = p[1];
+ int want = have == PROPEN ? PRCLOSE : BRCLOSE;
+ int depth = 1;
+
+ for (p += 2; *p != '\0' && depth > 0; p++) {
+ if (p[-1] != '\\') {
+ if (*p == have)
+ depth++;
+ if (*p == want)
+ depth--;
}
}
- } else if (pattern && *cp == '&')
- Buf_AddBytes(&buf, pattern->leftLen, pattern->lhs);
- else
- Buf_AddByte(&buf, *cp);
+ Buf_AddBytesBetween(&buf, varstart, p);
+ } else {
+ Buf_AddByte(&buf, *varstart);
+ p++;
+ }
}
- if (*cp != delim) {
- *tstr = cp;
- *length = 0;
+ if (*p != delim) {
+ *pp = p;
return NULL;
}
- *tstr = ++cp;
- *length = Buf_Size(&buf);
+ *pp = ++p;
+ if (out_length != NULL)
+ *out_length = Buf_Size(&buf);
+
rstr = Buf_Destroy(&buf, FALSE);
- if (DEBUG(VAR))
- fprintf(debug_file, "Modifier pattern: \"%s\"\n", rstr);
+ VAR_DEBUG("Modifier part: \"%s\"\n", rstr);
return rstr;
}
-/*-
- *-----------------------------------------------------------------------
- * VarQuote --
- * Quote shell meta-characters and space characters in the string
- * if quoteDollar is set, also quote and double any '$' characters.
- *
- * Results:
- * The quoted string
- *
- * Side Effects:
- * None.
- *
- *-----------------------------------------------------------------------
- */
+/* Quote shell meta-characters and space characters in the string.
+ * If quoteDollar is set, also quote and double any '$' characters. */
static char *
-VarQuote(char *str, Boolean quoteDollar)
+VarQuote(const char *str, Boolean quoteDollar)
{
-
- Buffer buf;
- const char *newline;
- size_t nlen;
-
- if ((newline = Shell_GetNewline()) == NULL)
- newline = "\\\n";
- nlen = strlen(newline);
-
+ char *res;
+ Buffer buf;
Buf_Init(&buf, 0);
for (; *str != '\0'; str++) {
if (*str == '\n') {
- Buf_AddBytes(&buf, nlen, newline);
+ const char *newline = Shell_GetNewline();
+ if (newline == NULL)
+ newline = "\\\n";
+ Buf_AddStr(&buf, newline);
continue;
}
if (isspace((unsigned char)*str) || ismeta((unsigned char)*str))
Buf_AddByte(&buf, '\\');
Buf_AddByte(&buf, *str);
if (quoteDollar && *str == '$')
- Buf_AddBytes(&buf, 2, "\\$");
+ Buf_AddStr(&buf, "\\$");
}
- str = Buf_Destroy(&buf, FALSE);
- if (DEBUG(VAR))
- fprintf(debug_file, "QuoteMeta: [%s]\n", str);
- return str;
+ res = Buf_Destroy(&buf, FALSE);
+ VAR_DEBUG("QuoteMeta: [%s]\n", res);
+ return res;
}
-/*-
- *-----------------------------------------------------------------------
- * 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.
- *
- *-----------------------------------------------------------------------
- */
+/* Compute the 32-bit hash of the given string, using the MurmurHash3
+ * algorithm. Output is encoded as 8 hex digits, in Little Endian order. */
static char *
VarHash(const char *str)
{
static const char hexdigits[16] = "0123456789abcdef";
- Buffer buf;
- size_t len, len2;
const unsigned char *ustr = (const unsigned char *)str;
- uint32_t h, k, c1, c2;
- h = 0x971e137bU;
- c1 = 0x95543787U;
- c2 = 0x2ad7eb25U;
- len2 = strlen(str);
+ uint32_t h = 0x971e137bU;
+ uint32_t c1 = 0x95543787U;
+ uint32_t c2 = 0x2ad7eb25U;
+ size_t len2 = strlen(str);
+
+ char *buf;
+ size_t i;
+ size_t len;
for (len = len2; len; ) {
- k = 0;
+ uint32_t k = 0;
switch (len) {
default:
k = ((uint32_t)ustr[3] << 24) |
@@ -2162,1483 +1821,1562 @@ VarHash(const char *str)
h = h * 5 + 0x52dce729U;
h ^= k;
}
- h ^= len2;
+ h ^= (uint32_t)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]);
+ buf = bmake_malloc(9);
+ for (i = 0; i < 8; i++) {
+ buf[i] = hexdigits[h & 0x0f];
h >>= 4;
}
-
- return Buf_Destroy(&buf, FALSE);
+ buf[8] = '\0';
+ return buf;
}
static char *
-VarStrftime(const char *fmt, int zulu, time_t utc)
+VarStrftime(const char *fmt, Boolean zulu, time_t tim)
{
char buf[BUFSIZ];
- if (!utc)
- time(&utc);
+ if (!tim)
+ time(&tim);
if (!*fmt)
fmt = "%c";
- strftime(buf, sizeof(buf), fmt, zulu ? gmtime(&utc) : localtime(&utc));
+ strftime(buf, sizeof(buf), fmt, zulu ? gmtime(&tim) : localtime(&tim));
buf[sizeof(buf) - 1] = '\0';
return bmake_strdup(buf);
}
-typedef struct {
- /* const parameters */
- int startc;
- int endc;
- Var *v;
- GNode *ctxt;
- int flags;
- int *lengthPtr;
- void **freePtr;
-
- /* read-write */
- char *nstr;
- const char *tstr;
- const char *start;
- const char *cp; /* Secondary pointer into str (place marker
- * for tstr) */
- 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 */
+/* The ApplyModifier functions all work in the same way. They get the
+ * current parsing position (pp) and parse the modifier from there. The
+ * modifier typically lasts until the next ':', or a closing '}' or ')'
+ * (taken from st->endc), or the end of the string (parse error).
+ *
+ * The high-level behavior of these functions is:
+ *
+ * 1. parse the modifier
+ * 2. evaluate the modifier
+ * 3. housekeeping
+ *
+ * Parsing the modifier
+ *
+ * If parsing succeeds, the parsing position *pp is updated to point to the
+ * first character following the modifier, which typically is either ':' or
+ * st->endc.
+ *
+ * If parsing fails because of a missing delimiter (as in the :S, :C or :@
+ * modifiers), set st->missing_delim and return AMR_CLEANUP.
+ *
+ * If parsing fails because the modifier is unknown, return AMR_UNKNOWN to
+ * try the SysV modifier ${VAR:from=to} as fallback. This should only be
+ * done as long as there have been no side effects from evaluating nested
+ * variables, to avoid evaluating them more than once. In this case, the
+ * parsing position must not be updated. (XXX: Why not? The original parsing
+ * position is well-known in ApplyModifiers.)
+ *
+ * If parsing fails and the SysV modifier ${VAR:from=to} should not be used
+ * as a fallback, either issue an error message using Error or Parse_Error
+ * and then return AMR_CLEANUP, or return AMR_BAD for the default error
+ * message. Both of these return values will stop processing the variable
+ * expression. (XXX: As of 2020-08-23, evaluation of the whole string
+ * continues nevertheless after skipping a few bytes, which essentially is
+ * undefined behavior. Not in the sense of C, but still it's impossible to
+ * predict what happens in the parser.)
+ *
+ * Evaluating the modifier
+ *
+ * After parsing, the modifier is evaluated. The side effects from evaluating
+ * nested variable expressions in the modifier text often already happen
+ * during parsing though.
+ *
+ * Evaluating the modifier usually takes the current value of the variable
+ * expression from st->val, or the variable name from st->v->name and stores
+ * the result in st->newVal.
+ *
+ * If evaluating fails (as of 2020-08-23), an error message is printed using
+ * Error. This function has no side-effects, it really just prints the error
+ * message. Processing the expression continues as if everything were ok.
+ * XXX: This should be fixed by adding proper error handling to Var_Subst,
+ * Var_Parse, ApplyModifiers and ModifyWords.
+ *
+ * Housekeeping
+ *
+ * Some modifiers such as :D and :U turn undefined variables into useful
+ * variables (VAR_JUNK, VAR_KEEP).
+ *
+ * Some modifiers need to free some memory.
+ */
- /* result */
- char *newStr; /* New value to return */
+typedef struct {
+ const char startc; /* '\0' or '{' or '(' */
+ const char endc; /* '\0' or '}' or ')' */
+ Var * const v;
+ GNode * const ctxt;
+ const VarEvalFlags eflags;
+
+ char *val; /* The old value of the expression,
+ * before applying the modifier, never NULL */
+ char *newVal; /* The new value of the expression,
+ * after applying the modifier, never NULL */
+ char missing_delim; /* For error reporting */
+
+ char sep; /* Word separator in expansions
+ * (see the :ts modifier) */
+ Boolean oneBigWord; /* TRUE if some modifiers that otherwise split
+ * the variable value into words, like :S and
+ * :C, treat the variable value as a single big
+ * word, possibly containing spaces. */
} ApplyModifiersState;
-/* we now have some modifiers with long names */
-#define STRMOD_MATCH(s, want, n) \
- (strncmp(s, want, n) == 0 && (s[n] == st->endc || s[n] == ':'))
-#define STRMOD_MATCHX(s, want, n) \
- (strncmp(s, want, n) == 0 && \
- (s[n] == st->endc || s[n] == ':' || s[n] == '='))
-#define CHARMOD_MATCH(c) (c == st->endc || c == ':')
-
-/* :@var@...${var}...@ */
+typedef enum {
+ AMR_OK, /* Continue parsing */
+ AMR_UNKNOWN, /* Not a match, try other modifiers as well */
+ AMR_BAD, /* Error out with "Bad modifier" message */
+ AMR_CLEANUP /* Error out, with "Unfinished modifier"
+ * if st->missing_delim is set. */
+} ApplyModifierResult;
+
+/* Test whether mod starts with modname, followed by a delimiter. */
static Boolean
-ApplyModifier_At(ApplyModifiersState *st) {
- VarLoop loop;
- VarPattern_Flags vflags = VAR_NOSUBST;
-
- st->cp = ++(st->tstr);
- st->delim = '@';
- loop.tvar = VarGetPattern(
- st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim,
- &vflags, &loop.tvarLen, NULL);
- if (loop.tvar == NULL)
- return FALSE;
-
- loop.str = VarGetPattern(
- st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim,
- &vflags, &loop.strLen, NULL);
- if (loop.str == NULL)
- return FALSE;
+ModMatch(const char *mod, const char *modname, char endc)
+{
+ size_t n = strlen(modname);
+ return strncmp(mod, modname, n) == 0 &&
+ (mod[n] == endc || mod[n] == ':');
+}
- st->termc = *st->cp;
- st->delim = '\0';
+/* Test whether mod starts with modname, followed by a delimiter or '='. */
+static inline Boolean
+ModMatchEq(const char *mod, const char *modname, char endc)
+{
+ size_t n = strlen(modname);
+ return strncmp(mod, modname, n) == 0 &&
+ (mod[n] == endc || mod[n] == ':' || mod[n] == '=');
+}
- loop.flags = st->flags & (VARF_UNDEFERR | VARF_WANTRES);
- loop.ctxt = st->ctxt;
- st->newStr = VarModify(
- st->ctxt, &st->parsestate, st->nstr, VarLoopExpand, &loop);
- Var_Delete(loop.tvar, st->ctxt);
- free(loop.tvar);
- free(loop.str);
- return TRUE;
+/* :@var@...${var}...@ */
+static ApplyModifierResult
+ApplyModifier_Loop(const char **pp, ApplyModifiersState *st)
+{
+ ModifyWord_LoopArgs args;
+ char delim;
+ char prev_sep;
+ VarEvalFlags eflags = st->eflags & ~(unsigned)VARE_WANTRES;
+
+ args.ctx = st->ctxt;
+
+ (*pp)++; /* Skip the first '@' */
+ delim = '@';
+ args.tvar = ParseModifierPart(pp, delim, eflags,
+ st->ctxt, NULL, NULL, NULL);
+ if (args.tvar == NULL) {
+ st->missing_delim = delim;
+ return AMR_CLEANUP;
+ }
+ if (DEBUG(LINT) && strchr(args.tvar, '$') != NULL) {
+ Parse_Error(PARSE_FATAL,
+ "In the :@ modifier of \"%s\", the variable name \"%s\" "
+ "must not contain a dollar.",
+ st->v->name, args.tvar);
+ return AMR_CLEANUP;
+ }
+
+ args.str = ParseModifierPart(pp, delim, eflags,
+ st->ctxt, NULL, NULL, NULL);
+ if (args.str == NULL) {
+ st->missing_delim = delim;
+ return AMR_CLEANUP;
+ }
+
+ args.eflags = st->eflags & (VARE_UNDEFERR | VARE_WANTRES);
+ prev_sep = st->sep;
+ st->sep = ' '; /* XXX: should be st->sep for consistency */
+ st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord, st->val,
+ ModifyWord_Loop, &args);
+ st->sep = prev_sep;
+ Var_Delete(args.tvar, st->ctxt);
+ free(args.tvar);
+ free(args.str);
+ return AMR_OK;
}
/* :Ddefined or :Uundefined */
-static void
-ApplyModifier_Defined(ApplyModifiersState *st)
+static ApplyModifierResult
+ApplyModifier_Defined(const char **pp, ApplyModifiersState *st)
{
- Buffer buf; /* Buffer for patterns */
- int nflags;
+ Buffer buf;
+ const char *p;
- if (st->flags & VARF_WANTRES) {
- int wantres;
- if (*st->tstr == 'U')
- wantres = ((st->v->flags & VAR_JUNK) != 0);
- else
- wantres = ((st->v->flags & VAR_JUNK) == 0);
- nflags = st->flags & ~VARF_WANTRES;
- if (wantres)
- nflags |= VARF_WANTRES;
- } else
- nflags = st->flags;
+ VarEvalFlags eflags = st->eflags & ~(unsigned)VARE_WANTRES;
+ if (st->eflags & VARE_WANTRES) {
+ if ((**pp == 'D') == !(st->v->flags & VAR_JUNK))
+ eflags |= VARE_WANTRES;
+ }
- /*
- * 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 (st->cp = st->tstr + 1;
- *st->cp != st->endc && *st->cp != ':' && *st->cp != '\0';
- st->cp++) {
- if (*st->cp == '\\' &&
- (st->cp[1] == ':' || st->cp[1] == '$' || st->cp[1] == st->endc ||
- st->cp[1] == '\\')) {
- Buf_AddByte(&buf, st->cp[1]);
- st->cp++;
- } else if (*st->cp == '$') {
- /*
- * If unescaped dollar sign, assume it's a
- * variable substitution and recurse.
- */
- char *cp2;
- int len;
- void *freeIt;
+ p = *pp + 1;
+ while (*p != st->endc && *p != ':' && *p != '\0') {
+
+ /* Escaped delimiter or other special character */
+ if (*p == '\\') {
+ char c = p[1];
+ if (c == st->endc || c == ':' || c == '$' || c == '\\') {
+ Buf_AddByte(&buf, c);
+ p += 2;
+ continue;
+ }
+ }
- cp2 = Var_Parse(st->cp, st->ctxt, nflags, &len, &freeIt);
- Buf_AddBytes(&buf, strlen(cp2), cp2);
+ /* Nested variable expression */
+ if (*p == '$') {
+ const char *cp2;
+ int len;
+ void *freeIt;
+
+ cp2 = Var_Parse(p, st->ctxt, eflags, &len, &freeIt);
+ Buf_AddStr(&buf, cp2);
free(freeIt);
- st->cp += len - 1;
- } else {
- Buf_AddByte(&buf, *st->cp);
+ p += len;
+ continue;
}
- }
- st->termc = *st->cp;
+ /* Ordinary text */
+ Buf_AddByte(&buf, *p);
+ p++;
+ }
+ *pp = p;
- if ((st->v->flags & VAR_JUNK) != 0)
+ if (st->v->flags & VAR_JUNK)
st->v->flags |= VAR_KEEP;
- if (nflags & VARF_WANTRES) {
- st->newStr = Buf_Destroy(&buf, FALSE);
+ if (eflags & VARE_WANTRES) {
+ st->newVal = Buf_Destroy(&buf, FALSE);
} else {
- st->newStr = st->nstr;
+ st->newVal = st->val;
Buf_Destroy(&buf, TRUE);
}
+ return AMR_OK;
}
/* :gmtime */
-static Boolean
-ApplyModifier_Gmtime(ApplyModifiersState *st)
+static ApplyModifierResult
+ApplyModifier_Gmtime(const char **pp, ApplyModifiersState *st)
{
time_t utc;
- char *ep;
- st->cp = st->tstr + 1; /* make sure it is set */
- if (!STRMOD_MATCHX(st->tstr, "gmtime", 6))
- return FALSE;
- if (st->tstr[6] == '=') {
- utc = strtoul(&st->tstr[7], &ep, 10);
- st->cp = ep;
+ const char *mod = *pp;
+ if (!ModMatchEq(mod, "gmtime", st->endc))
+ return AMR_UNKNOWN;
+
+ if (mod[6] == '=') {
+ char *ep;
+ utc = (time_t)strtoul(mod + 7, &ep, 10);
+ *pp = ep;
} else {
utc = 0;
- st->cp = st->tstr + 6;
+ *pp = mod + 6;
}
- st->newStr = VarStrftime(st->nstr, 1, utc);
- st->termc = *st->cp;
- return TRUE;
+ st->newVal = VarStrftime(st->val, TRUE, utc);
+ return AMR_OK;
}
/* :localtime */
static Boolean
-ApplyModifier_Localtime(ApplyModifiersState *st)
+ApplyModifier_Localtime(const char **pp, ApplyModifiersState *st)
{
time_t utc;
- char *ep;
- st->cp = st->tstr + 1; /* make sure it is set */
- if (!STRMOD_MATCHX(st->tstr, "localtime", 9))
- return FALSE;
+ const char *mod = *pp;
+ if (!ModMatchEq(mod, "localtime", st->endc))
+ return AMR_UNKNOWN;
- if (st->tstr[9] == '=') {
- utc = strtoul(&st->tstr[10], &ep, 10);
- st->cp = ep;
+ if (mod[9] == '=') {
+ char *ep;
+ utc = (time_t)strtoul(mod + 10, &ep, 10);
+ *pp = ep;
} else {
utc = 0;
- st->cp = st->tstr + 9;
+ *pp = mod + 9;
}
- st->newStr = VarStrftime(st->nstr, 0, utc);
- st->termc = *st->cp;
- return TRUE;
+ st->newVal = VarStrftime(st->val, FALSE, utc);
+ return AMR_OK;
}
/* :hash */
-static Boolean
-ApplyModifier_Hash(ApplyModifiersState *st)
+static ApplyModifierResult
+ApplyModifier_Hash(const char **pp, ApplyModifiersState *st)
{
- st->cp = st->tstr + 1; /* make sure it is set */
- if (!STRMOD_MATCH(st->tstr, "hash", 4))
- return FALSE;
- st->newStr = VarHash(st->nstr);
- st->cp = st->tstr + 4;
- st->termc = *st->cp;
- return TRUE;
+ if (!ModMatch(*pp, "hash", st->endc))
+ return AMR_UNKNOWN;
+
+ st->newVal = VarHash(st->val);
+ *pp += 4;
+ return AMR_OK;
}
/* :P */
-static void
-ApplyModifier_Path(ApplyModifiersState *st)
+static ApplyModifierResult
+ApplyModifier_Path(const char **pp, ApplyModifiersState *st)
{
GNode *gn;
+ char *path;
- if ((st->v->flags & VAR_JUNK) != 0)
+ if (st->v->flags & VAR_JUNK)
st->v->flags |= VAR_KEEP;
+
gn = Targ_FindNode(st->v->name, TARG_NOCREATE);
if (gn == NULL || gn->type & OP_NOPATH) {
- st->newStr = NULL;
+ path = NULL;
} else if (gn->path) {
- st->newStr = bmake_strdup(gn->path);
+ path = bmake_strdup(gn->path);
} else {
- st->newStr = Dir_FindFile(st->v->name, Suff_FindPath(gn));
+ Lst searchPath = Suff_FindPath(gn);
+ path = Dir_FindFile(st->v->name, searchPath);
}
- if (!st->newStr)
- st->newStr = bmake_strdup(st->v->name);
- st->cp = ++st->tstr;
- st->termc = *st->tstr;
+ if (path == NULL)
+ path = bmake_strdup(st->v->name);
+ st->newVal = path;
+
+ (*pp)++;
+ return AMR_OK;
}
/* :!cmd! */
-static Boolean
-ApplyModifier_Exclam(ApplyModifiersState *st)
+static ApplyModifierResult
+ApplyModifier_Exclam(const char **pp, ApplyModifiersState *st)
{
- const char *emsg;
- VarPattern pattern;
-
- pattern.flags = 0;
-
- st->delim = '!';
- emsg = NULL;
- st->cp = ++st->tstr;
- pattern.rhs = VarGetPattern(
- st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim,
- NULL, &pattern.rightLen, NULL);
- if (pattern.rhs == NULL)
- return FALSE;
- if (st->flags & VARF_WANTRES)
- st->newStr = Cmd_Exec(pattern.rhs, &emsg);
+ char delim;
+ char *cmd;
+ const char *errfmt;
+
+ (*pp)++;
+ delim = '!';
+ cmd = ParseModifierPart(pp, delim, st->eflags, st->ctxt,
+ NULL, NULL, NULL);
+ if (cmd == NULL) {
+ st->missing_delim = delim;
+ return AMR_CLEANUP;
+ }
+
+ errfmt = NULL;
+ if (st->eflags & VARE_WANTRES)
+ st->newVal = Cmd_Exec(cmd, &errfmt);
else
- st->newStr = varNoError;
- free(UNCONST(pattern.rhs));
- if (emsg)
- Error(emsg, st->nstr);
- st->termc = *st->cp;
- st->delim = '\0';
+ st->newVal = varNoError;
+ free(cmd);
+
+ if (errfmt != NULL)
+ Error(errfmt, st->val); /* XXX: why still return AMR_OK? */
+
if (st->v->flags & VAR_JUNK)
st->v->flags |= VAR_KEEP;
- return TRUE;
+ return AMR_OK;
}
-/* :range */
-static Boolean
-ApplyModifier_Range(ApplyModifiersState *st)
+/* The :range modifier generates an integer sequence as long as the words.
+ * The :range=7 modifier generates an integer sequence from 1 to 7. */
+static ApplyModifierResult
+ApplyModifier_Range(const char **pp, ApplyModifiersState *st)
{
- int n;
- char *ep;
+ size_t n;
+ Buffer buf;
+ size_t i;
- st->cp = st->tstr + 1; /* make sure it is set */
- if (!STRMOD_MATCHX(st->tstr, "range", 5))
- return FALSE;
+ const char *mod = *pp;
+ if (!ModMatchEq(mod, "range", st->endc))
+ return AMR_UNKNOWN;
- if (st->tstr[5] == '=') {
- n = strtoul(&st->tstr[6], &ep, 10);
- st->cp = ep;
+ if (mod[5] == '=') {
+ char *ep;
+ n = (size_t)strtoul(mod + 6, &ep, 10);
+ *pp = ep;
} else {
n = 0;
- st->cp = st->tstr + 5;
+ *pp = mod + 5;
}
- st->newStr = VarRange(st->nstr, n);
- st->termc = *st->cp;
- return TRUE;
+
+ if (n == 0) {
+ Words words = Str_Words(st->val, FALSE);
+ n = words.len;
+ Words_Free(words);
+ }
+
+ Buf_Init(&buf, 0);
+
+ for (i = 0; i < n; i++) {
+ if (i != 0)
+ Buf_AddByte(&buf, ' '); /* XXX: st->sep, for consistency */
+ Buf_AddInt(&buf, 1 + (int)i);
+ }
+
+ st->newVal = Buf_Destroy(&buf, FALSE);
+ return AMR_OK;
}
/* :Mpattern or :Npattern */
-static void
-ApplyModifier_Match(ApplyModifiersState *st)
+static ApplyModifierResult
+ApplyModifier_Match(const char **pp, ApplyModifiersState *st)
{
- 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;
+ const char *mod = *pp;
+ Boolean copy = FALSE; /* pattern should be, or has been, copied */
+ Boolean needSubst = FALSE;
+ const char *endpat;
+ char *pattern;
+ ModifyWordsCallback callback;
+
/*
- * 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.
+ * 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 (st->cp = st->tstr + 1;
- *st->cp != '\0' && !(*st->cp == ':' && nest == 1);
- st->cp++) {
- if (*st->cp == '\\' &&
- (st->cp[1] == ':' || st->cp[1] == st->endc ||
- st->cp[1] == st->startc)) {
+ int nest = 0;
+ const char *p;
+ for (p = mod + 1; *p != '\0' && !(*p == ':' && nest == 0); p++) {
+ if (*p == '\\' &&
+ (p[1] == ':' || p[1] == st->endc || p[1] == st->startc)) {
if (!needSubst)
copy = TRUE;
- st->cp++;
+ p++;
continue;
}
- if (*st->cp == '$')
+ if (*p == '$')
needSubst = TRUE;
- if (*st->cp == '(' || *st->cp == '{')
- ++nest;
- if (*st->cp == ')' || *st->cp == '}') {
- --nest;
- if (nest == 0)
+ if (*p == '(' || *p == '{')
+ nest++;
+ if (*p == ')' || *p == '}') {
+ nest--;
+ if (nest < 0)
break;
}
}
- st->termc = *st->cp;
- endpat = st->cp;
+ *pp = p;
+ endpat = p;
+
if (copy) {
- /*
- * Need to compress the \:'s out of the pattern, so
- * allocate enough room to hold the uncompressed
- * pattern (note that st->cp started at st->tstr+1, so
- * st->cp - st->tstr takes the null byte into account) and
- * compress the pattern into the space.
- */
- pattern = bmake_malloc(st->cp - st->tstr);
- for (cp2 = pattern, st->cp = st->tstr + 1;
- st->cp < endpat;
- st->cp++, cp2++) {
- if ((*st->cp == '\\') && (st->cp+1 < endpat) &&
- (st->cp[1] == ':' || st->cp[1] == st->endc))
- st->cp++;
- *cp2 = *st->cp;
- }
- *cp2 = '\0';
- endpat = cp2;
+ char *dst;
+ const char *src;
+
+ /* Compress the \:'s out of the pattern. */
+ pattern = bmake_malloc((size_t)(endpat - (mod + 1)) + 1);
+ dst = pattern;
+ src = mod + 1;
+ for (; src < endpat; src++, dst++) {
+ if (src[0] == '\\' && src + 1 < endpat &&
+ /* XXX: st->startc is missing here; see above */
+ (src[1] == ':' || src[1] == st->endc))
+ src++;
+ *dst = *src;
+ }
+ *dst = '\0';
+ endpat = dst;
} else {
- /*
- * Either Var_Subst or VarModify will need a
- * nul-terminated string soon, so construct one now.
- */
- pattern = bmake_strndup(st->tstr+1, endpat - (st->tstr + 1));
+ pattern = bmake_strsedup(mod + 1, endpat);
}
+
if (needSubst) {
/* pattern contains embedded '$', so use Var_Subst to expand it. */
- cp2 = pattern;
- pattern = Var_Subst(NULL, cp2, st->ctxt, st->flags);
- free(cp2);
- }
- if (DEBUG(VAR))
- fprintf(debug_file, "Pattern[%s] for [%s] is [%s]\n",
- st->v->name, st->nstr, pattern);
- if (*st->tstr == 'M') {
- st->newStr = VarModify(st->ctxt, &st->parsestate, st->nstr, VarMatch,
- pattern);
- } else {
- st->newStr = VarModify(st->ctxt, &st->parsestate, st->nstr, VarNoMatch,
- pattern);
+ char *old_pattern = pattern;
+ pattern = Var_Subst(pattern, st->ctxt, st->eflags);
+ free(old_pattern);
}
+
+ VAR_DEBUG("Pattern[%s] for [%s] is [%s]\n", st->v->name, st->val, pattern);
+
+ callback = mod[0] == 'M' ? ModifyWord_Match : ModifyWord_NoMatch;
+ st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord, st->val,
+ callback, pattern);
free(pattern);
+ return AMR_OK;
}
/* :S,from,to, */
-static Boolean
-ApplyModifier_Subst(ApplyModifiersState *st)
+static ApplyModifierResult
+ApplyModifier_Subst(const char **pp, ApplyModifiersState *st)
{
- VarPattern pattern;
- Var_Parse_State tmpparsestate;
+ ModifyWord_SubstArgs args;
+ char *lhs, *rhs;
+ Boolean oneBigWord;
+
+ char delim = (*pp)[1];
+ if (delim == '\0') {
+ Error("Missing delimiter for :S modifier");
+ (*pp)++;
+ return AMR_CLEANUP;
+ }
+
+ *pp += 2;
- pattern.flags = 0;
- tmpparsestate = st->parsestate;
- st->delim = st->tstr[1];
- st->tstr += 2;
+ args.pflags = 0;
+ args.matched = FALSE;
/*
* If pattern begins with '^', it is anchored to the
* start of the word -- skip over it and flag pattern.
*/
- if (*st->tstr == '^') {
- pattern.flags |= VAR_MATCH_START;
- st->tstr += 1;
+ if (**pp == '^') {
+ args.pflags |= VARP_ANCHOR_START;
+ (*pp)++;
}
- st->cp = st->tstr;
- pattern.lhs = VarGetPattern(
- st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim,
- &pattern.flags, &pattern.leftLen, NULL);
- if (pattern.lhs == NULL)
- return FALSE;
+ lhs = ParseModifierPart(pp, delim, st->eflags, st->ctxt,
+ &args.lhsLen, &args.pflags, NULL);
+ if (lhs == NULL) {
+ st->missing_delim = delim;
+ return AMR_CLEANUP;
+ }
+ args.lhs = lhs;
- pattern.rhs = VarGetPattern(
- st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim,
- NULL, &pattern.rightLen, &pattern);
- if (pattern.rhs == NULL)
- return FALSE;
+ rhs = ParseModifierPart(pp, delim, st->eflags, st->ctxt,
+ &args.rhsLen, NULL, &args);
+ if (rhs == NULL) {
+ st->missing_delim = delim;
+ return AMR_CLEANUP;
+ }
+ args.rhs = rhs;
- /*
- * Check for global substitution. If 'g' after the final
- * delimiter, substitution is global and is marked that
- * way.
- */
- for (;; st->cp++) {
- switch (*st->cp) {
+ oneBigWord = st->oneBigWord;
+ for (;; (*pp)++) {
+ switch (**pp) {
case 'g':
- pattern.flags |= VAR_SUB_GLOBAL;
+ args.pflags |= VARP_SUB_GLOBAL;
continue;
case '1':
- pattern.flags |= VAR_SUB_ONE;
+ args.pflags |= VARP_SUB_ONE;
continue;
case 'W':
- tmpparsestate.oneBigWord = TRUE;
+ oneBigWord = TRUE;
continue;
}
break;
}
- st->termc = *st->cp;
- st->newStr = VarModify(
- st->ctxt, &tmpparsestate, st->nstr, VarSubstitute, &pattern);
+ st->newVal = ModifyWords(st->ctxt, st->sep, oneBigWord, st->val,
+ ModifyWord_Subst, &args);
- /* Free the two strings. */
- free(UNCONST(pattern.lhs));
- free(UNCONST(pattern.rhs));
- st->delim = '\0';
- return TRUE;
+ free(lhs);
+ free(rhs);
+ return AMR_OK;
}
#ifndef NO_REGEX
+
/* :C,from,to, */
-static Boolean
-ApplyModifier_Regex(ApplyModifiersState *st)
+static ApplyModifierResult
+ApplyModifier_Regex(const char **pp, ApplyModifiersState *st)
{
- VarREPattern pattern;
- char *re;
- int error;
- Var_Parse_State tmpparsestate;
-
- pattern.flags = 0;
- tmpparsestate = st->parsestate;
- st->delim = st->tstr[1];
- st->tstr += 2;
-
- st->cp = st->tstr;
-
- re = VarGetPattern(
- st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim,
- NULL, NULL, NULL);
- if (re == NULL)
- return FALSE;
+ char *re;
+ ModifyWord_SubstRegexArgs args;
+ Boolean oneBigWord;
+ int error;
- pattern.replace = VarGetPattern(
- st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim,
- NULL, NULL, NULL);
- if (pattern.replace == NULL) {
+ char delim = (*pp)[1];
+ if (delim == '\0') {
+ Error("Missing delimiter for :C modifier");
+ (*pp)++;
+ return AMR_CLEANUP;
+ }
+
+ *pp += 2;
+
+ re = ParseModifierPart(pp, delim, st->eflags, st->ctxt, NULL, NULL, NULL);
+ if (re == NULL) {
+ st->missing_delim = delim;
+ return AMR_CLEANUP;
+ }
+
+ args.replace = ParseModifierPart(pp, delim, st->eflags, st->ctxt,
+ NULL, NULL, NULL);
+ if (args.replace == NULL) {
free(re);
- return FALSE;
+ st->missing_delim = delim;
+ return AMR_CLEANUP;
}
- for (;; st->cp++) {
- switch (*st->cp) {
+ args.pflags = 0;
+ args.matched = FALSE;
+ oneBigWord = st->oneBigWord;
+ for (;; (*pp)++) {
+ switch (**pp) {
case 'g':
- pattern.flags |= VAR_SUB_GLOBAL;
+ args.pflags |= VARP_SUB_GLOBAL;
continue;
case '1':
- pattern.flags |= VAR_SUB_ONE;
+ args.pflags |= VARP_SUB_ONE;
continue;
case 'W':
- tmpparsestate.oneBigWord = TRUE;
+ oneBigWord = TRUE;
continue;
}
break;
}
- st->termc = *st->cp;
-
- error = regcomp(&pattern.re, re, REG_EXTENDED);
+ error = regcomp(&args.re, re, REG_EXTENDED);
free(re);
if (error) {
- *st->lengthPtr = st->cp - st->start + 1;
- VarREError(error, &pattern.re, "RE substitution error");
- free(pattern.replace);
- return FALSE;
+ VarREError(error, &args.re, "Regex compilation error");
+ free(args.replace);
+ return AMR_CLEANUP;
+ }
+
+ args.nsub = args.re.re_nsub + 1;
+ if (args.nsub > 10)
+ args.nsub = 10;
+ st->newVal = ModifyWords(st->ctxt, st->sep, oneBigWord, st->val,
+ ModifyWord_SubstRegex, &args);
+ regfree(&args.re);
+ free(args.replace);
+ return AMR_OK;
+}
+#endif
+
+static void
+ModifyWord_Copy(const char *word, SepBuf *buf, void *data MAKE_ATTR_UNUSED)
+{
+ SepBuf_AddStr(buf, word);
+}
+
+/* :ts<separator> */
+static ApplyModifierResult
+ApplyModifier_ToSep(const char **pp, ApplyModifiersState *st)
+{
+ /* XXX: pp points to the 's', for historic reasons only.
+ * Changing this will influence the error messages. */
+ const char *sep = *pp + 1;
+
+ /* ":ts<any><endc>" or ":ts<any>:" */
+ if (sep[0] != st->endc && (sep[1] == st->endc || sep[1] == ':')) {
+ st->sep = sep[0];
+ *pp = sep + 1;
+ goto ok;
}
- 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));
- st->newStr = VarModify(
- st->ctxt, &tmpparsestate, st->nstr, VarRESubstitute, &pattern);
- regfree(&pattern.re);
- free(pattern.replace);
- free(pattern.matches);
- st->delim = '\0';
- return TRUE;
+ /* ":ts<endc>" or ":ts:" */
+ if (sep[0] == st->endc || sep[0] == ':') {
+ st->sep = '\0'; /* no separator */
+ *pp = sep;
+ goto ok;
+ }
+
+ /* ":ts<unrecognised><unrecognised>". */
+ if (sep[0] != '\\')
+ return AMR_BAD;
+
+ /* ":ts\n" */
+ if (sep[1] == 'n') {
+ st->sep = '\n';
+ *pp = sep + 2;
+ goto ok;
+ }
+
+ /* ":ts\t" */
+ if (sep[1] == 't') {
+ st->sep = '\t';
+ *pp = sep + 2;
+ goto ok;
+ }
+
+ /* ":ts\x40" or ":ts\100" */
+ {
+ const char *numStart = sep + 1;
+ int base = 8; /* assume octal */
+ char *end;
+
+ if (sep[1] == 'x') {
+ base = 16;
+ numStart++;
+ } else if (!isdigit((unsigned char)sep[1]))
+ return AMR_BAD; /* ":ts<backslash><unrecognised>". */
+
+ st->sep = (char)strtoul(numStart, &end, base);
+ if (*end != ':' && *end != st->endc)
+ return AMR_BAD;
+ *pp = end;
+ }
+
+ok:
+ st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord, st->val,
+ ModifyWord_Copy, NULL);
+ return AMR_OK;
}
-#endif
-/* :tA, :tu, :tl, etc. */
-static Boolean
-ApplyModifier_To(ApplyModifiersState *st)
+/* :tA, :tu, :tl, :ts<separator>, etc. */
+static ApplyModifierResult
+ApplyModifier_To(const char **pp, ApplyModifiersState *st)
{
- st->cp = st->tstr + 1; /* make sure it is set */
- if (st->tstr[1] != st->endc && st->tstr[1] != ':') {
- if (st->tstr[1] == 's') {
- /* Use the char (if any) at st->tstr[2] as the word separator. */
- VarPattern pattern;
-
- if (st->tstr[2] != st->endc &&
- (st->tstr[3] == st->endc || st->tstr[3] == ':')) {
- /* ":ts<unrecognised><endc>" or
- * ":ts<unrecognised>:" */
- st->parsestate.varSpace = st->tstr[2];
- st->cp = st->tstr + 3;
- } else if (st->tstr[2] == st->endc || st->tstr[2] == ':') {
- /* ":ts<endc>" or ":ts:" */
- st->parsestate.varSpace = 0; /* no separator */
- st->cp = st->tstr + 2;
- } else if (st->tstr[2] == '\\') {
- const char *xp = &st->tstr[3];
- int base = 8; /* assume octal */
-
- switch (st->tstr[3]) {
- case 'n':
- st->parsestate.varSpace = '\n';
- st->cp = st->tstr + 4;
- break;
- case 't':
- st->parsestate.varSpace = '\t';
- st->cp = st->tstr + 4;
- break;
- case 'x':
- base = 16;
- xp++;
- goto get_numeric;
- case '0':
- base = 0;
- goto get_numeric;
- default:
- if (isdigit((unsigned char)st->tstr[3])) {
- char *ep;
- get_numeric:
- st->parsestate.varSpace = strtoul(xp, &ep, base);
- if (*ep != ':' && *ep != st->endc)
- return FALSE;
- st->cp = ep;
- } else {
- /* ":ts<backslash><unrecognised>". */
- return FALSE;
- }
- break;
- }
- } else {
- /* Found ":ts<unrecognised><unrecognised>". */
- return FALSE;
- }
+ const char *mod = *pp;
+ assert(mod[0] == 't');
- st->termc = *st->cp;
+ *pp = mod + 1; /* make sure it is set */
+ if (mod[1] == st->endc || mod[1] == ':' || mod[1] == '\0')
+ return AMR_BAD; /* Found ":t<endc>" or ":t:". */
- /*
- * 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;
-
- st->newStr = VarModify(
- st->ctxt, &st->parsestate, st->nstr, VarSubstitute, &pattern);
- } else if (st->tstr[2] == st->endc || st->tstr[2] == ':') {
- /* Check for two-character options: ":tu", ":tl" */
- if (st->tstr[1] == 'A') { /* absolute path */
- st->newStr = VarModify(
- st->ctxt, &st->parsestate, st->nstr, VarRealpath, NULL);
- st->cp = st->tstr + 2;
- st->termc = *st->cp;
- } else if (st->tstr[1] == 'u') {
- char *dp = bmake_strdup(st->nstr);
- for (st->newStr = dp; *dp; dp++)
- *dp = toupper((unsigned char)*dp);
- st->cp = st->tstr + 2;
- st->termc = *st->cp;
- } else if (st->tstr[1] == 'l') {
- char *dp = bmake_strdup(st->nstr);
- for (st->newStr = dp; *dp; dp++)
- *dp = tolower((unsigned char)*dp);
- st->cp = st->tstr + 2;
- st->termc = *st->cp;
- } else if (st->tstr[1] == 'W' || st->tstr[1] == 'w') {
- st->parsestate.oneBigWord = (st->tstr[1] == 'W');
- st->newStr = st->nstr;
- st->cp = st->tstr + 2;
- st->termc = *st->cp;
- } else {
- /* Found ":t<unrecognised>:" or ":t<unrecognised><endc>". */
- return FALSE;
- }
- } else {
- /* Found ":t<unrecognised><unrecognised>". */
- return FALSE;
- }
- } else {
- /* Found ":t<endc>" or ":t:". */
- return FALSE;
+ if (mod[1] == 's')
+ return ApplyModifier_ToSep(pp, st);
+
+ if (mod[2] != st->endc && mod[2] != ':')
+ return AMR_BAD; /* Found ":t<unrecognised><unrecognised>". */
+
+ /* Check for two-character options: ":tu", ":tl" */
+ if (mod[1] == 'A') { /* absolute path */
+ st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord, st->val,
+ ModifyWord_Realpath, NULL);
+ *pp = mod + 2;
+ return AMR_OK;
}
- return TRUE;
+
+ if (mod[1] == 'u') {
+ size_t i;
+ size_t len = strlen(st->val);
+ st->newVal = bmake_malloc(len + 1);
+ for (i = 0; i < len + 1; i++)
+ st->newVal[i] = (char)toupper((unsigned char)st->val[i]);
+ *pp = mod + 2;
+ return AMR_OK;
+ }
+
+ if (mod[1] == 'l') {
+ size_t i;
+ size_t len = strlen(st->val);
+ st->newVal = bmake_malloc(len + 1);
+ for (i = 0; i < len + 1; i++)
+ st->newVal[i] = (char)tolower((unsigned char)st->val[i]);
+ *pp = mod + 2;
+ return AMR_OK;
+ }
+
+ if (mod[1] == 'W' || mod[1] == 'w') {
+ st->oneBigWord = mod[1] == 'W';
+ st->newVal = st->val;
+ *pp = mod + 2;
+ return AMR_OK;
+ }
+
+ /* Found ":t<unrecognised>:" or ":t<unrecognised><endc>". */
+ return AMR_BAD;
}
/* :[#], :[1], etc. */
-static int
-ApplyModifier_Words(ApplyModifiersState *st)
+static ApplyModifierResult
+ApplyModifier_Words(const char **pp, ApplyModifiersState *st)
{
- /*
- * Look for the closing ']', recursively
- * expanding any embedded variables.
- *
- * estr is a pointer to the expanded result,
- * which we must free().
- */
+ char delim;
char *estr;
+ char *ep;
+ int first, last;
- st->cp = st->tstr + 1; /* point to char after '[' */
- st->delim = ']'; /* look for closing ']' */
- estr = VarGetPattern(
- st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim,
- NULL, NULL, NULL);
- if (estr == NULL)
- return 'c'; /* report missing ']' */
- /* now st->cp points just after the closing ']' */
- st->delim = '\0';
- if (st->cp[0] != ':' && st->cp[0] != st->endc) {
- /* Found junk after ']' */
- free(estr);
- return 'b';
+ (*pp)++; /* skip the '[' */
+ delim = ']'; /* look for closing ']' */
+ estr = ParseModifierPart(pp, delim, st->eflags, st->ctxt,
+ NULL, NULL, NULL);
+ if (estr == NULL) {
+ st->missing_delim = delim;
+ return AMR_CLEANUP;
}
- if (estr[0] == '\0') {
- /* Found empty square brackets in ":[]". */
- free(estr);
- return 'b';
- } 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;
+ /* now *pp points just after the closing ']' */
+ if (**pp != ':' && **pp != st->endc)
+ goto bad_modifier; /* Found junk after ']' */
+
+ if (estr[0] == '\0')
+ goto bad_modifier; /* empty square brackets in ":[]". */
- st->newStr = bmake_malloc(newStrSize);
- if (st->parsestate.oneBigWord) {
- strncpy(st->newStr, "1", newStrSize);
+ if (estr[0] == '#' && estr[1] == '\0') { /* Found ":[#]" */
+ if (st->oneBigWord) {
+ st->newVal = bmake_strdup("1");
} else {
- /* XXX: brk_string() is a rather expensive
- * way of counting words. */
- char **av;
- char *as;
- int ac;
-
- av = brk_string(st->nstr, &ac, FALSE, &as);
- snprintf(st->newStr, newStrSize, "%d", ac);
- free(as);
- free(av);
+ Buffer buf;
+
+ Words words = Str_Words(st->val, FALSE);
+ size_t ac = words.len;
+ Words_Free(words);
+
+ Buf_Init(&buf, 4); /* 3 digits + '\0' is usually enough */
+ Buf_AddInt(&buf, (int)ac);
+ st->newVal = Buf_Destroy(&buf, FALSE);
}
- st->termc = *st->cp;
- free(estr);
- return 0;
- } else if (estr[0] == '*' && estr[1] == '\0') {
+ goto ok;
+ }
+
+ if (estr[0] == '*' && estr[1] == '\0') {
/* Found ":[*]" */
- st->parsestate.oneBigWord = TRUE;
- st->newStr = st->nstr;
- st->termc = *st->cp;
- free(estr);
- return 0;
- } else if (estr[0] == '@' && estr[1] == '\0') {
+ st->oneBigWord = TRUE;
+ st->newVal = st->val;
+ goto ok;
+ }
+
+ if (estr[0] == '@' && estr[1] == '\0') {
/* Found ":[@]" */
- st->parsestate.oneBigWord = FALSE;
- st->newStr = st->nstr;
- st->termc = *st->cp;
- free(estr);
- return 0;
- } else {
- char *ep;
- /*
- * We expect estr to contain a single
- * integer for :[N], or two integers
- * separated by ".." for :[start..end].
- */
- VarSelectWords_t seldata = { 0, 0 };
-
- seldata.start = strtol(estr, &ep, 0);
- if (ep == estr) {
- /* Found junk instead of a number */
- free(estr);
- return 'b';
- } 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);
- return 'b';
- }
- } else {
- /* Found junk instead of ".." */
- free(estr);
- return 'b';
- }
- /*
- * 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]" */
- st->parsestate.oneBigWord = TRUE;
- st->newStr = st->nstr;
- st->termc = *st->cp;
- free(estr);
- return 0;
- } else if (seldata.start == 0 || seldata.end == 0) {
- /* ":[0..N]" or ":[N..0]" */
- free(estr);
- return 'b';
- }
- /* Normal case: select the words described by seldata. */
- st->newStr = VarSelectWords(
- st->ctxt, &st->parsestate, st->nstr, &seldata);
+ st->oneBigWord = FALSE;
+ st->newVal = st->val;
+ goto ok;
+ }
+
+ /*
+ * We expect estr to contain a single integer for :[N], or two integers
+ * separated by ".." for :[start..end].
+ */
+ first = (int)strtol(estr, &ep, 0);
+ if (ep == estr) /* Found junk instead of a number */
+ goto bad_modifier;
+
+ if (ep[0] == '\0') { /* Found only one integer in :[N] */
+ last = first;
+ } else if (ep[0] == '.' && ep[1] == '.' && ep[2] != '\0') {
+ /* Expecting another integer after ".." */
+ ep += 2;
+ last = (int)strtol(ep, &ep, 0);
+ if (ep[0] != '\0') /* Found junk after ".." */
+ goto bad_modifier;
+ } else
+ goto bad_modifier; /* Found junk instead of ".." */
- st->termc = *st->cp;
- free(estr);
- return 0;
+ /*
+ * Now seldata is properly filled in, but we still have to check for 0 as
+ * a special case.
+ */
+ if (first == 0 && last == 0) {
+ /* ":[0]" or perhaps ":[0..0]" */
+ st->oneBigWord = TRUE;
+ st->newVal = st->val;
+ goto ok;
}
+
+ /* ":[0..N]" or ":[N..0]" */
+ if (first == 0 || last == 0)
+ goto bad_modifier;
+
+ /* Normal case: select the words described by seldata. */
+ st->newVal = VarSelectWords(st->sep, st->oneBigWord, st->val, first, last);
+
+ok:
+ free(estr);
+ return AMR_OK;
+
+bad_modifier:
+ free(estr);
+ return AMR_BAD;
}
-/* :O or :Ox */
-static Boolean
-ApplyModifier_Order(ApplyModifiersState *st)
+static int
+str_cmp_asc(const void *a, const void *b)
+{
+ return strcmp(*(const char * const *)a, *(const char * const *)b);
+}
+
+static int
+str_cmp_desc(const void *a, const void *b)
+{
+ return strcmp(*(const char * const *)b, *(const char * const *)a);
+}
+
+/* :O (order ascending) or :Or (order descending) or :Ox (shuffle) */
+static ApplyModifierResult
+ApplyModifier_Order(const char **pp, ApplyModifiersState *st)
{
- char otype;
-
- st->cp = st->tstr + 1; /* skip to the rest in any case */
- if (st->tstr[1] == st->endc || st->tstr[1] == ':') {
- otype = 's';
- st->termc = *st->cp;
- } else if ((st->tstr[1] == 'r' || st->tstr[1] == 'x') &&
- (st->tstr[2] == st->endc || st->tstr[2] == ':')) {
- otype = st->tstr[1];
- st->cp = st->tstr + 2;
- st->termc = *st->cp;
+ const char *mod = (*pp)++; /* skip past the 'O' in any case */
+
+ Words words = Str_Words(st->val, FALSE);
+
+ if (mod[1] == st->endc || mod[1] == ':') {
+ /* :O sorts ascending */
+ qsort(words.words, words.len, sizeof(char *), str_cmp_asc);
+
+ } else if ((mod[1] == 'r' || mod[1] == 'x') &&
+ (mod[2] == st->endc || mod[2] == ':')) {
+ (*pp)++;
+
+ if (mod[1] == 'r') {
+ /* :Or sorts descending */
+ qsort(words.words, words.len, sizeof(char *), str_cmp_desc);
+
+ } else {
+ /* :Ox shuffles
+ *
+ * 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).
+ */
+ size_t i;
+ for (i = words.len - 1; i > 0; i--) {
+ size_t rndidx = (size_t)random() % (i + 1);
+ char *t = words.words[i];
+ words.words[i] = words.words[rndidx];
+ words.words[rndidx] = t;
+ }
+ }
} else {
- return FALSE;
+ Words_Free(words);
+ return AMR_BAD;
}
- st->newStr = VarOrder(st->nstr, otype);
- return TRUE;
+
+ st->newVal = Words_JoinFree(words);
+ return AMR_OK;
}
/* :? then : else */
-static Boolean
-ApplyModifier_IfElse(ApplyModifiersState *st)
+static ApplyModifierResult
+ApplyModifier_IfElse(const char **pp, ApplyModifiersState *st)
{
- VarPattern pattern;
- Boolean value;
- int cond_rc;
- VarPattern_Flags lhs_flags, rhs_flags;
+ char delim;
+ char *then_expr, *else_expr;
+
+ Boolean value = FALSE;
+ VarEvalFlags then_eflags = st->eflags & ~(unsigned)VARE_WANTRES;
+ VarEvalFlags else_eflags = st->eflags & ~(unsigned)VARE_WANTRES;
- /* find ':', and then substitute accordingly */
- if (st->flags & VARF_WANTRES) {
+ int cond_rc = COND_PARSE; /* anything other than COND_INVALID */
+ if (st->eflags & VARE_WANTRES) {
cond_rc = Cond_EvalExpression(NULL, st->v->name, &value, 0, FALSE);
- if (cond_rc == COND_INVALID) {
- lhs_flags = rhs_flags = VAR_NOSUBST;
- } else if (value) {
- lhs_flags = 0;
- rhs_flags = VAR_NOSUBST;
- } else {
- lhs_flags = VAR_NOSUBST;
- rhs_flags = 0;
- }
- } else {
- /* we are just consuming and discarding */
- cond_rc = value = 0;
- lhs_flags = rhs_flags = VAR_NOSUBST;
+ if (cond_rc != COND_INVALID && value)
+ then_eflags |= VARE_WANTRES;
+ if (cond_rc != COND_INVALID && !value)
+ else_eflags |= VARE_WANTRES;
}
- pattern.flags = 0;
-
- st->cp = ++st->tstr;
- st->delim = ':';
- pattern.lhs = VarGetPattern(
- st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim,
- &lhs_flags, &pattern.leftLen, NULL);
- if (pattern.lhs == NULL)
- return FALSE;
- /* BROPEN or PROPEN */
- st->delim = st->endc;
- pattern.rhs = VarGetPattern(
- st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim,
- &rhs_flags, &pattern.rightLen, NULL);
- if (pattern.rhs == NULL)
- return FALSE;
+ (*pp)++; /* skip past the '?' */
+ delim = ':';
+ then_expr = ParseModifierPart(pp, delim, then_eflags, st->ctxt,
+ NULL, NULL, NULL);
+ if (then_expr == NULL) {
+ st->missing_delim = delim;
+ return AMR_CLEANUP;
+ }
- st->termc = *--st->cp;
- st->delim = '\0';
+ delim = st->endc; /* BRCLOSE or PRCLOSE */
+ else_expr = ParseModifierPart(pp, delim, else_eflags, st->ctxt,
+ NULL, NULL, NULL);
+ if (else_expr == NULL) {
+ st->missing_delim = delim;
+ return AMR_CLEANUP;
+ }
+
+ (*pp)--;
if (cond_rc == COND_INVALID) {
Error("Bad conditional expression `%s' in %s?%s:%s",
- st->v->name, st->v->name, pattern.lhs, pattern.rhs);
- return FALSE;
+ st->v->name, st->v->name, then_expr, else_expr);
+ return AMR_CLEANUP;
}
if (value) {
- st->newStr = UNCONST(pattern.lhs);
- free(UNCONST(pattern.rhs));
+ st->newVal = then_expr;
+ free(else_expr);
} else {
- st->newStr = UNCONST(pattern.rhs);
- free(UNCONST(pattern.lhs));
+ st->newVal = else_expr;
+ free(then_expr);
}
if (st->v->flags & VAR_JUNK)
st->v->flags |= VAR_KEEP;
- return TRUE;
+ return AMR_OK;
}
-/* "::=", "::!=", "::+=", or "::?=" */
-static int
-ApplyModifier_Assign(ApplyModifiersState *st)
+/*
+ * The ::= modifiers actually assign a value to the variable.
+ * Their main purpose is in supporting modifiers of .for loop
+ * iterators and other obscure uses. They always expand to
+ * nothing. In a target rule that would otherwise expand to an
+ * empty line they can be preceded with @: to keep make happy.
+ * Eg.
+ *
+ * foo: .USE
+ * .for i in ${.TARGET} ${.TARGET:R}.gz
+ * @: ${t::=$i}
+ * @echo blah ${t:T}
+ * .endfor
+ *
+ * ::=<str> Assigns <str> as the new value of variable.
+ * ::?=<str> Assigns <str> as value of variable if
+ * it was not already set.
+ * ::+=<str> Appends <str> to variable.
+ * ::!=<cmd> Assigns output of <cmd> as the new value of
+ * variable.
+ */
+static ApplyModifierResult
+ApplyModifier_Assign(const char **pp, ApplyModifiersState *st)
{
- if (st->tstr[1] == '=' ||
- (st->tstr[2] == '=' &&
- (st->tstr[1] == '!' || st->tstr[1] == '+' || st->tstr[1] == '?'))) {
- GNode *v_ctxt; /* context where v belongs */
- const char *emsg;
- char *sv_name;
- VarPattern pattern;
- int how;
- VarPattern_Flags vflags;
-
- if (st->v->name[0] == 0)
- return 'b';
-
- v_ctxt = st->ctxt;
- sv_name = NULL;
- ++st->tstr;
- if (st->v->flags & VAR_JUNK) {
- /*
- * We need to bmake_strdup() it incase
- * VarGetPattern() recurses.
- */
- sv_name = st->v->name;
- st->v->name = bmake_strdup(st->v->name);
- } else if (st->ctxt != VAR_GLOBAL) {
- Var *gv = VarFind(st->v->name, st->ctxt, 0);
- if (gv == NULL)
- v_ctxt = VAR_GLOBAL;
- else
- VarFreeEnv(gv, TRUE);
- }
+ GNode *v_ctxt;
+ char *sv_name;
+ char delim;
+ char *val;
+
+ const char *mod = *pp;
+ const char *op = mod + 1;
+ if (!(op[0] == '=' ||
+ (op[1] == '=' &&
+ (op[0] == '!' || op[0] == '+' || op[0] == '?'))))
+ return AMR_UNKNOWN; /* "::<unrecognised>" */
+
+
+ if (st->v->name[0] == 0) {
+ *pp = mod + 1;
+ return AMR_BAD;
+ }
+
+ v_ctxt = st->ctxt; /* context where v belongs */
+ sv_name = NULL;
+ if (st->v->flags & VAR_JUNK) {
+ /*
+ * We need to bmake_strdup() it in case ParseModifierPart() recurses.
+ */
+ sv_name = st->v->name;
+ st->v->name = bmake_strdup(st->v->name);
+ } else if (st->ctxt != VAR_GLOBAL) {
+ Var *gv = VarFind(st->v->name, st->ctxt, 0);
+ if (gv == NULL)
+ v_ctxt = VAR_GLOBAL;
+ else
+ VarFreeEnv(gv, TRUE);
+ }
+
+ switch (op[0]) {
+ case '+':
+ case '?':
+ case '!':
+ *pp = mod + 3;
+ break;
+ default:
+ *pp = mod + 2;
+ break;
+ }
- switch ((how = *st->tstr)) {
+ delim = st->startc == PROPEN ? PRCLOSE : BRCLOSE;
+ val = ParseModifierPart(pp, delim, st->eflags, st->ctxt, NULL, NULL, NULL);
+ if (st->v->flags & VAR_JUNK) {
+ /* restore original name */
+ free(st->v->name);
+ st->v->name = sv_name;
+ }
+ if (val == NULL) {
+ st->missing_delim = delim;
+ return AMR_CLEANUP;
+ }
+
+ (*pp)--;
+
+ if (st->eflags & VARE_WANTRES) {
+ switch (op[0]) {
case '+':
- case '?':
- case '!':
- st->cp = &st->tstr[2];
+ Var_Append(st->v->name, val, v_ctxt);
break;
- default:
- st->cp = ++st->tstr;
+ case '!': {
+ const char *errfmt;
+ char *cmd_output = Cmd_Exec(val, &errfmt);
+ if (errfmt)
+ Error(errfmt, val);
+ else
+ Var_Set(st->v->name, cmd_output, v_ctxt);
+ free(cmd_output);
break;
}
- st->delim = st->startc == PROPEN ? PRCLOSE : BRCLOSE;
- pattern.flags = 0;
-
- vflags = (st->flags & VARF_WANTRES) ? 0 : VAR_NOSUBST;
- pattern.rhs = VarGetPattern(
- st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim,
- &vflags, &pattern.rightLen, NULL);
- if (st->v->flags & VAR_JUNK) {
- /* restore original name */
- free(st->v->name);
- st->v->name = sv_name;
- }
- if (pattern.rhs == NULL)
- return 'c';
-
- st->termc = *--st->cp;
- st->delim = '\0';
-
- if (st->flags & VARF_WANTRES) {
- switch (how) {
- case '+':
- Var_Append(st->v->name, pattern.rhs, v_ctxt);
- break;
- case '!':
- st->newStr = Cmd_Exec(pattern.rhs, &emsg);
- if (emsg)
- Error(emsg, st->nstr);
- else
- Var_Set(st->v->name, st->newStr, v_ctxt);
- free(st->newStr);
- break;
- case '?':
- if ((st->v->flags & VAR_JUNK) == 0)
- break;
- /* FALLTHROUGH */
- default:
- Var_Set(st->v->name, pattern.rhs, v_ctxt);
+ case '?':
+ if (!(st->v->flags & VAR_JUNK))
break;
- }
+ /* FALLTHROUGH */
+ default:
+ Var_Set(st->v->name, val, v_ctxt);
+ break;
}
- free(UNCONST(pattern.rhs));
- st->newStr = varNoError;
- return 0;
}
- return 'd'; /* "::<unrecognised>" */
+ free(val);
+ st->newVal = varNoError; /* XXX: varNoError is kind of an error,
+ * the intention here is to just return
+ * an empty string. */
+ return AMR_OK;
}
/* remember current value */
-static Boolean
-ApplyModifier_Remember(ApplyModifiersState *st)
+static ApplyModifierResult
+ApplyModifier_Remember(const char **pp, ApplyModifiersState *st)
{
- st->cp = st->tstr + 1; /* make sure it is set */
- if (!STRMOD_MATCHX(st->tstr, "_", 1))
- return FALSE;
-
- if (st->tstr[1] == '=') {
- char *np;
- int n;
-
- st->cp++;
- n = strcspn(st->cp, ":)}");
- np = bmake_strndup(st->cp, n + 1);
- np[n] = '\0';
- st->cp = st->tstr + 2 + n;
- Var_Set(np, st->nstr, st->ctxt);
- free(np);
+ const char *mod = *pp;
+ if (!ModMatchEq(mod, "_", st->endc))
+ return AMR_UNKNOWN;
+
+ if (mod[1] == '=') {
+ size_t n = strcspn(mod + 2, ":)}");
+ char *name = bmake_strldup(mod + 2, n);
+ Var_Set(name, st->val, st->ctxt);
+ free(name);
+ *pp = mod + 2 + n;
} else {
- Var_Set("_", st->nstr, st->ctxt);
+ Var_Set("_", st->val, st->ctxt);
+ *pp = mod + 1;
}
- st->newStr = st->nstr;
- st->termc = *st->cp;
- return TRUE;
+ st->newVal = st->val;
+ return AMR_OK;
+}
+
+/* Apply the given function to each word of the variable value. */
+static ApplyModifierResult
+ApplyModifier_WordFunc(const char **pp, ApplyModifiersState *st,
+ ModifyWordsCallback modifyWord)
+{
+ char delim = (*pp)[1];
+ if (delim != st->endc && delim != ':')
+ return AMR_UNKNOWN;
+
+ st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord,
+ st->val, modifyWord, NULL);
+ (*pp)++;
+ return AMR_OK;
}
#ifdef SYSVVARSUB
/* :from=to */
-static int
-ApplyModifier_SysV(ApplyModifiersState *st)
+static ApplyModifierResult
+ApplyModifier_SysV(const char **pp, ApplyModifiersState *st)
{
- /*
- * This can either be a bogus modifier or a System-V
- * substitution command.
- */
- VarPattern pattern;
- Boolean eqFound = FALSE;
+ char delim;
+ char *lhs, *rhs;
- pattern.flags = 0;
+ const char *mod = *pp;
+ Boolean eqFound = FALSE;
/*
* First we make a pass through the string trying
* to verify it is a SYSV-make-style translation:
* it must be: <string1>=<string2>)
*/
- st->cp = st->tstr;
- st->cnt = 1;
- while (*st->cp != '\0' && st->cnt) {
- if (*st->cp == '=') {
+ int nest = 1;
+ const char *next = mod;
+ while (*next != '\0' && nest > 0) {
+ if (*next == '=') {
eqFound = TRUE;
/* continue looking for st->endc */
- } else if (*st->cp == st->endc)
- st->cnt--;
- else if (*st->cp == st->startc)
- st->cnt++;
- if (st->cnt)
- st->cp++;
+ } else if (*next == st->endc)
+ nest--;
+ else if (*next == st->startc)
+ nest++;
+ if (nest > 0)
+ next++;
+ }
+ if (*next != st->endc || !eqFound)
+ return AMR_UNKNOWN;
+
+ delim = '=';
+ *pp = mod;
+ lhs = ParseModifierPart(pp, delim, st->eflags, st->ctxt, NULL, NULL, NULL);
+ if (lhs == NULL) {
+ st->missing_delim = delim;
+ return AMR_CLEANUP;
+ }
+
+ delim = st->endc;
+ rhs = ParseModifierPart(pp, delim, st->eflags, st->ctxt, NULL, NULL, NULL);
+ if (rhs == NULL) {
+ st->missing_delim = delim;
+ return AMR_CLEANUP;
}
- if (*st->cp != st->endc || !eqFound)
- return 0;
-
- st->delim = '=';
- st->cp = st->tstr;
- pattern.lhs = VarGetPattern(
- st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim,
- &pattern.flags, &pattern.leftLen, NULL);
- if (pattern.lhs == NULL)
- return 'c';
-
- st->delim = st->endc;
- pattern.rhs = VarGetPattern(
- st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim,
- NULL, &pattern.rightLen, &pattern);
- if (pattern.rhs == NULL)
- return 'c';
/*
* SYSV modifications happen through the whole
* string. Note the pattern is anchored at the end.
*/
- st->termc = *--st->cp;
- st->delim = '\0';
- if (pattern.leftLen == 0 && *st->nstr == '\0') {
- st->newStr = st->nstr; /* special case */
+ (*pp)--;
+ if (lhs[0] == '\0' && *st->val == '\0') {
+ st->newVal = st->val; /* special case */
} else {
- st->newStr = VarModify(
- st->ctxt, &st->parsestate, st->nstr, VarSYSVMatch, &pattern);
+ ModifyWord_SYSVSubstArgs args = {st->ctxt, lhs, rhs};
+ st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord, st->val,
+ ModifyWord_SYSVSubst, &args);
}
- free(UNCONST(pattern.lhs));
- free(UNCONST(pattern.rhs));
- return '=';
+ free(lhs);
+ free(rhs);
+ return AMR_OK;
}
#endif
-/*
- * Now we need to apply any modifiers the user wants applied.
- * These are:
- * :M<pattern> words which match the given <pattern>.
- * <pattern> is of the standard file
- * wildcarding form.
- * :N<pattern> words which do not match the given <pattern>.
- * :S<d><pat1><d><pat2><d>[1gW]
- * Substitute <pat2> for <pat1> in the value
- * :C<d><pat1><d><pat2><d>[1gW]
- * Substitute <pat2> for regex <pat1> in the value
- * :H Substitute the head of each word
- * :T Substitute the tail of each word
- * :E Substitute the extension (minus '.') of
- * each word
- * :R Substitute the root of each word
- * (pathname minus the suffix).
- * :O ("Order") Alphabeticaly sort words in variable.
- * :Ox ("intermiX") Randomize words in variable.
- * :u ("uniq") Remove adjacent duplicate words.
- * :tu Converts the variable contents to uppercase.
- * :tl Converts the variable contents to lowercase.
- * :ts[c] Sets varSpace - the char used to
- * separate words to 'c'. If 'c' is
- * omitted then no separation is used.
- * :tW Treat the variable contents as a single
- * word, even if it contains spaces.
- * (Mnemonic: one big 'W'ord.)
- * :tw Treat the variable contents as multiple
- * space-separated words.
- * (Mnemonic: many small 'w'ords.)
- * :[index] Select a single word from the value.
- * :[start..end] Select multiple words from the value.
- * :[*] or :[0] Select the entire value, as a single
- * word. Equivalent to :tW.
- * :[@] Select the entire value, as multiple
- * words. Undoes the effect of :[*].
- * Equivalent to :tw.
- * :[#] Returns the number of words in the value.
- *
- * :?<true-value>:<false-value>
- * If the variable evaluates to true, return
- * true value, else return the second value.
- * :lhs=rhs Like :S, but the rhs goes to the end of
- * the invocation.
- * :sh Treat the current value as a command
- * to be run, new value is its output.
- * The following added so we can handle ODE makefiles.
- * :@<tmpvar>@<newval>@
- * Assign a temporary local variable <tmpvar>
- * to the current value of each word in turn
- * and replace each word with the result of
- * evaluating <newval>
- * :D<newval> Use <newval> as value if variable defined
- * :U<newval> Use <newval> as value if variable undefined
- * :L Use the name of the variable as the value.
- * :P Use the path of the node that has the same
- * name as the variable as the value. This
- * basically includes an implied :L so that
- * the common method of refering to the path
- * of your dependent 'x' in a rule is to use
- * the form '${x:P}'.
- * :!<cmd>! Run cmd much the same as :sh run's the
- * current value of the variable.
- * The ::= modifiers, actually assign a value to the variable.
- * Their main purpose is in supporting modifiers of .for loop
- * iterators and other obscure uses. They always expand to
- * nothing. In a target rule that would otherwise expand to an
- * empty line they can be preceded with @: to keep make happy.
- * Eg.
- *
- * foo: .USE
- * .for i in ${.TARGET} ${.TARGET:R}.gz
- * @: ${t::=$i}
- * @echo blah ${t:T}
- * .endfor
- *
- * ::=<str> Assigns <str> as the new value of variable.
- * ::?=<str> Assigns <str> as value of variable if
- * it was not already set.
- * ::+=<str> Appends <str> to variable.
- * ::!=<cmd> Assigns output of <cmd> as the new value of
- * variable.
- */
+/* Apply any modifiers (such as :Mpattern or :@var@loop@ or :Q or ::=value). */
static char *
-ApplyModifiers(char *nstr, const char *tstr,
- int const startc, int const endc,
- Var * const v, GNode * const ctxt, int const flags,
- int * const lengthPtr, void ** const freePtr)
-{
+ApplyModifiers(
+ const char **pp, /* the parsing position, updated upon return */
+ char *val, /* the current value of the variable */
+ char const startc, /* '(' or '{', or '\0' for indirect modifiers */
+ char const endc, /* ')' or '}', or '\0' for indirect modifiers */
+ Var * const v, /* the variable may have its flags changed */
+ GNode * const ctxt, /* for looking up and modifying variables */
+ VarEvalFlags const eflags,
+ void ** const freePtr /* free this after using the return value */
+) {
ApplyModifiersState st = {
- startc, endc, v, ctxt, flags, lengthPtr, freePtr,
- nstr, tstr, tstr, tstr,
- '\0', 0, '\0', 0, {' ', FALSE}, NULL
+ startc, endc, v, ctxt, eflags, val,
+ var_Error, /* .newVal */
+ '\0', /* .missing_delim */
+ ' ', /* .sep */
+ FALSE /* .oneBigWord */
};
+ const char *p;
+ const char *mod;
+ ApplyModifierResult res;
- while (*st.tstr && *st.tstr != st.endc) {
+ assert(startc == '(' || startc == '{' || startc == '\0');
+ assert(endc == ')' || endc == '}' || endc == '\0');
+ assert(val != NULL);
- if (*st.tstr == '$') {
+ p = *pp;
+ while (*p != '\0' && *p != endc) {
+
+ if (*p == '$') {
/*
* We may have some complex modifiers in a variable.
*/
- void *freeIt;
- char *rval;
int rlen;
- int c;
-
- rval = Var_Parse(st.tstr, st.ctxt, st.flags, &rlen, &freeIt);
+ void *freeIt;
+ const char *rval = Var_Parse(p, st.ctxt, st.eflags, &rlen, &freeIt);
/*
* If we have not parsed up to st.endc or ':',
* we are not interested.
*/
- if (rval != NULL && *rval &&
- (c = st.tstr[rlen]) != '\0' &&
- c != ':' &&
- c != st.endc) {
+ int c;
+ if (rval[0] != '\0' &&
+ (c = p[rlen]) != '\0' && c != ':' && c != st.endc) {
free(freeIt);
goto apply_mods;
}
- if (DEBUG(VAR)) {
- fprintf(debug_file, "Got '%s' from '%.*s'%.*s\n",
- rval, rlen, st.tstr, rlen, st.tstr + rlen);
- }
-
- st.tstr += rlen;
+ VAR_DEBUG("Indirect modifier \"%s\" from \"%.*s\"\n",
+ rval, rlen, p);
- if (rval != NULL && *rval) {
- int used;
+ p += rlen;
- st.nstr = ApplyModifiers(st.nstr, rval, 0, 0, st.v,
- st.ctxt, st.flags, &used, st.freePtr);
- if (st.nstr == var_Error
- || (st.nstr == varNoError && (st.flags & VARF_UNDEFERR) == 0)
- || strlen(rval) != (size_t) used) {
+ if (rval[0] != '\0') {
+ const char *rval_pp = rval;
+ st.val = ApplyModifiers(&rval_pp, st.val, '\0', '\0', v,
+ ctxt, eflags, freePtr);
+ if (st.val == var_Error
+ || (st.val == varNoError && !(st.eflags & VARE_UNDEFERR))
+ || *rval_pp != '\0') {
free(freeIt);
goto out; /* error already reported */
}
}
free(freeIt);
- if (*st.tstr == ':')
- st.tstr++;
- else if (!*st.tstr && st.endc) {
+ if (*p == ':')
+ p++;
+ else if (*p == '\0' && endc != '\0') {
Error("Unclosed variable specification after complex "
- "modifier (expecting '%c') for %s", st.endc, st.v->name);
+ "modifier (expecting '%c') for %s", st.endc, st.v->name);
goto out;
}
continue;
}
apply_mods:
+ st.newVal = var_Error; /* default value, in case of errors */
+ res = AMR_BAD; /* just a safe fallback */
+ mod = p;
+
if (DEBUG(VAR)) {
- fprintf(debug_file, "Applying[%s] :%c to \"%s\"\n", st.v->name,
- *st.tstr, st.nstr);
- }
- st.newStr = var_Error;
- switch ((st.modifier = *st.tstr)) {
+ char eflags_str[VarEvalFlags_ToStringSize];
+ char vflags_str[VarFlags_ToStringSize];
+ Boolean is_single_char = mod[0] != '\0' &&
+ (mod[1] == endc || mod[1] == ':');
+
+ /* At this point, only the first character of the modifier can
+ * be used since the end of the modifier is not yet known. */
+ VAR_DEBUG("Applying ${%s:%c%s} to \"%s\" "
+ "(eflags = %s, vflags = %s)\n",
+ st.v->name, mod[0], is_single_char ? "" : "...", st.val,
+ Enum_FlagsToString(eflags_str, sizeof eflags_str,
+ st.eflags, VarEvalFlags_ToStringSpecs),
+ Enum_FlagsToString(vflags_str, sizeof vflags_str,
+ st.v->flags, VarFlags_ToStringSpecs));
+ }
+
+ switch (*mod) {
case ':':
- {
- int res = ApplyModifier_Assign(&st);
- if (res == 'b')
- goto bad_modifier;
- if (res == 'c')
- goto cleanup;
- if (res == 'd')
- goto default_case;
- break;
- }
+ res = ApplyModifier_Assign(&p, &st);
+ break;
case '@':
- ApplyModifier_At(&st);
+ res = ApplyModifier_Loop(&p, &st);
break;
case '_':
- if (!ApplyModifier_Remember(&st))
- goto default_case;
+ res = ApplyModifier_Remember(&p, &st);
break;
case 'D':
case 'U':
- ApplyModifier_Defined(&st);
+ res = ApplyModifier_Defined(&p, &st);
break;
case 'L':
- {
- if ((st.v->flags & VAR_JUNK) != 0)
- st.v->flags |= VAR_KEEP;
- st.newStr = bmake_strdup(st.v->name);
- st.cp = ++st.tstr;
- st.termc = *st.tstr;
- break;
- }
+ if (st.v->flags & VAR_JUNK)
+ st.v->flags |= VAR_KEEP;
+ st.newVal = bmake_strdup(st.v->name);
+ p++;
+ res = AMR_OK;
+ break;
case 'P':
- ApplyModifier_Path(&st);
+ res = ApplyModifier_Path(&p, &st);
break;
case '!':
- if (!ApplyModifier_Exclam(&st))
- goto cleanup;
+ res = ApplyModifier_Exclam(&p, &st);
break;
case '[':
- {
- int res = ApplyModifier_Words(&st);
- if (res == 'b')
- goto bad_modifier;
- if (res == 'c')
- goto cleanup;
- break;
- }
+ res = ApplyModifier_Words(&p, &st);
+ break;
case 'g':
- if (!ApplyModifier_Gmtime(&st))
- goto default_case;
+ res = ApplyModifier_Gmtime(&p, &st);
break;
case 'h':
- if (!ApplyModifier_Hash(&st))
- goto default_case;
+ res = ApplyModifier_Hash(&p, &st);
break;
case 'l':
- if (!ApplyModifier_Localtime(&st))
- goto default_case;
+ res = ApplyModifier_Localtime(&p, &st);
break;
case 't':
- if (!ApplyModifier_To(&st))
- goto bad_modifier;
+ res = ApplyModifier_To(&p, &st);
break;
case 'N':
case 'M':
- ApplyModifier_Match(&st);
+ res = ApplyModifier_Match(&p, &st);
break;
case 'S':
- if (!ApplyModifier_Subst(&st))
- goto cleanup;
+ res = ApplyModifier_Subst(&p, &st);
break;
case '?':
- if (!ApplyModifier_IfElse(&st))
- goto cleanup;
+ res = ApplyModifier_IfElse(&p, &st);
break;
#ifndef NO_REGEX
case 'C':
- if (!ApplyModifier_Regex(&st))
- goto cleanup;
+ res = ApplyModifier_Regex(&p, &st);
break;
#endif
case 'q':
case 'Q':
- if (st.tstr[1] == st.endc || st.tstr[1] == ':') {
- st.newStr = VarQuote(st.nstr, st.modifier == 'q');
- st.cp = st.tstr + 1;
- st.termc = *st.cp;
- break;
- }
- goto default_case;
+ if (p[1] == st.endc || p[1] == ':') {
+ st.newVal = VarQuote(st.val, *mod == 'q');
+ p++;
+ res = AMR_OK;
+ } else
+ res = AMR_UNKNOWN;
+ break;
case 'T':
- if (st.tstr[1] == st.endc || st.tstr[1] == ':') {
- st.newStr = VarModify(st.ctxt, &st.parsestate, st.nstr, VarTail,
- NULL);
- st.cp = st.tstr + 1;
- st.termc = *st.cp;
- break;
- }
- goto default_case;
+ res = ApplyModifier_WordFunc(&p, &st, ModifyWord_Tail);
+ break;
case 'H':
- if (st.tstr[1] == st.endc || st.tstr[1] == ':') {
- st.newStr = VarModify(st.ctxt, &st.parsestate, st.nstr, VarHead,
- NULL);
- st.cp = st.tstr + 1;
- st.termc = *st.cp;
- break;
- }
- goto default_case;
+ res = ApplyModifier_WordFunc(&p, &st, ModifyWord_Head);
+ break;
case 'E':
- if (st.tstr[1] == st.endc || st.tstr[1] == ':') {
- st.newStr = VarModify(st.ctxt, &st.parsestate, st.nstr, VarSuffix,
- NULL);
- st.cp = st.tstr + 1;
- st.termc = *st.cp;
- break;
- }
- goto default_case;
+ res = ApplyModifier_WordFunc(&p, &st, ModifyWord_Suffix);
+ break;
case 'R':
- if (st.tstr[1] == st.endc || st.tstr[1] == ':') {
- st.newStr = VarModify(st.ctxt, &st.parsestate, st.nstr, VarRoot,
- NULL);
- st.cp = st.tstr + 1;
- st.termc = *st.cp;
- break;
- }
- goto default_case;
+ res = ApplyModifier_WordFunc(&p, &st, ModifyWord_Root);
+ break;
case 'r':
- if (!ApplyModifier_Range(&st))
- goto default_case;
+ res = ApplyModifier_Range(&p, &st);
break;
case 'O':
- if (!ApplyModifier_Order(&st))
- goto bad_modifier;
+ res = ApplyModifier_Order(&p, &st);
break;
case 'u':
- if (st.tstr[1] == st.endc || st.tstr[1] == ':') {
- st.newStr = VarUniq(st.nstr);
- st.cp = st.tstr + 1;
- st.termc = *st.cp;
- break;
- }
- goto default_case;
+ if (p[1] == st.endc || p[1] == ':') {
+ st.newVal = VarUniq(st.val);
+ p++;
+ res = AMR_OK;
+ } else
+ res = AMR_UNKNOWN;
+ break;
#ifdef SUNSHCMD
case 's':
- if (st.tstr[1] == 'h' && (st.tstr[2] == st.endc || st.tstr[2] == ':')) {
- const char *emsg;
- if (st.flags & VARF_WANTRES) {
- st.newStr = Cmd_Exec(st.nstr, &emsg);
- if (emsg)
- Error(emsg, st.nstr);
+ if (p[1] == 'h' && (p[2] == st.endc || p[2] == ':')) {
+ if (st.eflags & VARE_WANTRES) {
+ const char *errfmt;
+ st.newVal = Cmd_Exec(st.val, &errfmt);
+ if (errfmt)
+ Error(errfmt, st.val);
} else
- st.newStr = varNoError;
- st.cp = st.tstr + 2;
- st.termc = *st.cp;
- break;
- }
- goto default_case;
+ st.newVal = varNoError;
+ p += 2;
+ res = AMR_OK;
+ } else
+ res = AMR_UNKNOWN;
+ break;
#endif
default:
- default_case:
- {
+ res = AMR_UNKNOWN;
+ }
+
#ifdef SYSVVARSUB
- int res = ApplyModifier_SysV(&st);
- if (res == 'c')
- goto cleanup;
- if (res != '=')
+ if (res == AMR_UNKNOWN) {
+ assert(p == mod);
+ res = ApplyModifier_SysV(&p, &st);
+ }
#endif
- {
- Error("Unknown modifier '%c'", *st.tstr);
- for (st.cp = st.tstr+1;
- *st.cp != ':' && *st.cp != st.endc && *st.cp != '\0';
- st.cp++)
- continue;
- st.termc = *st.cp;
- st.newStr = var_Error;
- }
- }
+
+ if (res == AMR_UNKNOWN) {
+ Error("Unknown modifier '%c'", *mod);
+ for (p++; *p != ':' && *p != st.endc && *p != '\0'; p++)
+ continue;
+ st.newVal = var_Error;
}
+ if (res == AMR_CLEANUP)
+ goto cleanup;
+ if (res == AMR_BAD)
+ goto bad_modifier;
+
if (DEBUG(VAR)) {
- fprintf(debug_file, "Result[%s] of :%c is \"%s\"\n",
- st.v->name, st.modifier, st.newStr);
+ char eflags_str[VarEvalFlags_ToStringSize];
+ char vflags_str[VarFlags_ToStringSize];
+ const char *quot = st.newVal == var_Error ? "" : "\"";
+ const char *newVal = st.newVal == var_Error ? "error" : st.newVal;
+
+ VAR_DEBUG("Result of ${%s:%.*s} is %s%s%s "
+ "(eflags = %s, vflags = %s)\n",
+ st.v->name, (int)(p - mod), mod, quot, newVal, quot,
+ Enum_FlagsToString(eflags_str, sizeof eflags_str,
+ st.eflags, VarEvalFlags_ToStringSpecs),
+ Enum_FlagsToString(vflags_str, sizeof vflags_str,
+ st.v->flags, VarFlags_ToStringSpecs));
}
- if (st.newStr != st.nstr) {
- if (*st.freePtr) {
- free(st.nstr);
- *st.freePtr = NULL;
+ if (st.newVal != st.val) {
+ if (*freePtr) {
+ free(st.val);
+ *freePtr = NULL;
}
- st.nstr = st.newStr;
- if (st.nstr != var_Error && st.nstr != varNoError) {
- *st.freePtr = st.nstr;
+ st.val = st.newVal;
+ if (st.val != var_Error && st.val != varNoError) {
+ *freePtr = st.val;
}
}
- if (st.termc == '\0' && st.endc != '\0') {
+ if (*p == '\0' && st.endc != '\0') {
Error("Unclosed variable specification (expecting '%c') "
- "for \"%s\" (value \"%s\") modifier %c",
- st.endc, st.v->name, st.nstr, st.modifier);
- } else if (st.termc == ':') {
- st.cp++;
+ "for \"%s\" (value \"%s\") modifier %c",
+ st.endc, st.v->name, st.val, *mod);
+ } else if (*p == ':') {
+ p++;
}
- st.tstr = st.cp;
+ mod = p;
}
out:
- *st.lengthPtr = st.tstr - st.start;
- return st.nstr;
+ *pp = p;
+ assert(st.val != NULL); /* Use var_Error or varNoError instead. */
+ return st.val;
bad_modifier:
- /* "{(" */
- Error("Bad modifier `:%.*s' for %s", (int)strcspn(st.tstr, ":)}"), st.tstr,
- st.v->name);
+ Error("Bad modifier `:%.*s' for %s",
+ (int)strcspn(mod, ":)}"), mod, st.v->name);
cleanup:
- *st.lengthPtr = st.cp - st.start;
- if (st.delim != '\0')
- Error("Unclosed substitution for %s (%c missing)",
- st.v->name, st.delim);
- free(*st.freePtr);
- *st.freePtr = NULL;
+ *pp = p;
+ if (st.missing_delim != '\0')
+ Error("Unfinished modifier for %s ('%c' missing)",
+ st.v->name, st.missing_delim);
+ free(*freePtr);
+ *freePtr = NULL;
return var_Error;
}
+static Boolean
+VarIsDynamic(GNode *ctxt, const char *varname, size_t namelen)
+{
+ if ((namelen == 1 ||
+ (namelen == 2 && (varname[1] == 'F' || varname[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 (varname[0]) {
+ case '@':
+ case '%':
+ case '*':
+ case '!':
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ if ((namelen == 7 || namelen == 8) && varname[0] == '.' &&
+ isupper((unsigned char)varname[1]) &&
+ (ctxt == VAR_CMD || ctxt == VAR_GLOBAL))
+ {
+ return strcmp(varname, ".TARGET") == 0 ||
+ strcmp(varname, ".ARCHIVE") == 0 ||
+ strcmp(varname, ".PREFIX") == 0 ||
+ strcmp(varname, ".MEMBER") == 0;
+ }
+
+ return FALSE;
+}
+
/*-
*-----------------------------------------------------------------------
* Var_Parse --
- * Given the start of a variable invocation, extract the variable
- * name and find its value, then modify it according to the
- * specification.
+ * Given the start of a variable invocation (such as $v, $(VAR),
+ * ${VAR:Mpattern}), extract the variable name, possibly some
+ * modifiers and find its value by applying the modifiers to the
+ * original value.
*
* Input:
* str The string to parse
* ctxt The context for the variable
- * flags VARF_UNDEFERR if undefineds are an error
- * VARF_WANTRES if we actually want the result
- * VARF_ASSIGN if we are in a := assignment
+ * flags VARE_UNDEFERR if undefineds are an error
+ * VARE_WANTRES if we actually want the result
+ * VARE_ASSIGN if we are in a := assignment
* 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.
+ * Returns the value of the variable expression, never NULL.
+ * var_Error if there was a parse error and VARE_UNDEFERR was set.
+ * varNoError if there was a parse error and VARE_UNDEFERR was not set.
*
- * Side Effects:
- * None.
+ * Parsing should continue at str + *lengthPtr.
*
+ * After using the returned value, *freePtr must be freed, preferably
+ * using bmake_free since it is NULL in most cases.
+ *
+ * Side Effects:
+ * Any effects from the modifiers, such as :!cmd! or ::=value.
*-----------------------------------------------------------------------
*/
/* coverity[+alloc : arg-*4] */
-char *
-Var_Parse(const char *str, GNode *ctxt, Varf_Flags flags,
+const char *
+Var_Parse(const char * const str, GNode *ctxt, VarEvalFlags eflags,
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
+ char startc; /* Starting character if variable in parens
* or braces */
- char startc; /* Starting character when variable in parens
+ char endc; /* Ending character if 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 */
- const char *extramodifiers; /* extra modifiers to apply first */
- char name[2];
+ const char *extramodifiers;
+ Var *v;
+ char *nstr;
+ char eflags_str[VarEvalFlags_ToStringSize];
+
+ VAR_DEBUG("%s: %s with %s\n", __func__, str,
+ Enum_FlagsToString(eflags_str, sizeof eflags_str, eflags,
+ VarEvalFlags_ToStringSpecs));
*freePtr = NULL;
- extramodifiers = NULL;
+ extramodifiers = NULL; /* extra modifiers to apply first */
dynamic = FALSE;
- start = str;
+
+#ifdef USE_DOUBLE_BOOLEAN
+ /* Appease GCC 5.5.0, which thinks that the variable might not be
+ * initialized. */
+ endc = '\0';
+#endif
startc = str[1];
if (startc != PROPEN && startc != BROPEN) {
+ char name[2];
+
/*
* 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
@@ -3650,14 +3388,14 @@ Var_Parse(const char *str, GNode *ctxt, Varf_Flags flags,
*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 (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
@@ -3669,31 +3407,34 @@ Var_Parse(const char *str, GNode *ctxt, Varf_Flags flags,
*/
switch (str[1]) {
case '@':
- return UNCONST("$(.TARGET)");
+ return "$(.TARGET)";
case '%':
- return UNCONST("$(.MEMBER)");
+ return "$(.MEMBER)";
case '*':
- return UNCONST("$(.PREFIX)");
+ return "$(.PREFIX)";
case '!':
- return UNCONST("$(.ARCHIVE)");
+ return "$(.ARCHIVE)";
}
}
- return (flags & VARF_UNDEFERR) ? var_Error : varNoError;
+ return (eflags & VARE_UNDEFERR) ? var_Error : varNoError;
} else {
haveModifier = FALSE;
- tstr = &str[1];
- endc = str[1];
+ tstr = str + 1;
}
} else {
- Buffer buf; /* Holds the variable name */
- int depth = 1;
+ Buffer namebuf; /* Holds the variable name */
+ int depth;
+ size_t namelen;
+ char *varname;
endc = startc == PROPEN ? PRCLOSE : BRCLOSE;
- Buf_Init(&buf, 0);
+
+ Buf_Init(&namebuf, 0);
/*
* Skip to the end character or a colon, whichever comes first.
*/
+ depth = 1;
for (tstr = str + 2; *tstr != '\0'; tstr++) {
/* Track depth so we can spot parse errors. */
if (*tstr == startc)
@@ -3702,39 +3443,42 @@ Var_Parse(const char *str, GNode *ctxt, Varf_Flags flags,
if (--depth == 0)
break;
}
- if (depth == 1 && *tstr == ':')
+ if (*tstr == ':' && depth == 1)
break;
/* A variable inside a variable, expand. */
if (*tstr == '$') {
int rlen;
void *freeIt;
- char *rval = Var_Parse(tstr, ctxt, flags, &rlen, &freeIt);
- if (rval != NULL)
- Buf_AddBytes(&buf, strlen(rval), rval);
+ const char *rval = Var_Parse(tstr, ctxt, eflags, &rlen,
+ &freeIt);
+ Buf_AddStr(&namebuf, rval);
free(freeIt);
tstr += rlen - 1;
} else
- Buf_AddByte(&buf, *tstr);
+ Buf_AddByte(&namebuf, *tstr);
}
if (*tstr == ':') {
haveModifier = TRUE;
} else if (*tstr == endc) {
haveModifier = FALSE;
} else {
+ Parse_Error(PARSE_FATAL, "Unclosed variable \"%s\"",
+ Buf_GetAll(&namebuf, NULL));
/*
* 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);
+ *lengthPtr = (int)(size_t)(tstr - str);
+ Buf_Destroy(&namebuf, TRUE);
return var_Error;
}
- str = Buf_GetAll(&buf, &vlen);
+
+ varname = Buf_GetAll(&namebuf, &namelen);
/*
- * At this point, str points into newly allocated memory from
- * buf, containing only the name of the variable.
+ * At this point, varname points into newly allocated memory from
+ * namebuf, 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
@@ -3743,23 +3487,23 @@ Var_Parse(const char *str, GNode *ctxt, Varf_Flags flags,
* will be '\0', ':', PRCLOSE, or BRCLOSE.
*/
- v = VarFind(str, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD);
+ v = VarFind(varname, 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) {
+ if (v == NULL && ctxt != VAR_CMD && ctxt != VAR_GLOBAL &&
+ namelen == 2 && (varname[1] == 'F' || varname[1] == 'D') &&
+ strchr("@%?*!<>", varname[0]) != NULL)
+ {
/*
* Well, it's local -- go look for it.
*/
- name[0] = *str;
- name[1] = '\0';
+ char name[] = { varname[0], '\0' };
v = VarFind(name, ctxt, 0);
if (v != NULL) {
- if (str[1] == 'D') {
+ if (varname[1] == 'D') {
extramodifiers = "H:";
} else { /* F */
extramodifiers = "T:";
@@ -3768,55 +3512,22 @@ Var_Parse(const char *str, GNode *ctxt, Varf_Flags flags,
}
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 = 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;
- }
- }
+ dynamic = VarIsDynamic(ctxt, varname, namelen);
if (!haveModifier) {
/*
* No modifiers -- have specification length so we can return
* now.
*/
- *lengthPtr = tstr - start + 1;
+ *lengthPtr = (int)(size_t)(tstr - str) + 1;
if (dynamic) {
- char *pstr = bmake_strndup(start, *lengthPtr);
+ char *pstr = bmake_strldup(str, (size_t)*lengthPtr);
*freePtr = pstr;
- Buf_Destroy(&buf, TRUE);
+ Buf_Destroy(&namebuf, TRUE);
return pstr;
} else {
- Buf_Destroy(&buf, TRUE);
- return (flags & VARF_UNDEFERR) ? var_Error : varNoError;
+ Buf_Destroy(&namebuf, TRUE);
+ return (eflags & VARE_UNDEFERR) ? var_Error : varNoError;
}
} else {
/*
@@ -3824,13 +3535,13 @@ Var_Parse(const char *str, GNode *ctxt, Varf_Flags flags,
* so kludge up a Var structure for the modifications
*/
v = bmake_malloc(sizeof(Var));
- v->name = UNCONST(str);
+ v->name = varname;
Buf_Init(&v->val, 1);
v->flags = VAR_JUNK;
- Buf_Destroy(&buf, FALSE);
+ Buf_Destroy(&namebuf, FALSE);
}
} else
- Buf_Destroy(&buf, TRUE);
+ Buf_Destroy(&namebuf, TRUE);
}
if (v->flags & VAR_IN_USE) {
@@ -3839,6 +3550,7 @@ Var_Parse(const char *str, GNode *ctxt, Varf_Flags flags,
} 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
@@ -3849,66 +3561,64 @@ Var_Parse(const char *str, GNode *ctxt, Varf_Flags flags,
* return.
*/
nstr = Buf_GetAll(&v->val, NULL);
- if (strchr(nstr, '$') != NULL && (flags & VARF_WANTRES) != 0) {
- nstr = Var_Subst(NULL, nstr, ctxt, flags);
+ if (strchr(nstr, '$') != NULL && (eflags & VARE_WANTRES) != 0) {
+ nstr = Var_Subst(nstr, ctxt, eflags);
*freePtr = nstr;
}
- v->flags &= ~VAR_IN_USE;
+ v->flags &= ~(unsigned)VAR_IN_USE;
- if (nstr != NULL && (haveModifier || extramodifiers != NULL)) {
+ if (haveModifier || extramodifiers != NULL) {
void *extraFree;
- int used;
extraFree = NULL;
if (extramodifiers != NULL) {
- nstr = ApplyModifiers(nstr, extramodifiers, '(', ')',
- v, ctxt, flags, &used, &extraFree);
+ const char *em = extramodifiers;
+ nstr = ApplyModifiers(&em, nstr, '(', ')',
+ v, ctxt, eflags, &extraFree);
}
if (haveModifier) {
/* Skip initial colon. */
tstr++;
- nstr = ApplyModifiers(nstr, tstr, startc, endc,
- v, ctxt, flags, &used, freePtr);
- tstr += used;
+ nstr = ApplyModifiers(&tstr, nstr, startc, endc,
+ v, ctxt, eflags, freePtr);
free(extraFree);
} else {
*freePtr = extraFree;
}
}
- *lengthPtr = tstr - start + (*tstr ? 1 : 0);
- if (v->flags & VAR_FROM_ENV) {
- Boolean destroy = FALSE;
+ /* Skip past endc if possible. */
+ *lengthPtr = (int)(size_t)(tstr + (*tstr ? 1 : 0) - str);
- if (nstr != Buf_GetAll(&v->val, NULL)) {
- destroy = TRUE;
- } else {
+ if (v->flags & VAR_FROM_ENV) {
+ Boolean destroy = nstr != Buf_GetAll(&v->val, NULL);
+ if (!destroy) {
/*
* Returning the value unmodified, so tell the caller to free
* the thing.
*/
*freePtr = nstr;
}
- VarFreeEnv(v, destroy);
+ (void)VarFreeEnv(v, destroy);
} else if (v->flags & VAR_JUNK) {
/*
- * Perform any free'ing needed and set *freePtr to NULL so the caller
+ * Perform any freeing 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 VAR_KEEP is also set then we want to keep str(?) as is.
*/
if (!(v->flags & VAR_KEEP)) {
- if (*freePtr) {
- free(nstr);
+ if (*freePtr != NULL) {
+ free(*freePtr);
*freePtr = NULL;
}
if (dynamic) {
- nstr = bmake_strndup(start, *lengthPtr);
+ nstr = bmake_strldup(str, (size_t)*lengthPtr);
*freePtr = nstr;
} else {
- nstr = (flags & VARF_UNDEFERR) ? var_Error : varNoError;
+ nstr = (eflags & VARE_UNDEFERR) ? var_Error : varNoError;
}
}
if (nstr != Buf_GetAll(&v->val, NULL))
@@ -3919,58 +3629,52 @@ Var_Parse(const char *str, GNode *ctxt, Varf_Flags flags,
return nstr;
}
-/*-
- *-----------------------------------------------------------------------
- * Var_Subst --
- * Substitute for all variables in the given string in the given context.
- * If flags & VARF_UNDEFERR, Parse_Error will be called when an undefined
- * variable is encountered.
+/* Substitute for all variables in the given string in the given context.
+ *
+ * If eflags & VARE_UNDEFERR, Parse_Error will be called when an undefined
+ * variable is encountered.
+ *
+ * If eflags & VARE_WANTRES, any effects from the modifiers, such as ::=,
+ * :sh or !cmd! take place.
*
* Input:
- * var Named variable || NULL for all
* str the string which to substitute
* ctxt the context wherein to find variables
- * flags VARF_UNDEFERR if undefineds are an error
- * VARF_WANTRES if we actually want the result
- * VARF_ASSIGN if we are in a := assignment
+ * eflags VARE_UNDEFERR if undefineds are an error
+ * VARE_WANTRES if we actually want the result
+ * VARE_ASSIGN if we are in a := assignment
*
* Results:
* The resulting string.
- *
- * Side Effects:
- * None.
- *-----------------------------------------------------------------------
*/
char *
-Var_Subst(const char *var, const char *str, GNode *ctxt, Varf_Flags flags)
+Var_Subst(const char *str, GNode *ctxt, VarEvalFlags eflags)
{
- 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 */
+ Buffer buf; /* Buffer for forming things */
+ Boolean trailingBslash;
+
+ /* Set true if an error has already been reported,
+ * to prevent a plethora of messages when recursing */
+ static Boolean errorReported;
Buf_Init(&buf, 0);
errorReported = FALSE;
- trailingBslash = FALSE;
+ trailingBslash = FALSE; /* variable ends in \ */
while (*str) {
if (*str == '\n' && trailingBslash)
Buf_AddByte(&buf, ' ');
- if (var == NULL && (*str == '$') && (str[1] == '$')) {
+
+ if (*str == '$' && str[1] == '$') {
/*
- * A dollar sign may be escaped either with another dollar sign.
+ * A dollar sign may be escaped with another dollar sign.
* In such a case, we skip over the escape character and store the
* dollar sign into the buffer directly.
*/
- if (save_dollars && (flags & VARF_ASSIGN))
- Buf_AddByte(&buf, *str);
- str++;
- Buf_AddByte(&buf, *str);
- str++;
+ if (save_dollars && (eflags & VARE_ASSIGN))
+ Buf_AddByte(&buf, '$');
+ Buf_AddByte(&buf, '$');
+ str += 2;
} else if (*str != '$') {
/*
* Skip as many characters as possible -- either to the end of
@@ -3980,71 +3684,12 @@ Var_Subst(const char *var, const char *str, GNode *ctxt, Varf_Flags flags)
for (cp = str++; *str != '$' && *str != '\0'; str++)
continue;
- Buf_AddBytes(&buf, str - cp, cp);
+ Buf_AddBytesBetween(&buf, cp, str);
} 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, flags, &length, &freeIt);
+ int length;
+ void *freeIt;
+ const char *val = Var_Parse(str, ctxt, eflags, &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
@@ -4054,7 +3699,7 @@ Var_Subst(const char *var, const char *str, GNode *ctxt, Varf_Flags flags)
*/
if (oldVars) {
str += length;
- } else if ((flags & VARF_UNDEFERR) || val == var_Error) {
+ } else if ((eflags & VARE_UNDEFERR) || val == var_Error) {
/*
* If variable is undefined, complain and skip the
* variable. The complaint will stop us from doing anything
@@ -4071,19 +3716,13 @@ Var_Subst(const char *var, const char *str, GNode *ctxt, Varf_Flags flags)
str += 1;
}
} else {
- /*
- * We've now got a variable structure to store in. But first,
- * advance the string pointer.
- */
+ size_t val_len;
+
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] == '\\';
+ val_len = strlen(val);
+ Buf_AddBytes(&buf, val, val_len);
+ trailingBslash = val_len > 0 && val[val_len - 1] == '\\';
}
free(freeIt);
freeIt = NULL;
@@ -4106,6 +3745,13 @@ Var_Init(void)
void
Var_End(void)
{
+ Var_Stats();
+}
+
+void
+Var_Stats(void)
+{
+ Hash_DebugStats(&VAR_GLOBAL->context, "VAR_GLOBAL");
}
@@ -4117,12 +3763,7 @@ VarPrintVar(void *vp, void *data MAKE_ATTR_UNUSED)
fprintf(debug_file, "%-16s = %s\n", v->name, Buf_GetAll(&v->val, NULL));
}
-/*-
- *-----------------------------------------------------------------------
- * Var_Dump --
- * print all variables in a context
- *-----------------------------------------------------------------------
- */
+/* Print all variables in a context, unordered. */
void
Var_Dump(GNode *ctxt)
{