summaryrefslogtreecommitdiff
path: root/jam-files/engine
diff options
context:
space:
mode:
Diffstat (limited to 'jam-files/engine')
-rw-r--r--jam-files/engine/Jambase2473
-rw-r--r--jam-files/engine/boost-jam.spec64
-rw-r--r--jam-files/engine/boost-no-inspect1
-rw-r--r--jam-files/engine/build.bat532
-rw-r--r--jam-files/engine/build.jam1070
-rwxr-xr-xjam-files/engine/build.sh303
-rw-r--r--jam-files/engine/build_vms.com105
-rw-r--r--jam-files/engine/builtins.c2310
-rw-r--r--jam-files/engine/builtins.h69
-rw-r--r--jam-files/engine/bump_version.py80
-rw-r--r--jam-files/engine/class.c141
-rw-r--r--jam-files/engine/class.h13
-rw-r--r--jam-files/engine/command.c100
-rw-r--r--jam-files/engine/command.h61
-rw-r--r--jam-files/engine/compile.c1424
-rw-r--r--jam-files/engine/compile.h82
-rw-r--r--jam-files/engine/debian/changelog72
-rw-r--r--jam-files/engine/debian/control16
-rw-r--r--jam-files/engine/debian/copyright25
-rw-r--r--jam-files/engine/debian/jam.man.sgml236
-rwxr-xr-xjam-files/engine/debian/rules73
-rw-r--r--jam-files/engine/debug.c132
-rw-r--r--jam-files/engine/debug.h54
-rw-r--r--jam-files/engine/execcmd.h45
-rw-r--r--jam-files/engine/execmac.c69
-rw-r--r--jam-files/engine/execnt.c1296
-rw-r--r--jam-files/engine/execunix.c569
-rw-r--r--jam-files/engine/execvms.c161
-rw-r--r--jam-files/engine/expand.c733
-rw-r--r--jam-files/engine/expand.h14
-rw-r--r--jam-files/engine/filemac.c175
-rw-r--r--jam-files/engine/filent.c387
-rw-r--r--jam-files/engine/fileos2.c138
-rw-r--r--jam-files/engine/filesys.c83
-rw-r--r--jam-files/engine/filesys.h60
-rw-r--r--jam-files/engine/fileunix.c501
-rw-r--r--jam-files/engine/filevms.c327
-rw-r--r--jam-files/engine/frames.c22
-rw-r--r--jam-files/engine/frames.h37
-rw-r--r--jam-files/engine/glob.c152
-rw-r--r--jam-files/engine/hash.c459
-rw-r--r--jam-files/engine/hash.h25
-rw-r--r--jam-files/engine/hcache.c434
-rw-r--r--jam-files/engine/hcache.h18
-rw-r--r--jam-files/engine/hdrmacro.c137
-rw-r--r--jam-files/engine/hdrmacro.h14
-rw-r--r--jam-files/engine/headers.c203
-rw-r--r--jam-files/engine/headers.h16
-rw-r--r--jam-files/engine/jam.c632
-rw-r--r--jam-files/engine/jam.h579
-rw-r--r--jam-files/engine/jambase.c1691
-rw-r--r--jam-files/engine/jambase.h15
-rw-r--r--jam-files/engine/jamgram.c1830
-rw-r--r--jam-files/engine/jamgram.h140
-rw-r--r--jam-files/engine/jamgram.y371
-rw-r--r--jam-files/engine/jamgram.yy329
-rw-r--r--jam-files/engine/jamgramtab.h44
-rw-r--r--jam-files/engine/lists.c339
-rw-r--r--jam-files/engine/lists.h108
-rw-r--r--jam-files/engine/make.c814
-rw-r--r--jam-files/engine/make.h41
-rw-r--r--jam-files/engine/make1.c1145
-rw-r--r--jam-files/engine/md5.c381
-rw-r--r--jam-files/engine/md5.h91
-rw-r--r--jam-files/engine/mem.c75
-rw-r--r--jam-files/engine/mem.h134
-rw-r--r--jam-files/engine/mkjambase.c123
-rw-r--r--jam-files/engine/modules.c168
-rw-r--r--jam-files/engine/modules.h37
-rw-r--r--jam-files/engine/modules/order.c144
-rw-r--r--jam-files/engine/modules/path.c32
-rw-r--r--jam-files/engine/modules/property-set.c110
-rw-r--r--jam-files/engine/modules/readme.txt3
-rw-r--r--jam-files/engine/modules/regex.c96
-rw-r--r--jam-files/engine/modules/sequence.c42
-rw-r--r--jam-files/engine/modules/set.c41
-rw-r--r--jam-files/engine/native.c36
-rw-r--r--jam-files/engine/native.h34
-rw-r--r--jam-files/engine/newstr.c174
-rw-r--r--jam-files/engine/newstr.h14
-rw-r--r--jam-files/engine/option.c94
-rw-r--r--jam-files/engine/option.h23
-rw-r--r--jam-files/engine/output.c125
-rw-r--r--jam-files/engine/output.h29
-rw-r--r--jam-files/engine/parse.c132
-rw-r--r--jam-files/engine/parse.h59
-rw-r--r--jam-files/engine/patchlevel.h17
-rw-r--r--jam-files/engine/pathmac.c252
-rw-r--r--jam-files/engine/pathsys.h91
-rw-r--r--jam-files/engine/pathunix.c457
-rw-r--r--jam-files/engine/pathvms.c406
-rw-r--r--jam-files/engine/pwd.c66
-rw-r--r--jam-files/engine/pwd.h10
-rw-r--r--jam-files/engine/regexp.c1328
-rw-r--r--jam-files/engine/regexp.h32
-rw-r--r--jam-files/engine/rules.c810
-rw-r--r--jam-files/engine/rules.h280
-rw-r--r--jam-files/engine/scan.c418
-rw-r--r--jam-files/engine/scan.h56
-rw-r--r--jam-files/engine/search.c223
-rw-r--r--jam-files/engine/search.h11
-rw-r--r--jam-files/engine/strings.c201
-rw-r--r--jam-files/engine/strings.h34
-rw-r--r--jam-files/engine/subst.c94
-rw-r--r--jam-files/engine/timestamp.c226
-rw-r--r--jam-files/engine/timestamp.h12
-rw-r--r--jam-files/engine/variable.c631
-rw-r--r--jam-files/engine/variable.h35
-rw-r--r--jam-files/engine/w32_getreg.c207
-rw-r--r--jam-files/engine/yyacc.c268
110 files changed, 31951 insertions, 0 deletions
diff --git a/jam-files/engine/Jambase b/jam-files/engine/Jambase
new file mode 100644
index 00000000..94f8fbde
--- /dev/null
+++ b/jam-files/engine/Jambase
@@ -0,0 +1,2473 @@
+#
+# /+\
+# +\ Copyright 1993, 2000 Christopher Seiwald.
+# \+/
+#
+# This file is part of Jam - see jam.c for Copyright information.
+#
+
+# This file is ALSO:
+# Copyright 2001-2004 David Abrahams.
+# Copyright 2002-2004 Rene Rivera.
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+
+if $(NT)
+{
+ SLASH ?= \\ ;
+}
+SLASH ?= / ;
+
+
+# Glob for patterns in the directories starting from the given start directory,
+# up to and including the root of the file-system. We stop globbing as soon as
+# we find at least one match.
+#
+rule find-to-root ( dir : patterns + )
+{
+ local globs = [ GLOB $(dir) : $(patterns) ] ;
+ while ! $(globs) && $(dir:P) != $(dir)
+ {
+ dir = $(dir:P) ;
+ globs = [ GLOB $(dir) : $(patterns) ] ;
+ }
+ return $(globs) ;
+}
+
+
+# This global will hold the location of the user's boost-build.jam file.
+.boost-build-file = ;
+
+# This global will hold the location of the build system bootstrap file.
+.bootstrap-file = ;
+
+# Remember the value of $(BOOST_BUILD_PATH) supplied to us by the user.
+BOOST_BUILD_PATH.user-value = $(BOOST_BUILD_PATH) ;
+
+# On Unix only, when BOOST_BUILD_PATH is not supplied by the user, set it to a
+# sensible default value. This allows Boost.Build to work without any
+# environment variables, which is good in itself and also required by the Debian
+# Policy.
+if ! $(BOOST_BUILD_PATH) && $(UNIX)
+{
+ BOOST_BUILD_PATH = /usr/share/boost-build ;
+}
+
+
+rule _poke ( module-name ? : variables + : value * )
+{
+ module $(<)
+ {
+ $(>) = $(3) ;
+ }
+}
+
+
+# This rule can be invoked from an optional user's boost-build.jam file to both
+# indicate where to find the build system files, and to load them. The path
+# indicated is relative to the location of the boost-build.jam file.
+#
+rule boost-build ( dir ? )
+{
+ if $(.bootstrap-file)
+ {
+ ECHO "Error: Illegal attempt to re-bootstrap the build system by invoking" ;
+ ECHO ;
+ ECHO " 'boost-build" $(dir) ";'" ;
+ ECHO ;
+ EXIT "Please consult the documentation at 'http://www.boost.org'." ;
+ }
+
+ # Add the given directory to the path so we can find the build system. If
+ # dir is empty, has no effect.
+ BOOST_BUILD_PATH = $(dir:R=$(.boost-build-file:D)) $(BOOST_BUILD_PATH) ;
+
+ # We might have just modified the *global* value of BOOST_BUILD_PATH. The
+ # code that loads the rest of Boost.Build, in particular the site-config.jam
+ # and user-config.jam configuration files uses os.environ, so we need to
+ # update the value there.
+ _poke .ENVIRON : BOOST_BUILD_PATH : $(BOOST_BUILD_PATH) ;
+
+ # Try to find the build system bootstrap file 'bootstrap.jam'.
+ local bootstrap-file = [ GLOB $(BOOST_BUILD_PATH) : bootstrap.jam ] ;
+ .bootstrap-file = $(bootstrap-file[1]) ;
+
+ # There is no bootstrap.jam we can find, exit with an error.
+ if ! $(.bootstrap-file)
+ {
+ ECHO "Unable to load Boost.Build: could not find build system." ;
+ ECHO --------------------------------------------------------- ;
+ ECHO "$(.boost-build-file) attempted to load the build system by invoking" ;
+ ECHO ;
+ ECHO " 'boost-build" $(dir) ";'" ;
+ ECHO ;
+ ECHO "but we were unable to find \"bootstrap.jam\" in the specified directory" ;
+ ECHO "or in BOOST_BUILD_PATH (searching "$(BOOST_BUILD_PATH:J=", ")")." ;
+ ECHO ;
+ EXIT "Please consult the documentation at 'http://www.boost.org'." ;
+ }
+
+ if [ MATCH .*(--debug-configuration).* : $(ARGV) ]
+ {
+ ECHO "notice: loading Boost.Build from"
+ [ NORMALIZE_PATH $(.bootstrap-file:D) ] ;
+ }
+
+ # Load the build system, now that we know where to start from.
+ include $(.bootstrap-file) ;
+}
+
+
+if [ MATCH .*(b2).* : $(ARGV[1]:BL) ]
+ || [ MATCH .*(bjam).* : $(ARGV[1]:BL) ]
+ || $(BOOST_ROOT) # A temporary measure so Jam works with Boost.Build v1.
+{
+ # We attempt to load "boost-build.jam" by searching from the current
+ # invocation directory up to the root of the file-system.
+ #
+ # boost-build.jam is expected to invoke the "boost-build" rule to load the
+ # Boost.Build files.
+
+ local search-path = $(BOOST_BUILD_PATH) $(BOOST_ROOT) ;
+ local self = [ SELF_PATH ] ;
+ local boost-build-relative = ../../share/boost-build ;
+ local self-based-path = [ NORMALIZE_PATH $(boost-build-relative:R=$(self)) ] ;
+
+ local boost-build-files =
+ [ find-to-root [ PWD ] : boost-build.jam ]
+ [ GLOB $(self-based-path) : boost-build.jam ]
+ # Another temporary measure so Jam works with Boost.Build v1.
+ [ GLOB $(search-path) : boost-build.jam ] ;
+
+ .boost-build-file = $(boost-build-files[1]) ;
+
+ # There is no boost-build.jam we can find, exit with an error, and
+ # information.
+ if ! $(.boost-build-file)
+ {
+ ECHO "Unable to load Boost.Build: could not find \"boost-build.jam\"" ;
+ ECHO --------------------------------------------------------------- ;
+
+ if ! [ MATCH .*(bjam).* : $(ARGV[1]:BL) ]
+ {
+ ECHO "BOOST_ROOT must be set, either in the environment, or " ;
+ ECHO "on the command-line with -sBOOST_ROOT=..., to the root" ;
+ ECHO "of the boost installation." ;
+ ECHO ;
+ }
+
+ ECHO "Attempted search from" [ PWD ] "up to the root" ;
+ ECHO "at" $(self-based-path) ;
+ ECHO "and in these directories from BOOST_BUILD_PATH and BOOST_ROOT: "$(search-path:J=", ")"." ;
+ EXIT "Please consult the documentation at 'http://www.boost.org'." ;
+ }
+
+ if [ MATCH .*(--debug-configuration).* : $(ARGV) ]
+ {
+ ECHO "notice: found boost-build.jam at"
+ [ NORMALIZE_PATH $(.boost-build-file) ] ;
+ }
+
+ # Now load the boost-build.jam to get the build system loaded. This
+ # incidentaly loads the users jamfile and attempts to build targets.
+ #
+ # We also set it up so we can tell whether we are loading the new V2 system
+ # or the the old V1 system.
+ include $(.boost-build-file) ;
+
+ # Check that, at minimum, the bootstrap file was found.
+ if ! $(.bootstrap-file)
+ {
+ ECHO "Unable to load Boost.Build" ;
+ ECHO -------------------------- ;
+ ECHO "\"$(.boost-build-file)\" was found by searching from" [ PWD ] "up to the root" ;
+ ECHO "and in these directories from BOOST_BUILD_PATH and BOOST_ROOT: "$(search-path:J=", ")"." ;
+ ECHO ;
+ ECHO "However, it failed to call the \"boost-build\" rule to indicate" ;
+ ECHO "the location of the build system." ;
+ ECHO ;
+ EXIT "Please consult the documentation at 'http://www.boost.org'." ;
+ }
+}
+else
+{
+
+#
+# JAMBASE - jam 2.3 ruleset providing make(1)-like functionality
+#
+# Supports UNIX, NT, and VMS.
+#
+# 12/27/93 (seiwald) - purturb library sources with SOURCE_GRIST
+# 04/18/94 (seiwald) - use '?=' when setting OS specific vars
+# 04/21/94 (seiwald) - do RmTemps together
+# 05/05/94 (seiwald) - all supported C compilers support -o: relegate
+# RELOCATE as an option; set Ranlib to "" to disable it
+# 06/01/94 (seiwald) - new 'actions existing' to do existing sources
+# 08/25/94 (seiwald) - new ObjectCcFlags rule to append to per-target CCFLAGS
+# 08/29/94 (seiwald) - new ObjectHdrs rule to append to per-target HDRS
+# 09/19/94 (seiwald) - LinkLibraries and Undefs now append
+# - Rule names downshifted.
+# 10/06/94 (seiwald) - Dumb yyacc stuff moved into Jamfile.
+# 10/14/94 (seiwald) - (Crude) support for .s, .C, .cc, .cpp, and .f files.
+# 01/08/95 (seiwald) - Shell now handled with awk, not sed
+# 01/09/95 (seiwald) - Install* now take dest directory as target
+# 01/10/95 (seiwald) - All entries sorted.
+# 01/10/95 (seiwald) - NT support moved in, with LauraW's help.
+# 01/10/95 (seiwald) - VMS support moved in.
+# 02/06/95 (seiwald) - ObjectC++Flags and SubDirC++Flags added.
+# 02/07/95 (seiwald) - Iron out when HDRSEARCH uses "" or SEARCH_SOURCE.
+# 02/08/95 (seiwald) - SubDir works on VMS.
+# 02/14/95 (seiwald) - MkDir and entourage.
+# 04/30/95 (seiwald) - Use install -c flag so that it copies, not moves.
+# 07/10/95 (taylor) - Support for Microsoft C++.
+# 11/21/96 (peterk) - Support for BeOS
+# 07/19/99 (sickel) - Support for Mac OS X Server (and maybe client)
+# 02/18/00 (belmonte)- Support for Cygwin.
+
+# Special targets defined in this file:
+#
+# all - parent of first, shell, files, lib, exe
+# first - first dependency of 'all', for potential initialization
+# shell - parent of all Shell targets
+# files - parent of all File targets
+# lib - parent of all Library targets
+# exe - parent of all Main targets
+# dirs - parent of all MkDir targets
+# clean - removes all Shell, File, Library, and Main targets
+# uninstall - removes all Install targets
+#
+
+# Rules defined by this file:
+#
+# as obj.o : source.s ; .s -> .o
+# Bulk dir : files ; populate directory with many files
+# Cc obj.o : source.c ; .c -> .o
+# C++ obj.o : source.cc ; .cc -> .o
+# Clean clean : sources ; remove sources with 'jam clean'
+# File dest : source ; copy file
+# Fortran obj.o : source.f ; .f -> .o
+# GenFile source.c : program args ; make custom file
+# Hardlink target : source ; make link from source to target
+# HdrRule source : headers ; handle #includes
+# InstallInto dir : sources ; install any files
+# InstallBin dir : sources ; install binaries
+# InstallLib dir : sources ; install files
+# InstallFile dir : sources ; install files
+# InstallMan dir : sources ; install man pages
+# InstallShell dir : sources ; install shell scripts
+# Lex source.c : source.l ; .l -> .c
+# Library lib : source ; archive library from compiled sources
+# LibraryFromObjects lib : objects ; archive library from objects
+# LinkLibraries images : libraries ; bag libraries onto Mains
+# Main image : source ; link executable from compiled sources
+# MainFromObjects image : objects ; link executable from objects
+# MkDir dir ; make a directory, if not there
+# Object object : source ; compile object from source
+# ObjectCcFlags source : flags ; add compiler flags for object
+# ObjectC++Flags source : flags ; add compiler flags for object
+# ObjectHdrs source : dirs ; add include directories for object
+# Objects sources ; compile sources
+# RmTemps target : sources ; remove temp sources after target made
+# Setuid images ; mark executables Setuid
+# SubDir TOP d1 d2 ... ; start a subdirectory Jamfile
+# SubDirCcFlags flags ; add compiler flags until next SubDir
+# SubDirC++Flags flags ; add compiler flags until next SubDir
+# SubDirHdrs dirs ; add include dirs until next SubDir
+# SubInclude TOP d1 d2 ... ; include a subdirectory Jamfile
+# Shell exe : source ; make a shell executable
+# Undefines images : symbols ; save undef's for linking
+# UserObject object : source ; handle unknown suffixes for Object
+# Yacc source.c : source.y ; .y -> .c
+#
+# Utility rules that have no side effects (not supported):
+#
+# FAppendSuffix f1 f2 ... : $(SUF) ; return $(<) with suffixes
+# FConcat value ... ; return contatenated values
+# FDirName d1 d2 ... ; return path from root to dir
+# FGrist d1 d2 ... ; return d1!d2!...
+# FGristFiles value ; return $(value:G=$(SOURCE_GRIST))
+# FGristSourceFiles value ; return $(value:G=$(SOURCE_GRIST))
+# FRelPath d1 : d2 ; return rel path from d1 to d2
+# FSubDir d1 d2 ... ; return path to root
+#
+
+
+# Brief review of the jam language:
+#
+# Statements:
+# rule RULE - statements to process a rule
+# actions RULE - system commands to carry out target update
+#
+# Modifiers on actions:
+# together - multiple instances of same rule on target get executed
+# once with their sources ($(>)) concatenated
+# updated - refers to updated sources ($(>)) only
+# ignore - ignore return status of command
+# quietly - don't trace its execution unless verbose
+# piecemeal - iterate command each time with a small subset of $(>)
+# existing - refers to currently existing sources ($(>)) only
+# bind vars - subject to binding before expanding in actions
+#
+# Special rules:
+# ALWAYS - always build a target
+# DEPENDS - builds the dependency graph
+# ECHO - blurt out targets on stdout
+# EXIT - blurt out targets and exit
+# INCLUDES - marks sources as headers for target (a codependency)
+# NOCARE - don't panic if the target can't be built
+# NOUPDATE - create the target if needed but never update it
+# NOTFILE - ignore the timestamp of the target (it's not a file)
+# TEMPORARY - target need not be present if sources haven't changed
+#
+# Special variables set by jam:
+# $(<) - targets of a rule (to the left of the :)
+# $(>) - sources of a rule (to the right of the :)
+# $(xxx) - true on xxx (UNIX, VMS, NT, OS2, MAC)
+# $(OS) - name of OS - varies wildly
+# $(JAMVERSION) - version number (2.3)
+#
+# Special variables used by jam:
+# SEARCH - where to find something (used during binding and actions)
+# LOCATE - where to plop something not found with SEARCH
+# HDRRULE - rule to call to handle include files
+# HDRSCAN - egrep regex to extract include files
+#
+# Special targets:
+# all - default if none given on command line
+#
+
+# Initialize variables
+#
+
+#
+# OS specific variable settings
+#
+if $(NT)
+{
+ # the list of supported toolsets on Windows NT and Windows 95/98
+ #
+ local SUPPORTED_TOOLSETS = "BORLANDC" "VC7" "VISUALC" "VISUALC16" "INTELC" "WATCOM"
+ "MINGW" "LCC" ;
+
+ # this variable holds the current toolset
+ #
+ TOOLSET = "" ;
+
+ # if the JAM_TOOLSET environment variable is defined, check that it is
+ # one of our supported values
+ #
+ if $(JAM_TOOLSET)
+ {
+ local t ;
+
+ for t in $(SUPPORTED_TOOLSETS)
+ {
+ $(t) = $($(t):J=" ") ; # reconstitute paths with spaces in them
+ if $(t) = $(JAM_TOOLSET) { TOOLSET = $(t) ; }
+ }
+
+ if ! $(TOOLSET)
+ {
+ ECHO "The JAM_TOOLSET environment variable is defined but its value" ;
+ ECHO "is invalid, please use one of the following:" ;
+ ECHO ;
+
+ for t in $(SUPPORTED_TOOLSETS) { ECHO " " $(t) ; }
+ EXIT ;
+ }
+ }
+
+ # if TOOLSET is empty, we'll try to detect the toolset from other
+ # environment variables to remain backwards compatible with Jam 2.3
+ #
+ if ! $(TOOLSET)
+ {
+ if $(BCCROOT)
+ {
+ TOOLSET = BORLANDC ;
+ BORLANDC = $(BCCROOT:J=" ") ;
+ }
+ else if $(MSVC)
+ {
+ TOOLSET = VISUALC16 ;
+ VISUALC16 = $(MSVC:J=" ") ;
+ }
+ else if $(MSVCNT)
+ {
+ TOOLSET = VISUALC ;
+ VISUALC = $(MSVCNT:J=" ") ;
+ }
+ else if $(MSVCDir)
+ {
+ TOOLSET = VISUALC ;
+ VISUALC = $(MSVCDir:J=" ") ;
+ }
+ else if $(MINGW)
+ {
+ TOOLSET = MINGW ;
+ }
+ else
+ {
+ ECHO "Jam cannot be run because, either:" ;
+ ECHO " a. You didn't set BOOST_ROOT to indicate the root of your" ;
+ ECHO " Boost installation." ;
+ ECHO " b. You are trying to use stock Jam but didn't indicate which" ;
+ ECHO " compilation toolset to use. To do so, follow these simple" ;
+ ECHO " instructions:" ;
+ ECHO ;
+ ECHO " - define one of the following environment variable, with the" ;
+ ECHO " appropriate value according to this list:" ;
+ ECHO ;
+ ECHO " Variable Toolset Description" ;
+ ECHO ;
+ ECHO " BORLANDC Borland C++ BC++ install path" ;
+ ECHO " VISUALC Microsoft Visual C++ VC++ install path" ;
+ ECHO " VISUALC16 Microsoft Visual C++ 16 bit VC++ 16 bit install" ;
+ ECHO " INTELC Intel C/C++ IC++ install path" ;
+ ECHO " WATCOM Watcom C/C++ Watcom install path" ;
+ ECHO " MINGW MinGW (gcc) MinGW install path" ;
+ ECHO " LCC Win32-LCC LCC-Win32 install path" ;
+ ECHO ;
+ ECHO " - define the JAM_TOOLSET environment variable with the *name*" ;
+ ECHO " of the toolset variable you want to use." ;
+ ECHO ;
+ ECHO " e.g.: set VISUALC=C:\\Visual6" ;
+ ECHO " set JAM_TOOLSET=VISUALC" ;
+ EXIT ;
+ }
+ }
+
+ CP ?= copy ;
+ RM ?= del /f/q ;
+ SLASH ?= \\ ;
+ SUFLIB ?= .lib ;
+ SUFOBJ ?= .obj ;
+ SUFEXE ?= .exe ;
+
+ if $(TOOLSET) = BORLANDC
+ {
+ ECHO "Compiler is Borland C++" ;
+
+ AR ?= tlib /C /P64 ;
+ CC ?= bcc32 ;
+ CCFLAGS ?= -q -y -d -v -w-par -w-ccc -w-rch -w-pro -w-aus ;
+ C++ ?= bcc32 ;
+ C++FLAGS ?= -q -y -d -v -w-par -w-ccc -w-rch -w-pro -w-aus -P ;
+ LINK ?= $(CC) ;
+ LINKFLAGS ?= $(CCFLAGS) ;
+ STDLIBPATH ?= $(BORLANDC)\\lib ;
+ STDHDRS ?= $(BORLANDC)\\include ;
+ NOARSCAN ?= true ;
+ }
+ else if $(TOOLSET) = VISUALC16
+ {
+ ECHO "Compiler is Microsoft Visual C++ 16 bit" ;
+
+ AR ?= lib /nologo ;
+ CC ?= cl /nologo ;
+ CCFLAGS ?= /D \"WIN\" ;
+ C++ ?= $(CC) ;
+ C++FLAGS ?= $(CCFLAGS) ;
+ LINK ?= $(CC) ;
+ LINKFLAGS ?= $(CCFLAGS) ;
+ LINKLIBS ?=
+ \"$(VISUALC16)\\lib\\mlibce.lib\"
+ \"$(VISUALC16)\\lib\\oldnames.lib\"
+ ;
+ LINKLIBS ?= ;
+ NOARSCAN ?= true ;
+ OPTIM ?= "" ;
+ STDHDRS ?= $(VISUALC16)\\include ;
+ UNDEFFLAG ?= "/u _" ;
+ }
+ else if $(TOOLSET) = VISUALC
+ {
+ ECHO "Compiler is Microsoft Visual C++" ;
+
+ AR ?= lib ;
+ AS ?= masm386 ;
+ CC ?= cl /nologo ;
+ CCFLAGS ?= "" ;
+ C++ ?= $(CC) ;
+ C++FLAGS ?= $(CCFLAGS) ;
+ LINK ?= link /nologo ;
+ LINKFLAGS ?= "" ;
+ LINKLIBS ?= \"$(VISUALC)\\lib\\advapi32.lib\"
+ # $(VISUALC)\\lib\\libc.lib
+ # $(VISUALC)\\lib\\oldnames.lib
+ \"$(VISUALC)\\lib\\gdi32.lib\"
+ \"$(VISUALC)\\lib\\user32.lib\"
+ \"$(VISUALC)\\lib\\kernel32.lib\" ;
+ OPTIM ?= "" ;
+ STDHDRS ?= $(VISUALC)\\include ;
+ UNDEFFLAG ?= "/u _" ;
+ }
+ else if $(TOOLSET) = VC7
+ {
+ ECHO "Compiler is Microsoft Visual C++ .NET" ;
+
+ AR ?= lib ;
+ AS ?= masm386 ;
+ CC ?= cl /nologo ;
+ CCFLAGS ?= "" ;
+ C++ ?= $(CC) ;
+ C++FLAGS ?= $(CCFLAGS) ;
+ LINK ?= link /nologo ;
+ LINKFLAGS ?= "" ;
+ LINKLIBS ?= \"$(VISUALC)\\PlatformSDK\\lib\\advapi32.lib\"
+ # $(VISUALC)\\lib\\libc.lib
+ # $(VISUALC)\\lib\\oldnames.lib
+ \"$(VISUALC)\\PlatformSDK\\lib\\gdi32.lib\"
+ \"$(VISUALC)\\PlatformSDK\\lib\\user32.lib\"
+ \"$(VISUALC)\\PlatformSDK\\lib\\kernel32.lib\" ;
+ OPTIM ?= "" ;
+ STDHDRS ?= \"$(VISUALC)\\include\"
+ \"$(VISUALC)\\PlatformSDK\\include\" ;
+ UNDEFFLAG ?= "/u _" ;
+ }
+ else if $(TOOLSET) = INTELC
+ {
+ ECHO "Compiler is Intel C/C++" ;
+
+ if ! $(VISUALC)
+ {
+ ECHO "As a special exception, when using the Intel C++ compiler, you need" ;
+ ECHO "to define the VISUALC environment variable to indicate the location" ;
+ ECHO "of your Visual C++ installation. Aborting.." ;
+ EXIT ;
+ }
+
+ AR ?= lib ;
+ AS ?= masm386 ;
+ CC ?= icl /nologo ;
+ CCFLAGS ?= "" ;
+ C++ ?= $(CC) ;
+ C++FLAGS ?= $(CCFLAGS) ;
+ LINK ?= link /nologo ;
+ LINKFLAGS ?= "" ;
+ LINKLIBS ?= $(VISUALC)\\lib\\advapi32.lib
+ # $(VISUALC)\\lib\\libc.lib
+ # $(VISUALC)\\lib\\oldnames.lib
+ $(VISUALC)\\lib\\kernel32.lib
+ ;
+ OPTIM ?= "" ;
+ STDHDRS ?= $(INTELC)\include $(VISUALC)\\include ;
+ UNDEFFLAG ?= "/u _" ;
+ }
+ else if $(TOOLSET) = WATCOM
+ {
+ ECHO "Compiler is Watcom C/C++" ;
+
+ AR ?= wlib ;
+ CC ?= wcc386 ;
+ CCFLAGS ?= /zq /DWIN32 /I$(WATCOM)\\h ; # zq=quiet
+ C++ ?= wpp386 ;
+ C++FLAGS ?= $(CCFLAGS) ;
+ CP ?= copy ;
+ DOT ?= . ;
+ DOTDOT ?= .. ;
+ LINK ?= wcl386 ;
+ LINKFLAGS ?= /zq ; # zq=quiet
+ LINKLIBS ?= ;
+ MV ?= move ;
+ NOARSCAN ?= true ;
+ OPTIM ?= ;
+ RM ?= del /f ;
+ SLASH ?= \\ ;
+ STDHDRS ?= $(WATCOM)\\h $(WATCOM)\\h\\nt ;
+ SUFEXE ?= .exe ;
+ SUFLIB ?= .lib ;
+ SUFOBJ ?= .obj ;
+ UNDEFFLAG ?= "/u _" ;
+ }
+ else if $(TOOLSET) = MINGW
+ {
+ ECHO "Compiler is GCC with Mingw" ;
+
+ AR ?= ar -ru ;
+ CC ?= gcc ;
+ CCFLAGS ?= "" ;
+ C++ ?= $(CC) ;
+ C++FLAGS ?= $(CCFLAGS) ;
+ LINK ?= $(CC) ;
+ LINKFLAGS ?= "" ;
+ LINKLIBS ?= "" ;
+ OPTIM ?= ;
+ SUFOBJ = .o ;
+ SUFLIB = .a ;
+ SLASH = / ;
+# NOARSCAN ?= true ;
+ }
+ else if $(TOOLSET) = LCC
+ {
+ ECHO "Compiler is Win32-LCC" ;
+
+ AR ?= lcclib ;
+ CC ?= lcc ;
+ CCFLAGS ?= "" ;
+ C++ ?= $(CC) ;
+ C++FLAGS ?= $(CCFLAGS) ;
+ LINK ?= lcclnk ;
+ LINKFLAGS ?= "" ;
+ LINKLIBS ?= "" ;
+ OPTIM ?= ;
+ NOARSCAN = true ;
+ }
+ else
+ {
+#
+# XXX: We need better comments here !!
+#
+ EXIT On NT, set BCCROOT, MSVCNT, MINGW or MSVC to the root of the
+ Borland or Microsoft directories. ;
+ }
+
+}
+else if $(OS2)
+{
+ # the list of supported toolsets on Windows NT and Windows 95/98
+ #
+ local SUPPORTED_TOOLSETS = "EMX" "WATCOM" ;
+
+ # this variable holds the current toolset
+ #
+ TOOLSET = "" ;
+
+ # if the JAM_TOOLSET environment variable is defined, check that it is
+ # one of our supported values
+ #
+ if $(JAM_TOOLSET)
+ {
+ local t ;
+
+ for t in $(SUPPORTED_TOOLSETS)
+ {
+ $(t) = $($(t):J=" ") ; # reconstitute paths with spaces in them
+ if $(t) = $(JAM_TOOLSET) { TOOLSET = $(t) ; }
+ }
+
+ if ! $(TOOLSET)
+ {
+ ECHO "The JAM_TOOLSET environment variable is defined but its value" ;
+ ECHO "is invalid, please use one of the following:" ;
+ ECHO ;
+
+ for t in $(SUPPORTED_TOOLSETS) { ECHO " " $(t) ; }
+ EXIT ;
+ }
+ }
+
+ # if TOOLSET is empty, we'll try to detect the toolset from other
+ # environment variables to remain backwards compatible with Jam 2.3
+ #
+ if ! $(TOOLSET)
+ {
+ if $(watcom)
+ {
+ WATCOM = $(watcom:J=" ") ;
+ TOOLSET = WATCOM ;
+ }
+ else
+ {
+ ECHO "Jam cannot be run because you didn't indicate which compilation toolset" ;
+ ECHO "to use. To do so, follow these simple instructions:" ;
+ ECHO ;
+ ECHO " - define one of the following environment variable, with the" ;
+ ECHO " appropriate value according to this list:" ;
+ ECHO ;
+ ECHO " Variable Toolset Description" ;
+ ECHO ;
+ ECHO " WATCOM Watcom C/C++ Watcom install path" ;
+ ECHO " EMX EMX (gcc) EMX install path" ;
+ ECHO " VISUALAGE IBM Visual Age C/C++ VisualAge install path" ;
+ ECHO ;
+ ECHO " - define the JAM_TOOLSET environment variable with the *name*" ;
+ ECHO " of the toolset variable you want to use." ;
+ ECHO ;
+ ECHO " e.g.: set WATCOM=C:\WATCOM" ;
+ ECHO " set JAM_TOOLSET=WATCOM" ;
+ ECHO ;
+ EXIT ;
+ }
+ }
+
+ RM = del /f ;
+ CP = copy ;
+ MV ?= move ;
+ DOT ?= . ;
+ DOTDOT ?= .. ;
+ SUFLIB ?= .lib ;
+ SUFOBJ ?= .obj ;
+ SUFEXE ?= .exe ;
+
+ if $(TOOLSET) = WATCOM
+ {
+ AR ?= wlib ;
+ BINDIR ?= \\os2\\apps ;
+ CC ?= wcc386 ;
+ CCFLAGS ?= /zq /DOS2 /I$(WATCOM)\\h ; # zq=quiet
+ C++ ?= wpp386 ;
+ C++FLAGS ?= $(CCFLAGS) ;
+ LINK ?= wcl386 ;
+ LINKFLAGS ?= /zq ; # zq=quiet
+ LINKLIBS ?= ;
+ NOARSCAN ?= true ;
+ OPTIM ?= ;
+ SLASH ?= \\ ;
+ STDHDRS ?= $(WATCOM)\\h ;
+ UNDEFFLAG ?= "/u _" ;
+ }
+ else if $(TOOLSET) = EMX
+ {
+ ECHO "Compiler is GCC-EMX" ;
+ AR ?= ar -ru ;
+ CC ?= gcc ;
+ CCFLAGS ?= "" ;
+ C++ ?= $(CC) ;
+ C++FLAGS ?= $(CCFLAGS) ;
+ LINK ?= $(CC) ;
+ LINKFLAGS ?= "" ;
+ LINKLIBS ?= "" ;
+ OPTIM ?= ;
+ SUFOBJ = .o ;
+ SUFLIB = .a ;
+ UNDEFFLAG ?= "-U" ;
+ SLASH = / ;
+# NOARSCAN ?= true ;
+ }
+ else
+ {
+ # should never happen
+ EXIT "Sorry, but the $(JAM_TOOLSET) toolset isn't supported for now" ;
+ }
+}
+else if $(VMS)
+{
+ C++ ?= cxx ;
+ C++FLAGS ?= ;
+ CC ?= cc ;
+ CCFLAGS ?= ;
+ CHMOD ?= set file/prot= ;
+ CP ?= copy/replace ;
+ CRELIB ?= true ;
+ DOT ?= [] ;
+ DOTDOT ?= [-] ;
+ EXEMODE ?= (w:e) ;
+ FILEMODE ?= (w:r) ;
+ HDRS ?= ;
+ LINK ?= link ;
+ LINKFLAGS ?= "" ;
+ LINKLIBS ?= ;
+ MKDIR ?= create/dir ;
+ MV ?= rename ;
+ OPTIM ?= "" ;
+ RM ?= delete ;
+ RUNVMS ?= mcr ;
+ SHELLMODE ?= (w:er) ;
+ SLASH ?= . ;
+ STDHDRS ?= decc$library_include ;
+ SUFEXE ?= .exe ;
+ SUFLIB ?= .olb ;
+ SUFOBJ ?= .obj ;
+
+ switch $(OS)
+ {
+ case OPENVMS : CCFLAGS ?= /stand=vaxc ;
+ case VMS : LINKLIBS ?= sys$library:vaxcrtl.olb/lib ;
+ }
+}
+else if $(MAC)
+{
+ local OPT ;
+
+ CW ?= "{CW}" ;
+
+ MACHDRS ?=
+ "$(UMACHDRS):Universal:Interfaces:CIncludes"
+ "$(CW):MSL:MSL_C:MSL_Common:Include"
+ "$(CW):MSL:MSL_C:MSL_MacOS:Include" ;
+
+ MACLIBS ?=
+ "$(CW):MacOS Support:Universal:Libraries:StubLibraries:Interfacelib"
+ "$(CW):MacOS Support:Universal:Libraries:StubLibraries:Mathlib" ;
+
+ MPWLIBS ?=
+ "$(CW):MacOS Support:Libraries:Runtime:Runtime PPC:MSL MPWCRuntime.lib"
+ "$(CW):MSL:MSL_C:MSL_MacOS:Lib:PPC:MSL C.PPC MPW.Lib" ;
+
+ MPWNLLIBS ?=
+ "$(CW):MacOS Support:Libraries:Runtime:Runtime PPC:MSL MPWCRuntime.lib"
+ "$(CW):MSL:MSL_C:MSL_MacOS:Lib:PPC:MSL C.PPC MPW(NL).Lib" ;
+
+ SIOUXHDRS ?= ;
+
+ SIOUXLIBS ?=
+ "$(CW):MacOS Support:Libraries:Runtime:Runtime PPC:MSL RuntimePPC.lib"
+ "$(CW):MSL:MSL_C:MSL_MacOS:Lib:PPC:MSL SIOUX.PPC.Lib"
+ "$(CW):MSL:MSL_C:MSL_MacOS:Lib:PPC:MSL C.PPC.Lib" ;
+
+ C++ ?= mwcppc ;
+ C++FLAGS ?= -w off -nomapcr ;
+ CC ?= mwcppc ;
+ CCFLAGS ?= -w off -nomapcr ;
+ CP ?= duplicate -y ;
+ DOT ?= ":" ;
+ DOTDOT ?= "::" ;
+ HDRS ?= $(MACHDRS) $(MPWHDRS) ;
+ LINK ?= mwlinkppc ;
+ LINKFLAGS ?= -mpwtool -warn ;
+ LINKLIBS ?= $(MACLIBS) $(MPWLIBS) ;
+ MKDIR ?= newfolder ;
+ MV ?= rename -y ;
+ NOARSCAN ?= true ;
+ OPTIM ?= ;
+ RM ?= delete -y ;
+ SLASH ?= ":" ;
+ STDHDRS ?= ;
+ SUFLIB ?= .lib ;
+ SUFOBJ ?= .o ;
+}
+else if $(OS) = BEOS && $(METROWERKS)
+{
+ AR ?= mwld -xml -o ;
+ BINDIR ?= /boot/apps ;
+ CC ?= mwcc ;
+ CCFLAGS ?= -nosyspath ;
+ C++ ?= $(CC) ;
+ C++FLAGS ?= -nosyspath ;
+ FORTRAN ?= "" ;
+ LIBDIR ?= /boot/develop/libraries ;
+ LINK ?= mwld ;
+ LINKFLAGS ?= "" ;
+ MANDIR ?= /boot/documentation/"Shell Tools"/HTML ;
+ NOARSCAN ?= true ;
+ STDHDRS ?= /boot/develop/headers/posix ;
+}
+else if $(OS) = BEOS
+{
+ BINDIR ?= /boot/apps ;
+ CC ?= gcc ;
+ C++ ?= $(CC) ;
+ FORTRAN ?= "" ;
+ LIBDIR ?= /boot/develop/libraries ;
+ LINK ?= gcc ;
+ LINKLIBS ?= -lnet ;
+ NOARSCAN ?= true ;
+ STDHDRS ?= /boot/develop/headers/posix ;
+}
+else if $(UNIX)
+{
+ switch $(OS)
+ {
+ case AIX :
+ LINKLIBS ?= -lbsd ;
+
+ case AMIGA :
+ CC ?= gcc ;
+ YACC ?= "bison -y" ;
+
+ case CYGWIN :
+ CC ?= gcc ;
+ CCFLAGS += -D__cygwin__ ;
+ LEX ?= flex ;
+ RANLIB ?= "" ;
+ SUFEXE ?= .exe ;
+ YACC ?= "bison -y" ;
+
+ case DGUX :
+ RANLIB ?= "" ;
+ RELOCATE ?= true ;
+
+ case HPUX :
+ YACC = ;
+ CFLAGS += -Ae ;
+ CCFLAGS += -Ae ;
+ RANLIB ?= "" ;
+
+ case INTERIX :
+ CC ?= gcc ;
+ RANLIB ?= "" ;
+
+ case IRIX :
+ RANLIB ?= "" ;
+
+ case MPEIX :
+ CC ?= gcc ;
+ C++ ?= gcc ;
+ CCFLAGS += -D_POSIX_SOURCE ;
+ HDRS += /usr/include ;
+ RANLIB ?= "" ;
+ NOARSCAN ?= true ;
+ NOARUPDATE ?= true ;
+
+ case MVS :
+ RANLIB ?= "" ;
+
+ case NEXT :
+ AR ?= libtool -o ;
+ RANLIB ?= "" ;
+
+ case MACOSX :
+ AR ?= libtool -o ;
+ C++ ?= c++ ;
+ MANDIR ?= /usr/local/share/man ;
+ RANLIB ?= "" ;
+
+ case NCR :
+ RANLIB ?= "" ;
+
+ case PTX :
+ RANLIB ?= "" ;
+
+ case QNX :
+ AR ?= wlib ;
+ CC ?= cc ;
+ CCFLAGS ?= -Q ; # quiet
+ C++ ?= $(CC) ;
+ C++FLAGS ?= -Q ; # quiet
+ LINK ?= $(CC) ;
+ LINKFLAGS ?= -Q ; # quiet
+ NOARSCAN ?= true ;
+ RANLIB ?= "" ;
+
+ case SCO :
+ RANLIB ?= "" ;
+ RELOCATE ?= true ;
+
+ case SINIX :
+ RANLIB ?= "" ;
+
+ case SOLARIS :
+ RANLIB ?= "" ;
+ AR ?= "/usr/ccs/bin/ar ru" ;
+
+ case UNICOS :
+ NOARSCAN ?= true ;
+ OPTIM ?= -O0 ;
+
+ case UNIXWARE :
+ RANLIB ?= "" ;
+ RELOCATE ?= true ;
+ }
+
+ # UNIX defaults
+
+ CCFLAGS ?= ;
+ C++FLAGS ?= $(CCFLAGS) ;
+ CHMOD ?= chmod ;
+ CHGRP ?= chgrp ;
+ CHOWN ?= chown ;
+ LEX ?= lex ;
+ LINKFLAGS ?= $(CCFLAGS) ;
+ LINKLIBS ?= ;
+ OPTIM ?= -O ;
+ RANLIB ?= ranlib ;
+ YACC ?= yacc ;
+ YACCFILES ?= y.tab ;
+ YACCFLAGS ?= -d ;
+}
+
+#
+# General defaults; a lot like UNIX
+#
+
+ AR ?= ar ru ;
+ AS ?= as ;
+ ASFLAGS ?= ;
+ AWK ?= awk ;
+ BINDIR ?= /usr/local/bin ;
+ C++ ?= cc ;
+ C++FLAGS ?= ;
+ CC ?= cc ;
+ CCFLAGS ?= ;
+ CP ?= cp -f ;
+ CRELIB ?= ;
+ DOT ?= . ;
+ DOTDOT ?= .. ;
+ EXEMODE ?= 711 ;
+ FILEMODE ?= 644 ;
+ FORTRAN ?= f77 ;
+ FORTRANFLAGS ?= ;
+ HDRS ?= ;
+ INSTALLGRIST ?= installed ;
+ JAMFILE ?= Jamfile ;
+ JAMRULES ?= Jamrules ;
+ LEX ?= ;
+ LIBDIR ?= /usr/local/lib ;
+ LINK ?= $(CC) ;
+ LINKFLAGS ?= ;
+ LINKLIBS ?= ;
+ LN ?= ln ;
+ MANDIR ?= /usr/local/man ;
+ MKDIR ?= mkdir ;
+ MV ?= mv -f ;
+ OPTIM ?= ;
+ RCP ?= rcp ;
+ RM ?= rm -f ;
+ RSH ?= rsh ;
+ SED ?= sed ;
+ SHELLHEADER ?= "#!/bin/sh" ;
+ SHELLMODE ?= 755 ;
+ SLASH ?= / ;
+ STDHDRS ?= /usr/include ;
+ SUFEXE ?= "" ;
+ SUFLIB ?= .a ;
+ SUFOBJ ?= .o ;
+ UNDEFFLAG ?= "-u _" ;
+ YACC ?= ;
+ YACCFILES ?= ;
+ YACCFLAGS ?= ;
+
+ HDRPATTERN =
+ "^[ ]*#[ ]*include[ ]*[<\"]([^\">]*)[\">].*$" ;
+
+ OSFULL = $(OS)$(OSVER)$(OSPLAT) $(OS)$(OSPLAT) $(OS)$(OSVER) $(OS) ;
+
+
+#
+# Base dependencies - first for "bootstrap" kinds of rules
+#
+
+DEPENDS all : shell files lib exe obj ;
+DEPENDS all shell files lib exe obj : first ;
+NOTFILE all first shell files lib exe obj dirs clean uninstall ;
+ALWAYS clean uninstall ;
+
+#
+# Rules
+#
+
+rule As
+{
+ DEPENDS $(<) : $(>) ;
+ ASFLAGS on $(<) += $(ASFLAGS) $(SUBDIRASFLAGS) ;
+}
+
+rule Bulk
+{
+ local i ;
+
+ for i in $(>)
+ {
+ File $(i:D=$(<)) : $(i) ;
+ }
+}
+
+rule Cc
+{
+ local _h ;
+
+ DEPENDS $(<) : $(>) ;
+
+ # Just to clarify here: this sets the per-target CCFLAGS to
+ # be the current value of (global) CCFLAGS and SUBDIRCCFLAGS.
+
+ CCFLAGS on $(<) += $(CCFLAGS) $(SUBDIRCCFLAGS) ;
+
+ # If the compiler's -o flag doesn't work, relocate the .o
+
+ if $(RELOCATE)
+ {
+ CcMv $(<) : $(>) ;
+ }
+
+ _h = $(SEARCH_SOURCE) $(HDRS) $(SUBDIRHDRS) ;
+
+ if $(VMS) && $(_h)
+ {
+ SLASHINC on $(<) = "/inc=(" $(_h[1]) ,$(_h[2-]) ")" ;
+ }
+ else if $(MAC) && $(_h)
+ {
+ local _i _j ;
+ _j = $(_h[1]) ;
+ for _i in $(_h[2-])
+ {
+ _j = $(_j),$(_i) ;
+ }
+ MACINC on $(<) = \"$(_j)\" ;
+ }
+}
+
+rule C++
+{
+ local _h ;
+
+ DEPENDS $(<) : $(>) ;
+ C++FLAGS on $(<) += $(C++FLAGS) $(SUBDIRC++FLAGS) ;
+
+ if $(RELOCATE)
+ {
+ CcMv $(<) : $(>) ;
+ }
+
+ _h = $(SEARCH_SOURCE) $(HDRS) $(SUBDIRHDRS) ;
+
+ if $(VMS) && $(_h)
+ {
+ SLASHINC on $(<) = "/inc=(" $(_h[1]) ,$(_h[2-]) ")" ;
+ }
+ else if $(MAC) && $(_h)
+ {
+ local _i _j ;
+ _j = $(_h[1]) ;
+ for _i in $(_h[2-])
+ {
+ _j = $(_j),$(_i) ;
+ }
+ MACINC on $(<) = \"$(_j)\" ;
+ }
+}
+
+rule Chmod
+{
+ if $(CHMOD) { Chmod1 $(<) ; }
+}
+
+rule File
+{
+ DEPENDS files : $(<) ;
+ DEPENDS $(<) : $(>) ;
+ SEARCH on $(>) = $(SEARCH_SOURCE) ;
+ MODE on $(<) = $(FILEMODE) ;
+ Chmod $(<) ;
+}
+
+rule Fortran
+{
+ DEPENDS $(<) : $(>) ;
+}
+
+rule GenFile
+{
+ local _t = [ FGristSourceFiles $(<) ] ;
+ local _s = [ FAppendSuffix $(>[1]) : $(SUFEXE) ] ;
+ Depends $(_t) : $(_s) $(>[2-]) ;
+ GenFile1 $(_t) : $(_s) $(>[2-]) ;
+ Clean clean : $(_t) ;
+}
+
+rule GenFile1
+{
+ MakeLocate $(<) : $(LOCATE_SOURCE) ;
+ SEARCH on $(>) = $(SEARCH_SOURCE) ;
+}
+
+rule HardLink
+{
+ DEPENDS files : $(<) ;
+ DEPENDS $(<) : $(>) ;
+ SEARCH on $(>) = $(SEARCH_SOURCE) ;
+}
+
+rule HdrMacroFile
+{
+ # HdrMacroFile file ;
+ #
+ # this rule is used to indicate that a given file contains definitions
+ # for filename macros (e.g. "#define MYFILE_H <myfile.h>") that can
+ # later be used in #include statements in the rest of the source
+ #
+ # theses files must be parsed before any make is tried..
+ #
+ HDRMACRO $(<) ;
+}
+
+rule HdrRule
+{
+ # HdrRule source : headers ;
+
+ # N.B. This rule is called during binding, potentially after
+ # the fate of many targets has been determined, and must be
+ # used with caution: don't add dependencies to unrelated
+ # targets, and don't set variables on $(<).
+
+ # Tell Jam that anything depending on $(<) also depends on $(>),
+ # set SEARCH so Jam can find the headers, but then say we don't
+ # care if we can't actually find the headers (they may have been
+ # within ifdefs),
+
+ local s ;
+
+ if $(HDRGRIST)
+ {
+ s = $(>:G=$(HDRGRIST)) ;
+ } else {
+ s = $(>) ;
+ }
+
+ INCLUDES $(<) : $(s) ;
+ SEARCH on $(s) = $(HDRSEARCH) ;
+ NOCARE $(s) ;
+
+ # Propagate on $(<) to $(>)
+
+ HDRSEARCH on $(s) = $(HDRSEARCH) ;
+ HDRSCAN on $(s) = $(HDRSCAN) ;
+ HDRRULE on $(s) = $(HDRRULE) ;
+ HDRGRIST on $(s) = $(HDRGRIST) ;
+}
+
+rule InstallInto
+{
+ # InstallInto dir : sources ;
+
+ local i t ;
+
+ t = $(>:G=$(INSTALLGRIST)) ;
+
+ # Arrange for jam install
+ # Arrange for jam uninstall
+ # sources are in SEARCH_SOURCE
+ # targets are in dir
+
+ Depends install : $(t) ;
+ Clean uninstall : $(t) ;
+ SEARCH on $(>) = $(SEARCH_SOURCE) ;
+ MakeLocate $(t) : $(<) ;
+
+ # For each source, make gristed target name
+ # and Install, Chmod, Chown, and Chgrp
+
+ for i in $(>)
+ {
+ local tt = $(i:G=$(INSTALLGRIST)) ;
+
+ Depends $(tt) : $(i) ;
+ Install $(tt) : $(i) ;
+ Chmod $(tt) ;
+
+ if $(OWNER) && $(CHOWN)
+ {
+ Chown $(tt) ;
+ OWNER on $(tt) = $(OWNER) ;
+ }
+
+ if $(GROUP) && $(CHGRP)
+ {
+ Chgrp $(tt) ;
+ GROUP on $(tt) = $(GROUP) ;
+ }
+ }
+}
+
+rule InstallBin
+{
+ local _t = [ FAppendSuffix $(>) : $(SUFEXE) ] ;
+
+ InstallInto $(<) : $(_t) ;
+ MODE on $(_t:G=installed) = $(EXEMODE) ;
+}
+
+rule InstallFile
+{
+ InstallInto $(<) : $(>) ;
+ MODE on $(>:G=installed) = $(FILEMODE) ;
+}
+
+rule InstallLib
+{
+ InstallInto $(<) : $(>) ;
+ MODE on $(>:G=installed) = $(FILEMODE) ;
+}
+
+rule InstallMan
+{
+ # Really this just strips the . from the suffix
+
+ local i s d ;
+
+ for i in $(>)
+ {
+ switch $(i:S)
+ {
+ case .1 : s = 1 ; case .2 : s = 2 ; case .3 : s = 3 ;
+ case .4 : s = 4 ; case .5 : s = 5 ; case .6 : s = 6 ;
+ case .7 : s = 7 ; case .8 : s = 8 ; case .l : s = l ;
+ case .n : s = n ; case .man : s = 1 ;
+ }
+
+ d = man$(s) ;
+
+ InstallInto $(d:R=$(<)) : $(i) ;
+ }
+
+ MODE on $(>:G=installed) = $(FILEMODE) ;
+}
+
+rule InstallShell
+{
+ InstallInto $(<) : $(>) ;
+ MODE on $(>:G=installed) = $(SHELLMODE) ;
+}
+
+rule Lex
+{
+ LexMv $(<) : $(>) ;
+ DEPENDS $(<) : $(>) ;
+ MakeLocate $(<) : $(LOCATE_SOURCE) ;
+ Clean clean : $(<) ;
+}
+
+rule Library
+{
+ LibraryFromObjects $(<) : $(>:S=$(SUFOBJ)) ;
+ Objects $(>) ;
+}
+
+rule LibraryFromObjects
+{
+ local _i _l _s ;
+
+ # Add grist to file names
+
+ _s = [ FGristFiles $(>) ] ;
+ _l = $(<:S=$(SUFLIB)) ;
+
+ # library depends on its member objects
+
+ if $(KEEPOBJS)
+ {
+ DEPENDS obj : $(_s) ;
+ }
+ else
+ {
+ DEPENDS lib : $(_l) ;
+ }
+
+ # Set LOCATE for the library and its contents. The bound
+ # value shows up as $(NEEDLIBS) on the Link actions.
+ # For compatibility, we only do this if the library doesn't
+ # already have a path.
+
+ if ! $(_l:D)
+ {
+ MakeLocate $(_l) $(_l)($(_s:BS)) : $(LOCATE_TARGET) ;
+ }
+
+ if $(NOARSCAN)
+ {
+ # If we can't scan the library to timestamp its contents,
+ # we have to just make the library depend directly on the
+ # on-disk object files.
+
+ DEPENDS $(_l) : $(_s) ;
+ }
+ else
+ {
+ # If we can scan the library, we make the library depend
+ # on its members and each member depend on the on-disk
+ # object file.
+
+ DEPENDS $(_l) : $(_l)($(_s:BS)) ;
+
+ for _i in $(_s)
+ {
+ DEPENDS $(_l)($(_i:BS)) : $(_i) ;
+ }
+ }
+
+ Clean clean : $(_l) ;
+
+ if $(CRELIB) { CreLib $(_l) : $(_s[1]) ; }
+
+ Archive $(_l) : $(_s) ;
+
+ if $(RANLIB) { Ranlib $(_l) ; }
+
+ # If we can't scan the library, we have to leave the .o's around.
+
+ if ! ( $(NOARSCAN) || $(KEEPOBJS) ) { RmTemps $(_l) : $(_s) ; }
+}
+
+rule Link
+{
+ MODE on $(<) = $(EXEMODE) ;
+ Chmod $(<) ;
+}
+
+rule LinkLibraries
+{
+ # make library dependencies of target
+ # set NEEDLIBS variable used by 'actions Main'
+
+ local _t = [ FAppendSuffix $(<) : $(SUFEXE) ] ;
+
+ DEPENDS $(_t) : $(>:S=$(SUFLIB)) ;
+ NEEDLIBS on $(_t) += $(>:S=$(SUFLIB)) ;
+}
+
+rule Main
+{
+ MainFromObjects $(<) : $(>:S=$(SUFOBJ)) ;
+ Objects $(>) ;
+}
+
+rule MainFromObjects
+{
+ local _s _t ;
+
+ # Add grist to file names
+ # Add suffix to exe
+
+ _s = [ FGristFiles $(>) ] ;
+ _t = [ FAppendSuffix $(<) : $(SUFEXE) ] ;
+
+ if $(_t) != $(<)
+ {
+ DEPENDS $(<) : $(_t) ;
+ NOTFILE $(<) ;
+ }
+
+ # make compiled sources a dependency of target
+
+ DEPENDS exe : $(_t) ;
+ DEPENDS $(_t) : $(_s) ;
+ MakeLocate $(_t) : $(LOCATE_TARGET) ;
+
+ Clean clean : $(_t) ;
+
+ Link $(_t) : $(_s) ;
+}
+
+rule MakeLocate
+{
+ if $(>)
+ {
+ LOCATE on $(<) = $(>) ;
+ Depends $(<) : $(>[1]) ;
+ MkDir $(>[1]) ;
+ }
+}
+
+rule MkDir
+{
+ # If dir exists, don't update it
+ # Do this even for $(DOT).
+
+ NOUPDATE $(<) ;
+
+ if $(<) != $(DOT) && ! $($(<)-mkdir)
+ {
+ local s ;
+
+ # Cheesy gate to prevent multiple invocations on same dir
+ # MkDir1 has the actions
+ # Arrange for jam dirs
+
+ $(<)-mkdir = true ;
+ MkDir1 $(<) ;
+ Depends dirs : $(<) ;
+
+ # Recursively make parent directories.
+ # $(<:P) = $(<)'s parent, & we recurse until root
+
+ s = $(<:P) ;
+
+ if $(NT)
+ {
+ switch $(s)
+ {
+ case *: : s = ;
+ case *:\\ : s = ;
+ }
+ }
+
+ if $(s) && $(s) != $(<)
+ {
+ Depends $(<) : $(s) ;
+ MkDir $(s) ;
+ }
+ else if $(s)
+ {
+ NOTFILE $(s) ;
+ }
+
+ }
+}
+
+rule Object
+{
+ local h ;
+
+ # locate object and search for source, if wanted
+
+ Clean clean : $(<) ;
+
+ MakeLocate $(<) : $(LOCATE_TARGET) ;
+ SEARCH on $(>) = $(SEARCH_SOURCE) ;
+
+ # Save HDRS for -I$(HDRS) on compile.
+ # We shouldn't need -I$(SEARCH_SOURCE) as cc can find headers
+ # in the .c file's directory, but generated .c files (from
+ # yacc, lex, etc) are located in $(LOCATE_TARGET), possibly
+ # different from $(SEARCH_SOURCE).
+
+ HDRS on $(<) = $(SEARCH_SOURCE) $(HDRS) $(SUBDIRHDRS) ;
+
+ # handle #includes for source: Jam scans for headers with
+ # the regexp pattern $(HDRSCAN) and then invokes $(HDRRULE)
+ # with the scanned file as the target and the found headers
+ # as the sources. HDRSEARCH is the value of SEARCH used for
+ # the found header files. Finally, if jam must deal with
+ # header files of the same name in different directories,
+ # they can be distinguished with HDRGRIST.
+
+ # $(h) is where cc first looks for #include "foo.h" files.
+ # If the source file is in a distant directory, look there.
+ # Else, look in "" (the current directory).
+
+ if $(SEARCH_SOURCE)
+ {
+ h = $(SEARCH_SOURCE) ;
+ }
+ else
+ {
+ h = "" ;
+ }
+
+ HDRRULE on $(>) = HdrRule ;
+ HDRSCAN on $(>) = $(HDRPATTERN) ;
+ HDRSEARCH on $(>) = $(HDRS) $(SUBDIRHDRS) $(h) $(STDHDRS) ;
+ HDRGRIST on $(>) = $(HDRGRIST) ;
+
+ # if source is not .c, generate .c with specific rule
+
+ switch $(>:S)
+ {
+ case .asm : As $(<) : $(>) ;
+ case .c : Cc $(<) : $(>) ;
+ case .C : C++ $(<) : $(>) ;
+ case .cc : C++ $(<) : $(>) ;
+ case .cpp : C++ $(<) : $(>) ;
+ case .f : Fortran $(<) : $(>) ;
+ case .l : Cc $(<) : $(<:S=.c) ;
+ Lex $(<:S=.c) : $(>) ;
+ case .s : As $(<) : $(>) ;
+ case .y : Cc $(<) : $(<:S=.c) ;
+ Yacc $(<:S=.c) : $(>) ;
+ case * : UserObject $(<) : $(>) ;
+ }
+}
+
+
+rule ObjectCcFlags
+{
+ CCFLAGS on [ FGristFiles $(<:S=$(SUFOBJ)) ] += $(>) ;
+}
+
+rule ObjectC++Flags
+{
+ C++FLAGS on [ FGristFiles $(<:S=$(SUFOBJ)) ] += $(>) ;
+}
+
+rule ObjectHdrs
+{
+ HDRS on [ FGristFiles $(<:S=$(SUFOBJ)) ] += $(>) ;
+}
+
+rule Objects
+{
+ local _i ;
+
+ for _i in [ FGristFiles $(<) ]
+ {
+ Object $(_i:S=$(SUFOBJ)) : $(_i) ;
+ DEPENDS obj : $(_i:S=$(SUFOBJ)) ;
+ }
+}
+
+rule RmTemps
+{
+ TEMPORARY $(>) ;
+}
+
+rule Setuid
+{
+ MODE on [ FAppendSuffix $(<) : $(SUFEXE) ] = 4711 ;
+}
+
+rule Shell
+{
+ DEPENDS shell : $(<) ;
+ DEPENDS $(<) : $(>) ;
+ SEARCH on $(>) = $(SEARCH_SOURCE) ;
+ MODE on $(<) = $(SHELLMODE) ;
+ Clean clean : $(<) ;
+ Chmod $(<) ;
+}
+
+rule SubDir
+{
+ local _r _s ;
+
+ #
+ # SubDir TOP d1 [ ... ]
+ #
+ # This introduces a Jamfile that is part of a project tree
+ # rooted at $(TOP). It (only once) includes the project-specific
+ # rules file $(TOP)/Jamrules and then sets search & locate stuff.
+ #
+ # If the variable $(TOPRULES) is set (where TOP is the first arg
+ # to SubDir), that file is included instead of $(TOP)/Jamrules.
+ #
+ # d1 ... are the directory elements that lead to this directory
+ # from $(TOP). We construct the system dependent path from these
+ # directory elements in order to set search & locate stuff.
+ #
+
+ if ! $($(<[1]))
+ {
+ if ! $(<[1])
+ {
+ EXIT SubDir syntax error ;
+ }
+
+ $(<[1]) = [ FSubDir $(<[2-]) ] ;
+ }
+
+ #
+ # If $(TOP)/Jamrules hasn't been included, do so.
+ #
+
+ if ! $($(<[1])-included)
+ {
+ # Gated entry.
+
+ $(<[1])-included = TRUE ;
+
+ # File is $(TOPRULES) or $(TOP)/Jamrules.
+
+ _r = $($(<[1])RULES) ;
+
+ if ! $(_r)
+ {
+ _r = $(JAMRULES:R=$($(<[1]))) ;
+ }
+
+ # Include it.
+
+ include $(_r) ;
+ }
+
+ # Get path to current directory from root using SubDir.
+ # Save dir tokens for other potential uses.
+
+ _s = [ FDirName $(<[2-]) ] ;
+ SUBDIR = $(_s:R=$($(<[1]))) ;
+ SUBDIR_TOKENS = $(<[2-]) ;
+
+ # Now set up SEARCH_SOURCE, LOCATE_TARGET, SOURCE_GRIST
+ # These can be reset if needed. For example, if the source
+ # directory should not hold object files, LOCATE_TARGET can
+ # subsequently be redefined.
+
+ SEARCH_SOURCE = $(SUBDIR) ;
+ LOCATE_SOURCE = $(ALL_LOCATE_TARGET) $(SUBDIR) ;
+ LOCATE_TARGET = $(ALL_LOCATE_TARGET) $(SUBDIR) ;
+ SOURCE_GRIST = [ FGrist $(<[2-]) ] ;
+
+ # Reset per-directory ccflags, hdrs
+
+ SUBDIRCCFLAGS = ;
+ SUBDIRC++FLAGS = ;
+ SUBDIRHDRS = ;
+}
+
+rule SubDirCcFlags
+{
+ SUBDIRCCFLAGS += $(<) ;
+}
+
+rule SubDirC++Flags
+{
+ SUBDIRC++FLAGS += $(<) ;
+}
+
+rule SubDirHdrs
+{
+ SUBDIRHDRS += $(<) ;
+}
+
+rule SubInclude
+{
+ local _s ;
+
+ # That's
+ # SubInclude TOP d1 [ d2 [ d3 [ d4 ] ] ]
+ #
+ # to include a subdirectory's Jamfile.
+
+ if ! $($(<[1]))
+ {
+ EXIT Top level of source tree has not been set with $(<[1]) ;
+ }
+
+ _s = [ FDirName $(<[2-]) ] ;
+
+ include $(JAMFILE:D=$(_s):R=$($(<[1]))) ;
+}
+
+rule Undefines
+{
+ UNDEFS on [ FAppendSuffix $(<) : $(SUFEXE) ] += $(UNDEFFLAG)$(>) ;
+}
+
+rule UserObject
+{
+ EXIT "Unknown suffix on" $(>) "- see UserObject rule in Jamfile(5)." ;
+}
+
+rule Yacc
+{
+ local _h ;
+
+ _h = $(<:BS=.h) ;
+
+ # Some places don't have a yacc.
+
+ MakeLocate $(<) $(_h) : $(LOCATE_SOURCE) ;
+
+ if $(YACC)
+ {
+ DEPENDS $(<) $(_h) : $(>) ;
+ Yacc1 $(<) $(_h) : $(>) ;
+ YaccMv $(<) $(_h) : $(>) ;
+ Clean clean : $(<) $(_h) ;
+ }
+
+ # Make sure someone includes $(_h) else it will be a deadly independent
+ # target.
+ INCLUDES $(<) : $(_h) ;
+}
+
+#
+# Utility rules; no side effects on these.
+#
+
+rule FGrist
+{
+ # Turn individual elements in $(<) into grist.
+
+ local _g _i ;
+
+ _g = $(<[1]) ;
+
+ for _i in $(<[2-])
+ {
+ _g = $(_g)!$(_i) ;
+ }
+
+ return $(_g) ;
+}
+
+rule FGristFiles
+{
+ if ! $(SOURCE_GRIST)
+ {
+ return $(<) ;
+ }
+ else
+ {
+ return $(<:G=$(SOURCE_GRIST)) ;
+ }
+}
+
+rule FGristSourceFiles
+{
+ # Produce source file name name with grist in it,
+ # if SOURCE_GRIST is set.
+
+ # Leave header files alone, because they have a global
+ # visibility.
+
+ if ! $(SOURCE_GRIST)
+ {
+ return $(<) ;
+ }
+ else
+ {
+ local _i _o ;
+
+ for _i in $(<)
+ {
+ switch $(_i)
+ {
+ case *.h : _o += $(_i) ;
+ case * : _o += $(_i:G=$(SOURCE_GRIST)) ;
+ }
+ }
+
+ return $(_o) ;
+ }
+}
+
+rule FConcat
+{
+ # Puts the variables together, removing spaces.
+
+ local _t _r ;
+
+ $(_r) = $(<[1]) ;
+
+ for _t in $(<[2-])
+ {
+ $(_r) = $(_r)$(_t) ;
+ }
+
+ return $(_r) ;
+}
+
+rule FSubDir
+{
+ local _i _d ;
+
+ # If $(>) is the path to the current directory, compute the
+ # path (using ../../ etc) back to that root directory.
+ # Sets result in $(<)
+
+ if ! $(<[1])
+ {
+ _d = $(DOT) ;
+ }
+ else
+ {
+ _d = $(DOTDOT) ;
+
+ for _i in $(<[2-])
+ {
+ _d = $(_d:R=$(DOTDOT)) ;
+ }
+ }
+
+ return $(_d) ;
+}
+
+rule FDirName
+{
+ local _s _i ;
+
+ # Turn individual elements in $(<) into a usable path.
+
+ if ! $(<)
+ {
+ _s = $(DOT) ;
+ }
+ else if $(VMS)
+ {
+ # This handles the following cases:
+ # a -> [.a]
+ # a b c -> [.a.b.c]
+ # x: -> x:
+ # x: a -> x:[a]
+ # x:[a] b -> x:[a.b]
+
+ switch $(<[1])
+ {
+ case *:* : _s = $(<[1]) ;
+ case \\[*\\] : _s = $(<[1]) ;
+ case * : _s = [.$(<[1])] ;
+ }
+
+ for _i in [.$(<[2-])]
+ {
+ _s = $(_i:R=$(_s)) ;
+ }
+ }
+ else if $(MAC)
+ {
+ _s = $(DOT) ;
+
+ for _i in $(<)
+ {
+ _s = $(_i:R=$(_s)) ;
+ }
+ }
+ else
+ {
+ _s = $(<[1]) ;
+
+ for _i in $(<[2-])
+ {
+ _s = $(_i:R=$(_s)) ;
+ }
+ }
+
+ return $(_s) ;
+}
+
+
+rule _makeCommon
+{
+ # strip common initial elements
+
+ if $($(<)[1]) && $($(<)[1]) = $($(>)[1])
+ {
+ $(<) = $($(<)[2-]) ;
+ $(>) = $($(>)[2-]) ;
+ _makeCommon $(<) : $(>) ;
+ }
+}
+
+
+rule FRelPath
+{
+ local _l _r ;
+
+ # first strip off common parts
+
+ _l = $(<) ;
+ _r = $(>) ;
+
+ _makeCommon _l : _r ;
+
+ # now make path to root and path down
+
+ _l = [ FSubDir $(_l) ] ;
+ _r = [ FDirName $(_r) ] ;
+
+ # Concatenate and save
+
+ # XXX This should be better
+
+ if $(_r) = $(DOT) {
+ return $(_l) ;
+ } else {
+ return $(_r:R=$(_l)) ;
+ }
+}
+
+rule FAppendSuffix
+{
+ # E.g., "FAppendSuffix yacc lex foo.bat : $(SUFEXE) ;"
+ # returns (yacc,lex,foo.bat) on Unix and
+ # (yacc.exe,lex.exe,foo.bat) on NT.
+
+ if $(>)
+ {
+ local _i _o ;
+
+ for _i in $(<)
+ {
+ if $(_i:S)
+ {
+ _o += $(_i) ;
+ }
+ else
+ {
+ _o += $(_i:S=$(>)) ;
+ }
+ }
+ return $(_o) ;
+ }
+ else
+ {
+ return $(<) ;
+ }
+}
+
+rule unmakeDir
+{
+ if $(>[1]:D) && $(>[1]:D) != $(>[1]) && $(>[1]:D) != \\\\
+ {
+ unmakeDir $(<) : $(>[1]:D) $(>[1]:BS) $(>[2-]) ;
+ }
+ else
+ {
+ $(<) = $(>) ;
+ }
+}
+
+
+rule FConvertToSlashes
+{
+ local _d, _s, _i ;
+
+ unmakeDir _d : $(<) ;
+
+ _s = $(_d[1]) ;
+ for _i in $(_d[2-])
+ {
+ _s = $(_s)/$(_i) ;
+ }
+ return $(_s) ;
+}
+
+
+#
+# Actions
+#
+
+#
+# First the defaults
+#
+
+actions updated together piecemeal Archive
+{
+ $(AR) $(<) $(>)
+}
+
+actions As
+{
+ $(AS) $(ASFLAGS) -I$(HDRS) -o $(<) $(>)
+}
+
+actions C++
+{
+ $(C++) -c $(C++FLAGS) $(OPTIM) -I$(HDRS) -o $(<) $(>)
+}
+
+actions Cc
+{
+ $(CC) -c $(CCFLAGS) $(OPTIM) -I$(HDRS) -o $(<) $(>)
+}
+
+actions Chgrp
+{
+ $(CHGRP) $(GROUP) $(<)
+}
+
+actions Chmod1
+{
+ $(CHMOD) $(MODE) $(<)
+}
+
+actions Chown
+{
+ $(CHOWN) $(OWNER) $(<)
+}
+
+actions piecemeal together existing Clean
+{
+ $(RM) $(>)
+}
+
+actions File
+{
+ $(CP) $(>) $(<)
+}
+
+actions GenFile1
+{
+ $(>[1]) $(<) $(>[2-])
+}
+
+actions Fortran
+{
+ $(FORTRAN) $(FORTRANFLAGS) -o $(<) $(>)
+}
+
+actions HardLink
+{
+ $(RM) $(<) && $(LN) $(>) $(<)
+}
+
+actions Install
+{
+ $(CP) $(>) $(<)
+}
+
+actions Lex
+{
+ $(LEX) $(>)
+}
+
+actions LexMv
+{
+ $(MV) lex.yy.c $(<)
+}
+
+actions Link bind NEEDLIBS
+{
+ $(LINK) $(LINKFLAGS) -o $(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)
+}
+
+actions MkDir1
+{
+ $(MKDIR) $(<)
+}
+
+actions together Ranlib
+{
+ $(RANLIB) $(<)
+}
+
+actions quietly updated piecemeal together RmTemps
+{
+ $(RM) $(>)
+}
+
+actions Shell
+{
+ $(AWK) '
+ NR == 1 { print "$(SHELLHEADER)" }
+ NR == 1 && /^[#:]/ { next }
+ /^##/ { next }
+ { print }
+ ' < $(>) > $(<)
+}
+
+actions Yacc1
+{
+ $(YACC) $(YACCFLAGS) $(>)
+}
+
+actions YaccMv
+{
+ $(MV) $(YACCFILES).c $(<[1])
+ $(MV) $(YACCFILES).h $(<[2])
+}
+
+#
+# RELOCATE - for compilers with broken -o flags
+#
+
+if $(RELOCATE)
+{
+ actions C++
+ {
+ $(C++) -c $(C++FLAGS) $(OPTIM) -I$(HDRS) $(>)
+ }
+
+ actions Cc
+ {
+ $(CC) -c $(CCFLAGS) $(OPTIM) -I$(HDRS) $(>)
+ }
+
+ actions ignore CcMv
+ {
+ [ $(<) != $(>:BS=$(SUFOBJ)) ] && $(MV) $(>:BS=$(SUFOBJ)) $(<)
+ }
+}
+
+#
+# NOARUPDATE - can't update an archive
+#
+
+if $(NOARUPDATE)
+{
+ actions Archive
+ {
+ $(AR) $(<) $(>)
+ }
+}
+
+#
+# NT specific actions
+#
+
+if $(NT)
+{
+ if $(TOOLSET) = VISUALC || $(TOOLSET) = VC7 || $(TOOLSET) = INTELC
+ {
+ actions updated together piecemeal Archive
+ {
+ if exist $(<) set _$(<:B)_=$(<)
+ $(AR) /out:$(<) %_$(<:B)_% $(>)
+ }
+
+ actions As
+ {
+ $(AS) /Ml /p /v /w2 $(>) $(<) ,nul,nul;
+ }
+
+ actions Cc
+ {
+ $(CC) /c $(CCFLAGS) $(OPTIM) /Fo$(<) /I$(HDRS) /I$(STDHDRS) $(>)
+ }
+
+ actions C++
+ {
+ $(C++) /c $(C++FLAGS) $(OPTIM) /Fo$(<) /I$(HDRS) /I$(STDHDRS) /Tp$(>)
+ }
+
+ actions Link bind NEEDLIBS
+ {
+ $(LINK) $(LINKFLAGS) /out:$(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)
+ }
+ }
+ else if $(TOOLSET) = VISUALC16
+ {
+ actions updated together piecemeal Archive
+ {
+ $(AR) $(<) -+$(>)
+ }
+
+ actions Cc
+ {
+ $(CC) /c $(CCFLAGS) $(OPTIM) /Fo$(<) /I$(HDRS) $(>)
+ }
+
+ actions C++
+ {
+ $(C++) /c $(C++FLAGS) $(OPTIM) /Fo$(<) /I$(HDRS) /Tp$(>)
+ }
+
+ actions Link bind NEEDLIBS
+ {
+ $(LINK) $(LINKFLAGS) /out:$(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)
+ }
+ }
+ else if $(TOOLSET) = BORLANDC
+ {
+ actions updated together piecemeal Archive
+ {
+ $(AR) $(<) -+$(>)
+ }
+
+ actions Link bind NEEDLIBS
+ {
+ $(LINK) -e$(<) $(LINKFLAGS) $(UNDEFS) -L$(LINKLIBS) $(NEEDLIBS) $(>)
+ }
+
+ actions C++
+ {
+ $(C++) -c $(C++FLAGS) $(OPTIM) -I$(HDRS) -o$(<) $(>)
+ }
+
+ actions Cc
+ {
+ $(CC) -c $(CCFLAGS) $(OPTIM) -I$(HDRS) -o$(<) $(>)
+ }
+
+ }
+ else if $(TOOLSET) = MINGW
+ {
+ actions together piecemeal Archive
+ {
+ $(AR) $(<) $(>:T)
+ }
+
+ actions Cc
+ {
+ $(CC) -c $(CCFLAGS) $(OPTIM) -I$(HDRS) -o$(<) $(>)
+ }
+
+ actions C++
+ {
+ $(C++) -c $(C++FLAGS) $(OPTIM) -I$(HDRS) -o$(<) $(>)
+ }
+ }
+ else if $(TOOLSET) = WATCOM
+ {
+ actions together piecemeal Archive
+ {
+ $(AR) $(<) +-$(>)
+ }
+
+ actions Cc
+ {
+ $(CC) $(CCFLAGS) $(OPTIM) /Fo=$(<) /I$(HDRS) $(>)
+ }
+
+ actions C++
+ {
+ $(C++) $(C++FLAGS) $(OPTIM) /Fo=$(<) /I$(HDRS) $(>)
+ }
+
+ actions Link bind NEEDLIBS
+ {
+ $(LINK) $(LINKFLAGS) /Fe=$(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)
+ }
+
+ actions Shell
+ {
+ $(CP) $(>) $(<)
+ }
+ }
+ else if $(TOOLSET) = LCC
+ {
+ actions together piecemeal Archive
+ {
+ $(AR) /out:$(<) $(>)
+ }
+
+ actions Cc
+ {
+ $(CC) $(CCFLAGS) $(OPTIM) -Fo$(<) -I$(HDRS) $(>)
+ }
+
+ actions Link bind NEEDLIBS
+ {
+ $(LINK) $(LINKFLAGS) -o $(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)
+ }
+
+ actions Shell
+ {
+ $(CP) $(>) $(<)
+ }
+ }
+}
+
+#
+# OS2 specific actions
+#
+
+else if $(OS2)
+{
+ if $(TOOLSET) = WATCOM
+ {
+ actions together piecemeal Archive
+ {
+ $(AR) $(<) +-$(>)
+ }
+
+ actions Cc
+ {
+ $(CC) $(CCFLAGS) $(OPTIM) /Fo=$(<) /I$(HDRS) $(>)
+ }
+
+ actions C++
+ {
+ $(C++) $(C++FLAGS) $(OPTIM) /Fo=$(<) /I$(HDRS) $(>)
+ }
+
+ actions Link bind NEEDLIBS
+ {
+ $(LINK) $(LINKFLAGS) /Fe=$(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)
+ }
+
+ actions Shell
+ {
+ $(CP) $(>) $(<)
+ }
+ }
+ else if $(TOOLSET) = EMX
+ {
+ actions together piecemeal Archive
+ {
+ $(AR) $(<) $(>:T)
+ }
+
+ actions Cc
+ {
+ $(CC) -c $(CCFLAGS) $(OPTIM) -I$(HDRS) -o$(<) $(>)
+ }
+
+ actions C++
+ {
+ $(C++) -c $(C++FLAGS) $(OPTIM) -I$(HDRS) -o$(<) $(>)
+ }
+ }
+}
+
+#
+# VMS specific actions
+#
+
+else if $(VMS)
+{
+ actions updated together piecemeal Archive
+ {
+ lib/replace $(<) $(>[1]) ,$(>[2-])
+ }
+
+ actions Cc
+ {
+ $(CC)/obj=$(<) $(CCFLAGS) $(OPTIM) $(SLASHINC) $(>)
+ }
+
+ actions C++
+ {
+ $(C++)/obj=$(<) $(C++FLAGS) $(OPTIM) $(SLASHINC) $(>)
+ }
+
+ actions piecemeal together existing Clean
+ {
+ $(RM) $(>[1]);* ,$(>[2-]);*
+ }
+
+ actions together quietly CreLib
+ {
+ if f$search("$(<)") .eqs. "" then lib/create $(<)
+ }
+
+ actions GenFile1
+ {
+ mcr $(>[1]) $(<) $(>[2-])
+ }
+
+ actions Link bind NEEDLIBS
+ {
+ $(LINK)/exe=$(<) $(LINKFLAGS) $(>[1]) ,$(>[2-]) ,$(NEEDLIBS)/lib ,$(LINKLIBS)
+ }
+
+ actions quietly updated piecemeal together RmTemps
+ {
+ $(RM) $(>[1]);* ,$(>[2-]);*
+ }
+
+ actions Shell
+ {
+ $(CP) $(>) $(<)
+ }
+}
+
+#
+# Mac specifc actions
+#
+
+else if $(MAC)
+{
+ actions together Archive
+ {
+ $(LINK) -library -o $(<) $(>)
+ }
+
+ actions Cc
+ {
+ set -e MWCincludes $(MACINC)
+ $(CC) -o $(<) $(CCFLAGS) $(OPTIM) $(>)
+ }
+
+ actions C++
+ {
+ set -e MWCincludes $(MACINC)
+ $(CC) -o $(<) $(C++FLAGS) $(OPTIM) $(>)
+ }
+
+ actions Link bind NEEDLIBS
+ {
+ $(LINK) -o $(<) $(LINKFLAGS) $(>) $(NEEDLIBS) "$(LINKLIBS)"
+ }
+}
+
+#
+# Backwards compatibility with jam 1, where rules were uppercased.
+#
+
+rule BULK { Bulk $(<) : $(>) ; }
+rule FILE { File $(<) : $(>) ; }
+rule HDRRULE { HdrRule $(<) : $(>) ; }
+rule INSTALL { Install $(<) : $(>) ; }
+rule LIBRARY { Library $(<) : $(>) ; }
+rule LIBS { LinkLibraries $(<) : $(>) ; }
+rule LINK { Link $(<) : $(>) ; }
+rule MAIN { Main $(<) : $(>) ; }
+rule SETUID { Setuid $(<) ; }
+rule SHELL { Shell $(<) : $(>) ; }
+rule UNDEFINES { Undefines $(<) : $(>) ; }
+
+# Old INSTALL* didn't take dest directory.
+
+rule INSTALLBIN { InstallBin $(BINDIR) : $(<) ; }
+rule INSTALLLIB { InstallLib $(LIBDIR) : $(<) ; }
+rule INSTALLMAN { InstallMan $(MANDIR) : $(<) ; }
+
+# Compatibility with jam 2.2.
+
+rule addDirName { $(<) += [ FDirName $(>) ] ; }
+rule makeDirName { $(<) = [ FDirName $(>) ] ; }
+rule makeGristedName { $(<) = [ FGristSourceFiles $(>) ] ; }
+rule makeRelPath { $(<[1]) = [ FRelPath $(<[2-]) : $(>) ] ; }
+rule makeSuffixed { $(<[1]) = [ FAppendSuffix $(>) : $(<[2]) ] ; }
+
+#
+# Now include the user's Jamfile.
+#
+
+{
+ if $(JAMFILE) { include $(JAMFILE) ; }
+}
+
+}
diff --git a/jam-files/engine/boost-jam.spec b/jam-files/engine/boost-jam.spec
new file mode 100644
index 00000000..bc572fc9
--- /dev/null
+++ b/jam-files/engine/boost-jam.spec
@@ -0,0 +1,64 @@
+Name: boost-jam
+Version: 3.1.19
+Summary: Build tool
+Release: 1
+Source: %{name}-%{version}.tgz
+
+License: Boost Software License, Version 1.0
+Group: Development/Tools
+URL: http://www.boost.org
+Packager: Rene Rivera <grafik@redshift-software.com>
+BuildRoot: /var/tmp/%{name}-%{version}.root
+
+%description
+Boost Jam is a build tool based on FTJam, which in turn is based on
+Perforce Jam. It contains significant improvements made to facilitate
+its use in the Boost Build System, but should be backward compatible
+with Perforce Jam.
+
+Authors:
+ Perforce Jam : Cristopher Seiwald
+ FT Jam : David Turner
+ Boost Jam : David Abrahams
+
+Copyright:
+ /+\
+ +\ Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
+ \+/
+ License is hereby granted to use this software and distribute it
+ freely, as long as this copyright notice is retained and modifications
+ are clearly marked.
+ ALL WARRANTIES ARE HEREBY DISCLAIMED.
+
+Also:
+ Copyright 2001-2006 David Abrahams.
+ Copyright 2002-2006 Rene Rivera.
+ Copyright 2003-2006 Vladimir Prus.
+
+ Distributed under the Boost Software License, Version 1.0.
+ (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+
+%prep
+%setup -n %{name}-%{version}
+
+%build
+LOCATE_TARGET=bin ./build.sh $BOOST_JAM_TOOLSET
+
+%install
+rm -rf $RPM_BUILD_ROOT
+mkdir -p $RPM_BUILD_ROOT%{_bindir}
+mkdir -p $RPM_BUILD_ROOT%{_docdir}/%{name}-%{version}
+install -m 755 bin/bjam $RPM_BUILD_ROOT%{_bindir}/bjam-%{version}
+ln -sf bjam-%{version} $RPM_BUILD_ROOT%{_bindir}/bjam
+cp -R *.html *.png *.css LICENSE*.txt images jam $RPM_BUILD_ROOT%{_docdir}/%{name}-%{version}
+
+find $RPM_BUILD_ROOT -name CVS -type d -exec rm -r {} \;
+
+%files
+%defattr(-,root,root)
+%attr(755,root,root) /usr/bin/*
+%doc %{_docdir}/%{name}-%{version}
+
+
+%clean
+rm -rf $RPM_BUILD_ROOT
diff --git a/jam-files/engine/boost-no-inspect b/jam-files/engine/boost-no-inspect
new file mode 100644
index 00000000..8a06f3a7
--- /dev/null
+++ b/jam-files/engine/boost-no-inspect
@@ -0,0 +1 @@
+this really out of our hands, so tell inspect to ignore directory \ No newline at end of file
diff --git a/jam-files/engine/build.bat b/jam-files/engine/build.bat
new file mode 100644
index 00000000..f927b769
--- /dev/null
+++ b/jam-files/engine/build.bat
@@ -0,0 +1,532 @@
+@ECHO OFF
+
+REM ~ Copyright 2002-2007 Rene Rivera.
+REM ~ Distributed under the Boost Software License, Version 1.0.
+REM ~ (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+
+setlocal
+goto Start
+
+
+:Set_Error
+color 00
+goto :eof
+
+
+:Clear_Error
+ver >nul
+goto :eof
+
+
+:Error_Print
+REM Output an error message and set the errorlevel to indicate failure.
+setlocal
+ECHO ###
+ECHO ### %1
+ECHO ###
+ECHO ### You can specify the toolset as the argument, i.e.:
+ECHO ### .\build.bat msvc
+ECHO ###
+ECHO ### Toolsets supported by this script are: borland, como, gcc, gcc-nocygwin,
+ECHO ### intel-win32, metrowerks, mingw, msvc, vc7, vc8, vc9, vc10
+ECHO ###
+call :Set_Error
+endlocal
+goto :eof
+
+
+:Test_Path
+REM Tests for the given file(executable) presence in the directories in the PATH
+REM environment variable. Additionaly sets FOUND_PATH to the path of the
+REM found file.
+call :Clear_Error
+setlocal
+set test=%~$PATH:1
+endlocal
+if not errorlevel 1 set FOUND_PATH=%~dp$PATH:1
+goto :eof
+
+
+:Test_Option
+REM Tests whether the given string is in the form of an option: "--*"
+call :Clear_Error
+setlocal
+set test=%1
+if not defined test (
+ call :Set_Error
+ goto Test_Option_End
+)
+set test=###%test%###
+set test=%test:"###=%
+set test=%test:###"=%
+set test=%test:###=%
+if not "-" == "%test:~1,1%" call :Set_Error
+:Test_Option_End
+endlocal
+goto :eof
+
+
+:Test_Empty
+REM Tests whether the given string is not empty
+call :Clear_Error
+setlocal
+set test=%1
+if not defined test (
+ call :Clear_Error
+ goto Test_Empty_End
+)
+set test=###%test%###
+set test=%test:"###=%
+set test=%test:###"=%
+set test=%test:###=%
+if not "" == "%test%" call :Set_Error
+:Test_Empty_End
+endlocal
+goto :eof
+
+
+:Call_If_Exists
+if EXIST %1 call %*
+goto :eof
+
+
+:Guess_Toolset
+REM Try and guess the toolset to bootstrap the build with...
+REM Sets BOOST_JAM_TOOLSET to the first found toolset.
+REM May also set BOOST_JAM_TOOLSET_ROOT to the
+REM location of the found toolset.
+
+call :Clear_Error
+call :Test_Empty %ProgramFiles%
+if not errorlevel 1 set ProgramFiles=C:\Program Files
+
+call :Clear_Error
+if NOT "_%VS100COMNTOOLS%_" == "__" (
+ set "BOOST_JAM_TOOLSET=vc10"
+ set "BOOST_JAM_TOOLSET_ROOT=%VS100COMNTOOLS%..\..\VC\"
+ goto :eof)
+call :Clear_Error
+if EXIST "%ProgramFiles%\Microsoft Visual Studio 10.0\VC\VCVARSALL.BAT" (
+ set "BOOST_JAM_TOOLSET=vc10"
+ set "BOOST_JAM_TOOLSET_ROOT=%ProgramFiles%\Microsoft Visual Studio 10.0\VC\"
+ goto :eof)
+call :Clear_Error
+if NOT "_%VS90COMNTOOLS%_" == "__" (
+ set "BOOST_JAM_TOOLSET=vc9"
+ set "BOOST_JAM_TOOLSET_ROOT=%VS90COMNTOOLS%..\..\VC\"
+ goto :eof)
+call :Clear_Error
+if EXIST "%ProgramFiles%\Microsoft Visual Studio 9.0\VC\VCVARSALL.BAT" (
+ set "BOOST_JAM_TOOLSET=vc9"
+ set "BOOST_JAM_TOOLSET_ROOT=%ProgramFiles%\Microsoft Visual Studio 9.0\VC\"
+ goto :eof)
+call :Clear_Error
+if NOT "_%VS80COMNTOOLS%_" == "__" (
+ set "BOOST_JAM_TOOLSET=vc8"
+ set "BOOST_JAM_TOOLSET_ROOT=%VS80COMNTOOLS%..\..\VC\"
+ goto :eof)
+call :Clear_Error
+if EXIST "%ProgramFiles%\Microsoft Visual Studio 8\VC\VCVARSALL.BAT" (
+ set "BOOST_JAM_TOOLSET=vc8"
+ set "BOOST_JAM_TOOLSET_ROOT=%ProgramFiles%\Microsoft Visual Studio 8\VC\"
+ goto :eof)
+call :Clear_Error
+if NOT "_%VS71COMNTOOLS%_" == "__" (
+ set "BOOST_JAM_TOOLSET=vc7"
+ set "BOOST_JAM_TOOLSET_ROOT=%VS71COMNTOOLS%\..\..\VC7\"
+ goto :eof)
+call :Clear_Error
+if NOT "_%VCINSTALLDIR%_" == "__" (
+ REM %VCINSTALLDIR% is also set for VC9 (and probably VC8)
+ set "BOOST_JAM_TOOLSET=vc7"
+ set "BOOST_JAM_TOOLSET_ROOT=%VCINSTALLDIR%\VC7\"
+ goto :eof)
+call :Clear_Error
+if EXIST "%ProgramFiles%\Microsoft Visual Studio .NET 2003\VC7\bin\VCVARS32.BAT" (
+ set "BOOST_JAM_TOOLSET=vc7"
+ set "BOOST_JAM_TOOLSET_ROOT=%ProgramFiles%\Microsoft Visual Studio .NET 2003\VC7\"
+ goto :eof)
+call :Clear_Error
+if EXIST "%ProgramFiles%\Microsoft Visual Studio .NET\VC7\bin\VCVARS32.BAT" (
+ set "BOOST_JAM_TOOLSET=vc7"
+ set "BOOST_JAM_TOOLSET_ROOT=%ProgramFiles%\Microsoft Visual Studio .NET\VC7\"
+ goto :eof)
+call :Clear_Error
+if NOT "_%MSVCDir%_" == "__" (
+ set "BOOST_JAM_TOOLSET=msvc"
+ set "BOOST_JAM_TOOLSET_ROOT=%MSVCDir%\"
+ goto :eof)
+call :Clear_Error
+if EXIST "%ProgramFiles%\Microsoft Visual Studio\VC98\bin\VCVARS32.BAT" (
+ set "BOOST_JAM_TOOLSET=msvc"
+ set "BOOST_JAM_TOOLSET_ROOT=%ProgramFiles%\Microsoft Visual Studio\VC98\"
+ goto :eof)
+call :Clear_Error
+if EXIST "%ProgramFiles%\Microsoft Visual C++\VC98\bin\VCVARS32.BAT" (
+ set "BOOST_JAM_TOOLSET=msvc"
+ set "BOOST_JAM_TOOLSET_ROOT=%ProgramFiles%\Microsoft Visual C++\VC98\"
+ goto :eof)
+call :Clear_Error
+call :Test_Path cl.exe
+if not errorlevel 1 (
+ set "BOOST_JAM_TOOLSET=msvc"
+ set "BOOST_JAM_TOOLSET_ROOT=%FOUND_PATH%..\"
+ goto :eof)
+call :Clear_Error
+call :Test_Path vcvars32.bat
+if not errorlevel 1 (
+ set "BOOST_JAM_TOOLSET=msvc"
+ call "%FOUND_PATH%VCVARS32.BAT"
+ set "BOOST_JAM_TOOLSET_ROOT=%MSVCDir%\"
+ goto :eof)
+call :Clear_Error
+if EXIST "C:\Borland\BCC55\Bin\bcc32.exe" (
+ set "BOOST_JAM_TOOLSET=borland"
+ set "BOOST_JAM_TOOLSET_ROOT=C:\Borland\BCC55\"
+ goto :eof)
+call :Clear_Error
+call :Test_Path bcc32.exe
+if not errorlevel 1 (
+ set "BOOST_JAM_TOOLSET=borland"
+ set "BOOST_JAM_TOOLSET_ROOT=%FOUND_PATH%..\"
+ goto :eof)
+call :Clear_Error
+call :Test_Path icl.exe
+if not errorlevel 1 (
+ set "BOOST_JAM_TOOLSET=intel-win32"
+ set "BOOST_JAM_TOOLSET_ROOT=%FOUND_PATH%..\"
+ goto :eof)
+call :Clear_Error
+if EXIST "C:\MinGW\bin\gcc.exe" (
+ set "BOOST_JAM_TOOLSET=mingw"
+ set "BOOST_JAM_TOOLSET_ROOT=C:\MinGW\"
+ goto :eof)
+call :Clear_Error
+if NOT "_%CWFolder%_" == "__" (
+ set "BOOST_JAM_TOOLSET=metrowerks"
+ set "BOOST_JAM_TOOLSET_ROOT=%CWFolder%\"
+ goto :eof )
+call :Clear_Error
+call :Test_Path mwcc.exe
+if not errorlevel 1 (
+ set "BOOST_JAM_TOOLSET=metrowerks"
+ set "BOOST_JAM_TOOLSET_ROOT=%FOUND_PATH%..\..\"
+ goto :eof)
+call :Clear_Error
+call :Error_Print "Could not find a suitable toolset."
+goto :eof
+
+
+:Guess_Yacc
+REM Tries to find bison or yacc in common places so we can build the grammar.
+call :Clear_Error
+call :Test_Path yacc.exe
+if not errorlevel 1 (
+ set "YACC=yacc -d"
+ goto :eof)
+call :Clear_Error
+call :Test_Path bison.exe
+if not errorlevel 1 (
+ set "YACC=bison -d --yacc"
+ goto :eof)
+call :Clear_Error
+if EXIST "C:\Program Files\GnuWin32\bin\bison.exe" (
+ set "YACC=C:\Program Files\GnuWin32\bin\bison.exe" -d --yacc
+ goto :eof)
+call :Clear_Error
+call :Error_Print "Could not find Yacc to build the Jam grammar."
+goto :eof
+
+
+:Start
+set BOOST_JAM_TOOLSET=
+set BOOST_JAM_ARGS=
+
+REM If no arguments guess the toolset;
+REM or if first argument is an option guess the toolset;
+REM otherwise the argument is the toolset to use.
+call :Clear_Error
+call :Test_Empty %1
+if not errorlevel 1 (
+ call :Guess_Toolset
+ if not errorlevel 1 ( goto Setup_Toolset ) else ( goto Finish )
+)
+
+call :Clear_Error
+call :Test_Option %1
+if not errorlevel 1 (
+ call :Guess_Toolset
+ if not errorlevel 1 ( goto Setup_Toolset ) else ( goto Finish )
+)
+
+call :Clear_Error
+set BOOST_JAM_TOOLSET=%1
+shift
+goto Setup_Toolset
+
+
+:Setup_Toolset
+REM Setup the toolset command and options. This bit of code
+REM needs to be flexible enough to handle both when
+REM the toolset was guessed at and found, or when the toolset
+REM was indicated in the command arguments.
+REM NOTE: The strange multiple "if ?? == _toolset_" tests are that way
+REM because in BAT variables are subsituted only once during a single
+REM command. A complete "if ... ( commands ) else ( commands )"
+REM is a single command, even though it's in multiple lines here.
+:Setup_Args
+call :Clear_Error
+call :Test_Empty %1
+if not errorlevel 1 goto Config_Toolset
+call :Clear_Error
+call :Test_Option %1
+if errorlevel 1 (
+ set BOOST_JAM_ARGS=%BOOST_JAM_ARGS% %1
+ shift
+ goto Setup_Args
+)
+:Config_Toolset
+if NOT "_%BOOST_JAM_TOOLSET%_" == "_metrowerks_" goto Skip_METROWERKS
+if NOT "_%CWFolder%_" == "__" (
+ set "BOOST_JAM_TOOLSET_ROOT=%CWFolder%\"
+ )
+set "PATH=%BOOST_JAM_TOOLSET_ROOT%Other Metrowerks Tools\Command Line Tools;%PATH%"
+set "BOOST_JAM_CC=mwcc -runtime ss -cwd include -DNT -lkernel32.lib -ladvapi32.lib -luser32.lib"
+set "BOOST_JAM_OPT_JAM=-o bootstrap\jam0.exe"
+set "BOOST_JAM_OPT_MKJAMBASE=-o bootstrap\mkjambase0.exe"
+set "BOOST_JAM_OPT_YYACC=-o bootstrap\yyacc0.exe"
+set "_known_=1"
+:Skip_METROWERKS
+if NOT "_%BOOST_JAM_TOOLSET%_" == "_msvc_" goto Skip_MSVC
+if NOT "_%MSVCDir%_" == "__" (
+ set "BOOST_JAM_TOOLSET_ROOT=%MSVCDir%\"
+ )
+call :Call_If_Exists "%BOOST_JAM_TOOLSET_ROOT%bin\VCVARS32.BAT"
+if not "_%BOOST_JAM_TOOLSET_ROOT%_" == "__" (
+ set "PATH=%BOOST_JAM_TOOLSET_ROOT%bin;%PATH%"
+ )
+set "BOOST_JAM_CC=cl /nologo /GZ /Zi /MLd /Fobootstrap/ /Fdbootstrap/ -DNT -DYYDEBUG kernel32.lib advapi32.lib user32.lib"
+set "BOOST_JAM_OPT_JAM=/Febootstrap\jam0"
+set "BOOST_JAM_OPT_MKJAMBASE=/Febootstrap\mkjambase0"
+set "BOOST_JAM_OPT_YYACC=/Febootstrap\yyacc0"
+set "_known_=1"
+:Skip_MSVC
+if NOT "_%BOOST_JAM_TOOLSET%_" == "_vc7_" goto Skip_VC7
+if NOT "_%VS71COMNTOOLS%_" == "__" (
+ set "BOOST_JAM_TOOLSET_ROOT=%VS71COMNTOOLS%..\..\VC7\"
+ )
+if "_%VCINSTALLDIR%_" == "__" call :Call_If_Exists "%BOOST_JAM_TOOLSET_ROOT%bin\VCVARS32.BAT"
+if NOT "_%BOOST_JAM_TOOLSET_ROOT%_" == "__" (
+ if "_%VCINSTALLDIR%_" == "__" (
+ set "PATH=%BOOST_JAM_TOOLSET_ROOT%bin;%PATH%"
+ ) )
+set "BOOST_JAM_CC=cl /nologo /GZ /Zi /MLd /Fobootstrap/ /Fdbootstrap/ -DNT -DYYDEBUG kernel32.lib advapi32.lib user32.lib"
+set "BOOST_JAM_OPT_JAM=/Febootstrap\jam0"
+set "BOOST_JAM_OPT_MKJAMBASE=/Febootstrap\mkjambase0"
+set "BOOST_JAM_OPT_YYACC=/Febootstrap\yyacc0"
+set "_known_=1"
+:Skip_VC7
+if NOT "_%BOOST_JAM_TOOLSET%_" == "_vc8_" goto Skip_VC8
+if NOT "_%VS80COMNTOOLS%_" == "__" (
+ set "BOOST_JAM_TOOLSET_ROOT=%VS80COMNTOOLS%..\..\VC\"
+ )
+if "_%VCINSTALLDIR%_" == "__" call :Call_If_Exists "%BOOST_JAM_TOOLSET_ROOT%VCVARSALL.BAT" %BOOST_JAM_ARGS%
+if NOT "_%BOOST_JAM_TOOLSET_ROOT%_" == "__" (
+ if "_%VCINSTALLDIR%_" == "__" (
+ set "PATH=%BOOST_JAM_TOOLSET_ROOT%bin;%PATH%"
+ ) )
+set "BOOST_JAM_CC=cl /nologo /RTC1 /Zi /MTd /Fobootstrap/ /Fdbootstrap/ -DNT -DYYDEBUG -wd4996 kernel32.lib advapi32.lib user32.lib"
+set "BOOST_JAM_OPT_JAM=/Febootstrap\jam0"
+set "BOOST_JAM_OPT_MKJAMBASE=/Febootstrap\mkjambase0"
+set "BOOST_JAM_OPT_YYACC=/Febootstrap\yyacc0"
+set "_known_=1"
+:Skip_VC8
+if NOT "_%BOOST_JAM_TOOLSET%_" == "_vc9_" goto Skip_VC9
+if NOT "_%VS90COMNTOOLS%_" == "__" (
+ set "BOOST_JAM_TOOLSET_ROOT=%VS90COMNTOOLS%..\..\VC\"
+ )
+if "_%VCINSTALLDIR%_" == "__" call :Call_If_Exists "%BOOST_JAM_TOOLSET_ROOT%VCVARSALL.BAT" %BOOST_JAM_ARGS%
+if NOT "_%BOOST_JAM_TOOLSET_ROOT%_" == "__" (
+ if "_%VCINSTALLDIR%_" == "__" (
+ set "PATH=%BOOST_JAM_TOOLSET_ROOT%bin;%PATH%"
+ ) )
+set "BOOST_JAM_CC=cl /nologo /RTC1 /Zi /MTd /Fobootstrap/ /Fdbootstrap/ -DNT -DYYDEBUG -wd4996 kernel32.lib advapi32.lib user32.lib"
+set "BOOST_JAM_OPT_JAM=/Febootstrap\jam0"
+set "BOOST_JAM_OPT_MKJAMBASE=/Febootstrap\mkjambase0"
+set "BOOST_JAM_OPT_YYACC=/Febootstrap\yyacc0"
+set "_known_=1"
+:Skip_VC9
+if NOT "_%BOOST_JAM_TOOLSET%_" == "_vc10_" goto Skip_VC10
+if NOT "_%VS100COMNTOOLS%_" == "__" (
+ set "BOOST_JAM_TOOLSET_ROOT=%VS100COMNTOOLS%..\..\VC\"
+ )
+if "_%VCINSTALLDIR%_" == "__" call :Call_If_Exists "%BOOST_JAM_TOOLSET_ROOT%VCVARSALL.BAT" %BOOST_JAM_ARGS%
+if NOT "_%BOOST_JAM_TOOLSET_ROOT%_" == "__" (
+ if "_%VCINSTALLDIR%_" == "__" (
+ set "PATH=%BOOST_JAM_TOOLSET_ROOT%bin;%PATH%"
+ ) )
+set "BOOST_JAM_CC=cl /nologo /RTC1 /Zi /MTd /Fobootstrap/ /Fdbootstrap/ -DNT -DYYDEBUG -wd4996 kernel32.lib advapi32.lib user32.lib"
+set "BOOST_JAM_OPT_JAM=/Febootstrap\jam0"
+set "BOOST_JAM_OPT_MKJAMBASE=/Febootstrap\mkjambase0"
+set "BOOST_JAM_OPT_YYACC=/Febootstrap\yyacc0"
+set "_known_=1"
+:Skip_VC10
+if NOT "_%BOOST_JAM_TOOLSET%_" == "_borland_" goto Skip_BORLAND
+if "_%BOOST_JAM_TOOLSET_ROOT%_" == "__" (
+ call :Test_Path bcc32.exe )
+if "_%BOOST_JAM_TOOLSET_ROOT%_" == "__" (
+ if not errorlevel 1 (
+ set "BOOST_JAM_TOOLSET_ROOT=%FOUND_PATH%..\"
+ ) )
+if not "_%BOOST_JAM_TOOLSET_ROOT%_" == "__" (
+ set "PATH=%BOOST_JAM_TOOLSET_ROOT%Bin;%PATH%"
+ )
+set "BOOST_JAM_CC=bcc32 -WC -w- -q -I%BOOST_JAM_TOOLSET_ROOT%Include -L%BOOST_JAM_TOOLSET_ROOT%Lib /DNT -nbootstrap"
+set "BOOST_JAM_OPT_JAM=-ejam0"
+set "BOOST_JAM_OPT_MKJAMBASE=-emkjambasejam0"
+set "BOOST_JAM_OPT_YYACC=-eyyacc0"
+set "_known_=1"
+:Skip_BORLAND
+if NOT "_%BOOST_JAM_TOOLSET%_" == "_como_" goto Skip_COMO
+set "BOOST_JAM_CC=como -DNT"
+set "BOOST_JAM_OPT_JAM=-o bootstrap\jam0.exe"
+set "BOOST_JAM_OPT_MKJAMBASE=-o bootstrap\mkjambase0.exe"
+set "BOOST_JAM_OPT_YYACC=-o bootstrap\yyacc0.exe"
+set "_known_=1"
+:Skip_COMO
+if NOT "_%BOOST_JAM_TOOLSET%_" == "_gcc_" goto Skip_GCC
+set "BOOST_JAM_CC=gcc -DNT"
+set "BOOST_JAM_OPT_JAM=-o bootstrap\jam0.exe"
+set "BOOST_JAM_OPT_MKJAMBASE=-o bootstrap\mkjambase0.exe"
+set "BOOST_JAM_OPT_YYACC=-o bootstrap\yyacc0.exe"
+set "_known_=1"
+:Skip_GCC
+if NOT "_%BOOST_JAM_TOOLSET%_" == "_gcc-nocygwin_" goto Skip_GCC_NOCYGWIN
+set "BOOST_JAM_CC=gcc -DNT -mno-cygwin"
+set "BOOST_JAM_OPT_JAM=-o bootstrap\jam0.exe"
+set "BOOST_JAM_OPT_MKJAMBASE=-o bootstrap\mkjambase0.exe"
+set "BOOST_JAM_OPT_YYACC=-o bootstrap\yyacc0.exe"
+set "_known_=1"
+:Skip_GCC_NOCYGWIN
+if NOT "_%BOOST_JAM_TOOLSET%_" == "_intel-win32_" goto Skip_INTEL_WIN32
+set "BOOST_JAM_CC=icl -DNT /nologo kernel32.lib advapi32.lib user32.lib"
+set "BOOST_JAM_OPT_JAM=/Febootstrap\jam0"
+set "BOOST_JAM_OPT_MKJAMBASE=/Febootstrap\mkjambase0"
+set "BOOST_JAM_OPT_YYACC=/Febootstrap\yyacc0"
+set "_known_=1"
+:Skip_INTEL_WIN32
+if NOT "_%BOOST_JAM_TOOLSET%_" == "_mingw_" goto Skip_MINGW
+if not "_%BOOST_JAM_TOOLSET_ROOT%_" == "__" (
+ set "PATH=%BOOST_JAM_TOOLSET_ROOT%bin;%PATH%"
+ )
+set "BOOST_JAM_CC=gcc -DNT"
+set "BOOST_JAM_OPT_JAM=-o bootstrap\jam0.exe"
+set "BOOST_JAM_OPT_MKJAMBASE=-o bootstrap\mkjambase0.exe"
+set "BOOST_JAM_OPT_YYACC=-o bootstrap\yyacc0.exe"
+set "_known_=1"
+:Skip_MINGW
+call :Clear_Error
+if "_%_known_%_" == "__" (
+ call :Error_Print "Unknown toolset: %BOOST_JAM_TOOLSET%"
+)
+if errorlevel 1 goto Finish
+
+echo ###
+echo ### Using '%BOOST_JAM_TOOLSET%' toolset.
+echo ###
+
+set YYACC_SOURCES=yyacc.c
+set MKJAMBASE_SOURCES=mkjambase.c
+set BJAM_SOURCES=
+set BJAM_SOURCES=%BJAM_SOURCES% command.c compile.c debug.c execnt.c expand.c filent.c glob.c hash.c
+set BJAM_SOURCES=%BJAM_SOURCES% hdrmacro.c headers.c jam.c jambase.c jamgram.c lists.c make.c make1.c
+set BJAM_SOURCES=%BJAM_SOURCES% newstr.c option.c output.c parse.c pathunix.c regexp.c
+set BJAM_SOURCES=%BJAM_SOURCES% rules.c scan.c search.c subst.c timestamp.c variable.c modules.c
+set BJAM_SOURCES=%BJAM_SOURCES% strings.c filesys.c builtins.c md5.c pwd.c class.c w32_getreg.c native.c
+set BJAM_SOURCES=%BJAM_SOURCES% modules/set.c modules/path.c modules/regex.c
+set BJAM_SOURCES=%BJAM_SOURCES% modules/property-set.c modules/sequence.c modules/order.c
+
+set BJAM_UPDATE=
+:Check_Update
+call :Test_Empty %1
+if not errorlevel 1 goto Check_Update_End
+call :Clear_Error
+setlocal
+set test=%1
+set test=###%test%###
+set test=%test:"###=%
+set test=%test:###"=%
+set test=%test:###=%
+if "%test%" == "--update" set BJAM_UPDATE=update
+endlocal
+shift
+if not "_%BJAM_UPDATE%_" == "_update_" goto Check_Update
+:Check_Update_End
+if "_%BJAM_UPDATE%_" == "_update_" (
+ if not exist ".\bootstrap\jam0.exe" (
+ set BJAM_UPDATE=
+ )
+)
+
+@echo ON
+@if "_%BJAM_UPDATE%_" == "_update_" goto Skip_Bootstrap
+if exist bootstrap rd /S /Q bootstrap
+md bootstrap
+@if not exist jamgram.y goto Bootstrap_GrammarPrep
+@if not exist jamgramtab.h goto Bootstrap_GrammarPrep
+@goto Skip_GrammarPrep
+:Bootstrap_GrammarPrep
+%BOOST_JAM_CC% %BOOST_JAM_OPT_YYACC% %YYACC_SOURCES%
+@if not exist ".\bootstrap\yyacc0.exe" goto Skip_GrammarPrep
+.\bootstrap\yyacc0 jamgram.y jamgramtab.h jamgram.yy
+:Skip_GrammarPrep
+@if not exist jamgram.c goto Bootstrap_GrammarBuild
+@if not exist jamgram.h goto Bootstrap_GrammarBuild
+@goto Skip_GrammarBuild
+:Bootstrap_GrammarBuild
+@echo OFF
+if "_%YACC%_" == "__" (
+ call :Guess_Yacc
+)
+if errorlevel 1 goto Finish
+@echo ON
+%YACC% jamgram.y
+@if errorlevel 1 goto Finish
+del /f jamgram.c
+rename y.tab.c jamgram.c
+del /f jamgram.h
+rename y.tab.h jamgram.h
+:Skip_GrammarBuild
+@echo ON
+@if exist jambase.c goto Skip_Jambase
+%BOOST_JAM_CC% %BOOST_JAM_OPT_MKJAMBASE% %MKJAMBASE_SOURCES%
+@if not exist ".\bootstrap\mkjambase0.exe" goto Skip_Jambase
+.\bootstrap\mkjambase0 jambase.c Jambase
+:Skip_Jambase
+%BOOST_JAM_CC% %BOOST_JAM_OPT_JAM% %BJAM_SOURCES%
+:Skip_Bootstrap
+@if not exist ".\bootstrap\jam0.exe" goto Skip_Jam
+@if "_%BJAM_UPDATE%_" == "_update_" goto Skip_Clean
+.\bootstrap\jam0 -f build.jam --toolset=%BOOST_JAM_TOOLSET% "--toolset-root=%BOOST_JAM_TOOLSET_ROOT% " clean
+:Skip_Clean
+@set args=%*
+@echo OFF
+:Set_Args
+setlocal
+call :Test_Empty %args%
+if not errorlevel 1 goto Set_Args_End
+set test=###%args:~0,2%###
+set test=%test:"###=%
+set test=%test:###"=%
+set test=%test:###=%
+set test=%test:~0,1%
+if "-" == "%test%" goto Set_Args_End
+endlocal
+set args=%args:~1%
+goto Set_Args
+:Set_Args_End
+@echo ON
+.\bootstrap\jam0 -f build.jam --toolset=%BOOST_JAM_TOOLSET% "--toolset-root=%BOOST_JAM_TOOLSET_ROOT% " %args%
+:Skip_Jam
+
+:Finish
diff --git a/jam-files/engine/build.jam b/jam-files/engine/build.jam
new file mode 100644
index 00000000..266b07a1
--- /dev/null
+++ b/jam-files/engine/build.jam
@@ -0,0 +1,1070 @@
+#~ Copyright 2002-2007 Rene Rivera.
+#~ Distributed under the Boost Software License, Version 1.0.
+#~ (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+
+# Clean env vars of any "extra" empty values.
+for local v in ARGV CC CFLAGS LIBS
+{
+ local values ;
+ for local x in $($(v))
+ {
+ if $(x) != ""
+ {
+ values += $(x) ;
+ }
+ }
+ $(v) = $(values) ;
+}
+
+# Platform related specifics.
+if $(OS) = NT { rule .path { return "$(<:J=\\)" ; } ./ = "/" ; }
+else if $(OS) = OS2 { rule .path { return "$(<:J=\\)" ; } ./ = "/" ; }
+else if $(OS) = VMS { rule .path { return "[.$(<:J=/)]" ; } }
+else if $(OS) = MAC { rule .path { return ":$(<:J=\:)" ; } }
+else { rule .path { return "$(<:J=/)" ; } }
+if $(OS) = VMS { . = "_" ; }
+else { . = "." ; }
+./ ?= "" ;
+
+# Info about what we are building.
+_VERSION_ = 3 1 19 ;
+NAME = boost-jam ;
+VERSION = $(_VERSION_:J=$(.)) ;
+RELEASE = 1 ;
+LICENSE = LICENSE_1_0 ;
+
+# Generate development debug binaries?
+if --debug in $(ARGV)
+{
+ debug = true ;
+}
+
+if --profile in $(ARGV)
+{
+ profile = true ;
+}
+
+# Attempt to generate and/or build the grammar?
+if --grammar in $(ARGV)
+{
+ grammar = true ;
+}
+
+# Do we need to add a default build type argument?
+if ! ( --release in $(ARGV) ) &&
+ ! ( --debug in $(ARGV) ) &&
+ ! ( --profile in $(ARGV) )
+{
+ ARGV += --release ;
+}
+
+# Enable, and configure, Python hooks.
+with-python = ;
+python-location = [ MATCH --with-python=(.*) : $(ARGV) ] ;
+if $(python-location)
+{
+ with-python = true ;
+}
+if $(with-python)
+{
+ if $(OS) = NT
+ {
+ --python-include = [ .path $(python-location) include ] ;
+ --python-lib = ;
+ for local v in 26 25 24 23 22
+ {
+ --python-lib ?=
+ [ GLOB [ .path $(python-location) libs ] : "python$(v).lib" ]
+ [ GLOB $(python-location) [ .path $(python-location) libs ]
+ $(Path) $(PATH) $(path) : "python$(v).dll" ]
+ ;
+ if ! $(--python-lib[2])
+ {
+ --python-lib = ;
+ }
+ }
+ --python-lib = $(--python-lib[1]) ;
+ }
+ else if $(OS) = MACOSX
+ {
+ --python-include = [ .path $(python-location) Headers ] ;
+ --python-lib = $(python-location) Python ;
+ }
+ else
+ {
+ --python-include = ;
+ --python-lib = ;
+ for local v in 2.6 2.5 2.4 2.3 2.2
+ {
+ local inc = [ GLOB [ .path $(python-location) include ] : python$(v) ] ;
+ local lib = [ GLOB [ .path $(python-location) lib ] : libpython$(v)* ] ;
+ if $(inc) && $(lib)
+ {
+ --python-include ?= $(inc) ;
+ --python-lib ?= $(lib[1]:D) python$(v) ;
+ }
+ }
+ }
+}
+
+# Boehm GC?
+if --gc in $(ARGV)
+{
+ --boehm-gc = true ;
+}
+if $(--boehm-gc)
+{
+ --extra-include += [ .path [ PWD ] "boehm_gc" "include" ] ;
+}
+
+# Duma?
+if --duma in $(ARGV)
+{
+ --duma = true ;
+}
+if $(--duma)
+{
+ --extra-include += [ .path [ PWD ] "duma" ] ;
+}
+
+# An explicit root for the toolset? (trim spaces)
+toolset-root = [ MATCH --toolset-root=(.*) : $(ARGV) ] ;
+{
+ local t = [ MATCH "[ ]*(.*)" : $(toolset-root:J=" ") ] ;
+ toolset-root = ;
+ while $(t)
+ {
+ t = [ MATCH "([^ ]+)([ ]*)(.*)" : $(t) ] ;
+ toolset-root += $(t[1]) ;
+ if $(t[3]) { toolset-root += $(t[2]) ; }
+ t = $(t[3]) ;
+ }
+ toolset-root = $(toolset-root:J="") ;
+}
+
+# Configure the implemented toolsets. These are minimal
+# commands and options to compile the full Jam. When
+# adding new toolsets make sure to add them to the
+# "known" list also.
+
+rule toolset ( name command .type ? : opt.out + : opt.define * : flags * : linklibs * )
+{
+ .type ?= "" ;
+ tool.$(name)$(.type).cc ?= $(command) ;
+ tool.$(name)$(.type).opt.out ?= $(opt.out) ;
+ tool.$(name)$(.type).opt.define ?= $(opt.define) ;
+ tool.$(name)$(.type).flags ?= $(flags) ;
+ tool.$(name)$(.type).linklibs ?= $(linklibs) ;
+ if ! $(name) in $(toolsets) { toolsets += $(name) ; }
+}
+
+rule if-os ( os + : yes-opt * : no-opt * )
+ { if $(os) in $(OS) { return $(yes-opt) ; } else { return $(no-opt) ; } }
+
+rule opt ( type : yes-opt * : no-opt * )
+ { if $(type) in $(ARGV) { return $(yes-opt) ; } else { return $(no-opt) ; } }
+
+## HP-UX aCC compiler
+toolset acc cc : "-o " : -D
+ : -Ae
+ [ opt --release : -s -O3 ]
+ [ opt --debug : -g -pg ]
+ -I$(--python-include) -I$(--extra-include)
+ : -L$(--python-lib[1]) -l$(--python-lib[2]) ;
+## Borland C++ 5.5.x
+toolset borland bcc32 : -e -n : /D
+ : -WC -w- -q "-I$(toolset-root)Include" "-L$(toolset-root)Lib"
+ [ opt --release : -O2 -vi -w-inl ]
+ [ opt --debug : -v -Od -vi- ]
+ -I$(--python-include) -I$(--extra-include)
+ : $(--python-lib[1]) ;
+## Generic Unix cc
+if ! $(CC) { CC = cc ; }
+toolset cc $(CC) : "-o " : -D
+ : $(CFLAGS)
+ [ opt --release : -s -O ]
+ [ opt --debug : -g ]
+ -I$(--python-include) -I$(--extra-include)
+ : $(LIBS) -L$(--python-lib[1]) -l$(--python-lib[2]) ;
+## Comeau C/C++ 4.x
+toolset como como : "-o " : -D
+ : --c
+ [ opt --release : --inlining ]
+ [ opt --debug : --no_inlining ]
+ -I$(--python-include) -I$(--extra-include)
+ : -L$(--python-lib[1]) -l$(--python-lib[2]) ;
+## Clang Linux 2.8+
+toolset clang clang : "-o " : -D
+ : -Wno-unused -Wno-format
+ [ opt --release : -Os ]
+ [ opt --debug : -g -O0 -fno-inline ]
+ [ opt --profile : -finline-functions -g ]
+ -I$(--python-include) -I$(--extra-include)
+ : -L$(--python-lib[1]) -l$(--python-lib[2]) ;
+## MacOSX Darwin, using GCC 2.9.x, 3.x
+toolset darwin cc : "-o " : -D
+ :
+ [ opt --release : -Wl,-x -O3 -finline-functions ]
+ [ opt --debug : -g -O0 -fno-inline -pg ]
+ [ opt --profile : -Wl,-x -O3 -finline-functions -g -pg ]
+ -I$(--python-include) -I$(--extra-include)
+ : -L$(--python-lib[1]) -l$(--python-lib[2]) ;
+## GCC 2.x, 3.x, 4.x
+toolset gcc gcc : "-o " : -D
+ : -pedantic -fno-strict-aliasing
+ [ opt --release : [ opt --symbols : -g : -s ] -O3 ]
+ [ opt --debug : -g -O0 -fno-inline ]
+ -I$(--python-include) -I$(--extra-include) -Wno-long-long
+ : -L$(--python-lib[1]) -l$(--python-lib[2]) ;
+## GCC 2.x, 3.x on CYGWIN but without cygwin1.dll
+toolset gcc-nocygwin gcc : "-o " : -D
+ : -s -O3 -mno-cygwin
+ [ opt --release : -finline-functions ]
+ [ opt --debug : -s -O3 -fno-inline -pg ]
+ -I$(--python-include) -I$(--extra-include)
+ : -L$(--python-lib[1]) -l$(--python-lib[2]) ;
+## Intel C/C++ for Darwin
+toolset intel-darwin icc : "-o " : -D
+ :
+ [ opt --release : -O3 ]
+ [ opt --debug : -g -O0 -p ]
+ -I$(--python-include) -I$(--extra-include)
+ : -L$(--python-lib[1]) -l$(--python-lib[2]) ;
+## Intel C/C++ for Linux
+toolset intel-linux icc : "-o " : -D
+ :
+ [ opt --release : -Xlinker -s -O3 ]
+ [ opt --debug : -g -O0 -p ]
+ -I$(--python-include) -I$(--extra-include)
+ : -L$(--python-lib[1]) -l$(--python-lib[2]) ;
+## Intel C/C++ for Win32
+toolset intel-win32 icl : /Fe : -D
+ : /nologo
+ [ opt --release : /MT /O2 /Ob2 /Gy /GF /GA /GB ]
+ [ opt --debug : /MTd /DEBUG /Z7 /Od /Ob0 ]
+ -I$(--python-include) -I$(--extra-include)
+ : kernel32.lib advapi32.lib user32.lib $(--python-lib[1]) ;
+## KCC ?
+toolset kcc KCC : "-o " : -D
+ :
+ [ opt --release : -s +K2 ]
+ [ opt --debug : -g +K0 ]
+ -I$(--python-include) -I$(--extra-include)
+ : -L$(--python-lib[1]) -l$(--python-lib[2]) ;
+## Borland Kylix
+toolset kylix bc++ : -o : -D
+ : -tC -q
+ [ opt --release : -O2 -vi -w-inl ]
+ [ opt --debug : -v -Od -vi- ]
+ -I$(--python-include) -I$(--extra-include)
+ : -L$(--python-lib[1]) -l$(--python-lib[2]) ;
+## Metrowerks CodeWarrior 8.x
+{
+ # Even though CW can compile all files at once, it crashes if it tries in the bjam case.
+ local mwcc = ; if $(OS) = NT { mwcc = mwcc ; } else { mwcc = mwc$(OSPLAT:L) ; }
+ mwcc ?= mwcc ;
+ toolset metrowerks $(mwcc) : "-o " : -D
+ : -c -lang c -subsystem console -cwd include
+ [ opt --release : -runtime ss -opt full -inline all ]
+ [ opt --debug : -runtime ssd -opt none -inline off ]
+ -I$(--python-include) -I$(--extra-include) ;
+ toolset metrowerks $(mwcc) .link : "-o " :
+ : -subsystem console -lkernel32.lib -ladvapi32.lib -luser32.lib
+ [ opt --release : -runtime ss ]
+ [ opt --debug : -runtime ssd ]
+ : $(--python-lib[1]) ;
+}
+## MINGW GCC
+toolset mingw gcc : "-o " : -D
+ :
+ [ opt --release : -s -O3 -finline-functions ]
+ [ opt --debug : -g -O0 -fno-inline -pg ]
+ -I$(--python-include) -I$(--extra-include)
+ : $(--python-lib[2]) ;
+## MIPS Pro
+toolset mipspro cc : "-o " : -D
+ :
+ [ opt --release : -s -O3 -g0 -INLINE:none ]
+ [ opt --debug : -g -O0 -INLINE ]
+ -I$(--python-include) -I$(--extra-include)
+ : -L$(--python-lib[1]) -l$(--python-lib[2]) ;
+## Microsoft Visual Studio C++ 6.x
+toolset msvc cl : /Fe /Fe /Fd /Fo : -D
+ : /nologo
+ [ opt --release : /ML /O2 /Ob2 /Gy /GF /GA /GB ]
+ [ opt --debug : /MLd /DEBUG /Z7 /Od /Ob0 ]
+ -I$(--python-include) -I$(--extra-include)
+ : kernel32.lib advapi32.lib user32.lib $(--python-lib[1]) ;
+## QNX 6.x GCC 3.x/2.95.3
+toolset qcc qcc : "-o " : -D
+ : -Wc,-pedantic -Wc,-fno-strict-aliasing
+ [ opt --release : [ opt --symbols : -g ] -O3 -Wc,-finline-functions ]
+ [ opt --debug : -g -O0 -Wc,-fno-inline ]
+ -I$(--python-include) -I$(--extra-include)
+ : -L$(--python-lib[1]) -l$(--python-lib[2]) ;
+## Qlogic Pathscale 2.4
+toolset pathscale pathcc : "-o " : -D
+ :
+ [ opt --release : -s -Ofast -O3 ]
+ [ opt --debug : -g ]
+ -I$(--python-include) -I$(--extra-include)
+ : -L$(--python-lib[1]) -l$(--python-lib[2]) ;
+## Portland Group Pgi 6.2
+toolset pgi pgcc : "-o " : -D
+ :
+ [ opt --release : -s -O3 ]
+ [ opt --debug : -g ]
+ -I$(--python-include) -I$(--extra-include)
+ : -L$(--python-lib[1]) -l$(--python-lib[2]) ;
+## Sun Workshop 6 C++
+toolset sun cc : "-o " : -D
+ :
+ [ opt --release : -s -fast -xO4 ]
+ [ opt --debug : -g ]
+ -I$(--python-include) -I$(--extra-include)
+ : -L$(--python-lib[1]) -l$(--python-lib[2]) ;
+## Sun Workshop 6 C++ (old alias)
+toolset sunpro cc : "-o " : -D
+ :
+ [ opt --release : -s -fast -xO4 ]
+ [ opt --debug : -g ]
+ -I$(--python-include) -I$(--extra-include)
+ : -L$(--python-lib[1]) -l$(--python-lib[2]) ;
+## Compaq Alpha CXX
+toolset tru64cxx cc : "-o " : -D
+ :
+ [ opt --release : -s -O5 -inline speed ]
+ [ opt --debug : -g -O0 -pg ]
+ -I$(--python-include) -I$(--extra-include)
+ : -L$(--python-lib[1]) -l$(--python-lib[2]) ;
+## IBM VisualAge C++
+toolset vacpp xlc : "-o " : -D
+ :
+ [ opt --release : -s -O3 -qstrict -qinline ]
+ [ opt --debug : -g -qNOOPTimize -qnoinline -pg ]
+ -I$(--python-include) -I$(--extra-include)
+ : -L$(--python-lib[1]) -l$(--python-lib[2]) [ if-os AIX : -bmaxdata:0x40000000 ] ;
+## Microsoft Visual C++ .NET 7.x
+toolset vc7 cl : /Fe /Fe /Fd /Fo : -D
+ : /nologo
+ [ opt --release : /ML /O2 /Ob2 /Gy /GF /GA /GB ]
+ [ opt --debug : /MLd /DEBUG /Z7 /Od /Ob0 ]
+ -I$(--python-include) -I$(--extra-include)
+ : kernel32.lib advapi32.lib user32.lib $(--python-lib[1]) ;
+## Microsoft Visual C++ 2005
+toolset vc8 cl : /Fe /Fe /Fd /Fo : -D
+ : /nologo
+ [ opt --release : /MT /O2 /Ob2 /Gy /GF /GA /wd4996 ]
+ [ opt --debug : /MTd /DEBUG /Z7 /Od /Ob0 /wd4996 ]
+ -I$(--python-include) -I$(--extra-include)
+ : kernel32.lib advapi32.lib user32.lib $(--python-lib[1]) ;
+## Microsoft Visual C++ 2008
+toolset vc9 cl : /Fe /Fe /Fd /Fo : -D
+ : /nologo
+ [ opt --release : /MT /O2 /Ob2 /Gy /GF /GA /wd4996 ]
+ [ opt --debug : /MTd /DEBUG /Z7 /Od /Ob0 /wd4996 ]
+ -I$(--python-include) -I$(--extra-include)
+ : kernel32.lib advapi32.lib user32.lib $(--python-lib[1]) ;
+## Microsoft Visual C++ 2010
+toolset vc10 cl : /Fe /Fe /Fd /Fo : -D
+ : /nologo
+ [ opt --release : /MT /O2 /Ob2 /Gy /GF /GA /wd4996 ]
+ [ opt --debug : /MTd /DEBUG /Z7 /Od /Ob0 /wd4996 ]
+ -I$(--python-include) -I$(--extra-include)
+ : kernel32.lib advapi32.lib user32.lib $(--python-lib[1]) ;
+## VMS/OpenVMS DEC C
+toolset vmsdecc cc : /OBJECT= : "/DEFINES=(" "," ")"
+ : /STANDARD=VAXC /PREFIX_LIBRARY_ENTRIES=ALL_ENTRIES
+ [ opt --release : /OPTIMIZE /NODEBUG ]
+ [ opt --debug : /NOOPTIMIZE /DEBUG ]
+ ;
+toolset vmsdecc link .link : /EXECUTABLE= :
+ : /NOMAP
+ [ opt --release : /NODEBUG ]
+ [ opt --debug : /DEBUG ]
+ ;
+
+# First set the build commands and options according to the
+# preset toolset.
+toolset = [ MATCH --toolset=(.*) : $(ARGV) ] ;
+if ! $(toolset)
+{
+ # For some reason, the following test does not catch empty toolset.
+ ECHO "###" ;
+ ECHO "###" No toolset specified. Please use --toolset option. ;
+ ECHO "###" ;
+ ECHO "###" Known toolsets are: $(toolsets:J=", ") ;
+ EXIT "###" ;
+}
+if ! $(toolset) in $(toolsets)
+{
+ ECHO "###" ;
+ ECHO "###" Unknown toolset: $(toolset) ;
+ ECHO "###" ;
+ ECHO "###" Known toolsets are: $(toolsets:J=", ") ;
+ EXIT "###" ;
+}
+--cc = $(tool.$(toolset).cc) ;
+if $(tool.$(toolset).opt.out[2])
+{
+ if $(tool.$(toolset).opt.out[1]) = $(tool.$(toolset).opt.out[2])
+ {
+ --out = $(tool.$(toolset).opt.out[1]) ;
+ --dir = $(tool.$(toolset).opt.out[3-]) ;
+ }
+ else
+ {
+ --bin = $(tool.$(toolset).opt.out[1]) ;
+ --dir = $(tool.$(toolset).opt.out[2-]) ;
+ }
+}
+else
+{
+ --out = $(tool.$(toolset).opt.out) ;
+}
+--def = $(tool.$(toolset).opt.define) ;
+--flags = $(tool.$(toolset).flags) ;
+--defs = $(tool.$(toolset).defines) ;
+--libs = $(tool.$(toolset).linklibs) ;
+if $(tool.$(toolset).link.cc)
+{
+ --link = $(tool.$(toolset).link.cc) ;
+ if $(tool.$(toolset).link.opt.out[2])
+ {
+ if $(tool.$(toolset).link.opt.out[1]) = $(tool.$(toolset).link.opt.out[2])
+ {
+ --link-out = $(tool.$(toolset).link.opt.out[1]) ;
+ --link-dir = $(tool.$(toolset).link.opt.out[3-]) ;
+ }
+ else
+ {
+ --link-bin = $(tool.$(toolset).link.opt.out[1]) ;
+ --link-dir = $(tool.$(toolset).link.opt.out[2-]) ;
+ }
+ }
+ else
+ {
+ --link-out = $(tool.$(toolset).link.opt.out) ;
+ }
+ --link-def = $(tool.$(toolset).link.opt.define) ;
+ --link-flags = $(tool.$(toolset).link.flags) ;
+ --link-defs = $(tool.$(toolset).link.defines) ;
+ --link-libs = $(tool.$(toolset).link.linklibs) ;
+}
+
+# Put executables in platform-specific subdirectory.
+locate-target = $(LOCATE_TARGET) ;
+if $(OS) = VMS
+{
+ locate-target ?= bin$(.)vms ;
+ platform = vms ;
+}
+else if $(OS) = MAC
+{
+ locate-target ?= bin$(.)$(OS:L)$(OSPLAT:L) ;
+ platform = $(OS:L)$(OSPLAT:L) ;
+}
+else if $(OSPLAT)
+{
+ locate-target ?= bin$(.)$(OS:L)$(OSPLAT:L) ;
+ platform = $(OS:L)$(OSPLAT:L) ;
+}
+else
+{
+ locate-target ?= bin$(.)$(OS:L) ;
+ platform = $(OS:L) ;
+}
+if $(debug)
+{
+ locate-target = [ .path $(locate-target)$(.)debug ] ;
+}
+if $(profile)
+{
+ locate-target = [ .path $(locate-target)$(.)profile ] ;
+}
+else
+{
+ locate-target = [ .path $(locate-target) ] ;
+}
+
+if --show-locate-target in $(ARGV)
+{
+ ECHO $(locate-target) ;
+}
+
+# We have some different files for UNIX, VMS, and NT.
+jam.source =
+ command.c compile.c debug.c expand.c glob.c
+ hash.c hcache.c headers.c hdrmacro.c
+ jam.c jambase.c jamgram.c
+ lists.c make.c make1.c mem.c newstr.c
+ option.c output.c parse.c regexp.c rules.c
+ scan.c search.c subst.c w32_getreg.c
+ timestamp.c variable.c modules.c strings.c filesys.c
+ builtins.c pwd.c class.c native.c md5.c modules/set.c
+ modules/path.c modules/regex.c modules/property-set.c
+ modules/sequence.c modules/order.c
+ ;
+if $(OS) = NT
+{
+ jam.source += execnt.c filent.c pathunix.c ;
+}
+else if $(OS) = OS2
+{
+ jam.source += execunix.c fileos2.c pathunix.c ;
+}
+else if $(OS) = VMS
+{
+ jam.source += execvms.c filevms.c pathvms.c ;
+}
+else if $(OS) = MAC
+{
+ jam.source += execmac.c filemac.c pathmac.c ;
+}
+else
+{
+ jam.source += execunix.c fileunix.c pathunix.c ;
+}
+
+# Debug assertions, or not.
+if ! $(debug) || --noassert in $(ARGV)
+{
+ --defs += NDEBUG ;
+}
+
+# Enable some optional features.
+--defs += OPT_HEADER_CACHE_EXT ;
+--defs += OPT_GRAPH_DEBUG_EXT ;
+--defs += OPT_SEMAPHORE ;
+--defs += OPT_AT_FILES ;
+--defs += OPT_DEBUG_PROFILE ;
+
+# Bug fixes
+--defs += OPT_FIX_TARGET_VARIABLES_EXT ;
+#~ --defs += OPT_NO_EXTERNAL_VARIABLE_SPLIT ;
+
+# Improvements
+--defs += OPT_IMPROVED_PATIENCE_EXT ;
+
+# Use Boehm GC memory allocator?
+if $(--boehm-gc)
+{
+ --defs += OPT_BOEHM_GC ;
+ if $(debug)
+ {
+ --defs += GC_DEBUG ;
+ }
+}
+
+if $(--duma)
+{
+ --defs += OPT_DUMA ;
+}
+
+if ( $(OS) = NT ) && ! NT in $(--defs)
+{
+ --defs += NT ;
+}
+if $(OS) = VMS
+{
+ --defs += VMS ;
+}
+--defs += YYSTACKSIZE=5000 ;
+
+if $(with-python)
+{
+ --defs += HAVE_PYTHON ;
+}
+
+if $(debug)
+{
+ --defs += BJAM_NEWSTR_NO_ALLOCATE ;
+}
+
+
+# The basic symbolic targets...
+NOTFILE all clean dist ;
+ALWAYS clean ;
+
+# Utility rules and actions...
+rule .clean
+{
+ [DELETE] clean : $(<) ;
+}
+if $(OS) = NT { actions piecemeal together existing [DELETE] {
+ del /F /Q "$(>)"
+} }
+if $(UNIX) = true { actions piecemeal together existing [DELETE] {
+ rm -f "$(>)"
+} }
+if $(OS) = VMS { actions piecemeal together existing [DELETE] {
+ DELETE $(>[--2]:J=";*, ") $(>[-1]);*
+} }
+if $(OS) = NT {
+ --chmod+w = "attrib -r " ;
+}
+if $(UNIX) = true {
+ --chmod+w = "chmod +w " ;
+}
+if $(OS) = VMS {
+ --chmod+w = "SET FILE/PROT=(S:RWED) " ;
+}
+
+rule .mkdir
+{
+ NOUPDATE $(<) ;
+ if $(<:P) { DEPENDS $(<) : $(<:P) ; .mkdir $(<:P) ; }
+ if ! $(md<$(<)>) { [MKDIR] $(<) ; md<$(<)> = - ; }
+}
+if $(OS) = NT { actions [MKDIR] {
+ md "$(<)"
+} }
+if $(UNIX) = true { actions [MKDIR] {
+ mkdir "$(<)"
+} }
+if $(OS) = VMS { actions [MKDIR] {
+ CREATE/DIR $(<J=", ")
+} }
+
+rule .exe
+{
+ local exe = $(<) ;
+ if $(OS) = NT || ( $(UNIX) = true && $(OS) = CYGWIN ) || $(OS) = VMS { exe = $(exe:S=.exe) ; }
+ LOCATE on $(exe) = $(locate-target) ;
+ DEPENDS all : $(exe) ;
+ .mkdir $(locate-target) ;
+ if $(--link)
+ {
+ local objs = ;
+ for local s in $(>)
+ {
+ # Translate any subdir elements into a simple file name.
+ local o = [ MATCH "([^/]+)[/]?(.+)" : $(s) ] ;
+ o = $(o:J=_) ;
+ o = $(o:S=.o) ;
+ objs += $(o) ;
+ LOCATE on $(o) = $(locate-target) ;
+ DEPENDS $(exe) : $(o) ;
+ DEPENDS $(o) : $(s) ;
+ DEPENDS $(o) : $(locate-target) ;
+ [COMPILE] $(o) : $(s) ;
+ .clean $(o) ;
+ }
+ DEPENDS $(exe) : $(objs) ;
+ DEPENDS $(exe) : $(locate-target) ;
+ [COMPILE.LINK] $(exe) : $(objs) ;
+ .clean $(exe) ;
+ }
+ else
+ {
+ DEPENDS $(exe) : $(>) ;
+ DEPENDS $(exe) : $(locate-target) ;
+ [COMPILE] $(exe) : $(>) ;
+ .clean $(exe) ;
+ }
+ return $(exe) ;
+}
+if ! $(--def[2]) { actions [COMPILE] {
+ "$(--cc)" "$(--bin)$(<:D=)" "$(--dir)$(<:D)$(./)" $(--out)$(<) "$(--def)$(--defs)" "$(--flags)" "$(--libs)" "$(>)"
+} }
+else { actions [COMPILE] {
+ "$(--cc)" "$(--bin)$(<:D=)" "$(--dir)$(<:D)$(./)" $(--out)$(<) "$(--def[1])$(--defs:J=$(--def[2]))$(--def[3])" "$(--flags)" "$(--libs)" "$(>)"
+} }
+if $(OS) = VMS { actions [COMPILE.LINK] {
+ "$(--link)" $(--link-bin)$(<:D=) $(--link-dir)$(<:D)$(./) $(--link-out)$(<) $(--link-def)$(--link-defs) $(--link-flags) "$(--link-libs)" $(>J=", ")
+} }
+else { actions [COMPILE.LINK] {
+ "$(--link)" "$(--link-bin)$(<:D=)" "$(--link-dir)$(<:D)$(./)" "$(--link-out)$(<)" "$(--link-def)$(--link-defs)" "$(--link-flags)" "$(--link-libs)" "$(>)"
+} }
+
+rule .link
+{
+ DEPENDS all : $(<) ;
+ DEPENDS $(<) : $(>) ;
+ [LINK] $(<) : $(>) ;
+ .clean $(<) ;
+}
+if $(OS) = NT { actions [LINK] {
+ copy "$(>)" "$(<)"
+} }
+if $(UNIX) = true { actions [LINK] {
+ ln -fs "$(>)" "$(<)"
+} }
+if $(OS) = VMS { actions [LINK] {
+ COPY/REPLACE $(>) $(<)
+} }
+
+rule .copy
+{
+ DEPENDS all : $(<) ;
+ DEPENDS $(<) : $(>) ;
+ [COPY] $(<) : $(>) ;
+ .clean $(<) ;
+}
+
+# Will be redefined later.
+actions [COPY]
+{
+}
+
+
+rule .move
+{
+ DEPENDS $(<) : $(>) ;
+ [MOVE] $(<) : $(>) ;
+}
+if $(OS) = NT { actions [MOVE] {
+ del /f "$(<)"
+ rename "$(>)" "$(<)"
+} }
+if $(UNIX) = true { actions [MOVE] {
+ mv -f "$(>)" "$(<)"
+} }
+if $(OS) = VMS { actions [MOVE] {
+ RENAME "$(>)" "$(<)"
+} }
+
+# Generate the grammar tokens table, and the real yacc grammar.
+rule .yyacc
+{
+ local exe = [ .exe yyacc : yyacc.c ] ;
+ NOUPDATE $(exe) ;
+ DEPENDS $(<) : $(exe) $(>) ;
+ LEAVES $(<) ;
+ yyacc.exe on $(<) = $(exe:R=$(locate-target)) ;
+ [YYACC] $(<) : $(>) ;
+}
+actions [YYACC] {
+ $(--chmod+w)$(<[1])
+ $(--chmod+w)$(<[2])
+ "$(yyacc.exe)" "$(<)" "$(>)"
+}
+if $(grammar)
+{
+ .yyacc jamgram.y jamgramtab.h : jamgram.yy ;
+}
+else if $(debug)
+{
+ .exe yyacc : yyacc.c ;
+}
+
+# How to build the grammar.
+if $(OS) = NT
+{
+ SUFEXE = .exe ;
+ # try some other likely spellings...
+ PATH ?= $(Path) ;
+ PATH ?= $(path) ;
+}
+SUFEXE ?= "" ;
+
+yacc ?= [ GLOB $(PATH) : yacc$(SUFEXE) ] ;
+yacc ?= [ GLOB $(PATH) : bison$(SUFEXE) ] ;
+yacc ?= [ GLOB "$(ProgramFiles:J= )\\GnuWin32\\bin" "C:\\Program Files\\GnuWin32\\bin" : bison$(SUFEXE) ] ;
+yacc = $(yacc[1]) ;
+switch $(yacc:D=:S=)
+{
+ case bison : yacc += -d --yacc ;
+ case yacc : yacc += -d ;
+}
+if $(debug) && $(yacc)
+{
+ yacc += -t -v ;
+}
+yacc += $(YACCFLAGS) ;
+
+rule .yacc
+{
+ DEPENDS $(<) : $(>) ;
+ LEAVES $(<) ;
+ [YACC] $(<) : $(>) ;
+}
+if $(OS) = NT { actions [YACC] {
+ "$(yacc)" "$(>)"
+ if not errorlevel 1 (
+ del /f "$(<[1])"
+ rename y.tab$(<[1]:S) "$(<[1])"
+ del /f $(<[2])
+ rename y.tab$(<[2]:S) "$(<[2])"
+ ) else set _error_ =
+} }
+if $(UNIX) = true { actions [YACC] {
+ if ` "$(yacc)" "$(>)" ` ; then
+ mv -f y.tab$(<[1]:S) "$(<[1])"
+ mv -f y.tab$(<[2]:S) "$(<[2])"
+ else
+ exit 1
+ fi
+} }
+if $(OS) = VMS { actions [YACC] {
+ IF "$(yacc)" $(>)
+ THEN
+ RENAME y_tab$(<[1]:S) $(<[1])
+ RENAME y_tab$(<[2]:S) $(<[2])
+ ENDIF
+} }
+if $(grammar) && ! $(yacc)
+{
+ EXIT "Could not find the 'yacc' tool, and therefore can not build the grammar." ;
+}
+if $(grammar) && $(yacc)
+{
+ .yacc jamgram.c jamgram.h : jamgram.y ;
+}
+
+# How to build the compiled in jambase.
+rule .mkjambase
+{
+ local exe = [ .exe mkjambase : mkjambase.c ] ;
+ DEPENDS $(<) : $(exe) $(>) ;
+ LEAVES $(<) ;
+ mkjambase.exe on $(<) = $(exe:R=$(locate-target)) ;
+ [MKJAMBASE] $(<) : $(>) ;
+}
+actions [MKJAMBASE] {
+ $(--chmod+w)$(<)
+ $(mkjambase.exe) "$(<)" "$(>)"
+}
+if $(debug)
+{
+ .mkjambase jambase.c : Jambase ;
+}
+
+# How to build Jam.
+rule .jam
+{
+ $(>).exe = [ .exe $(>) : $(jam.source) ] ;
+ DEPENDS all : $($(>).exe) ;
+
+ # Make a copy under the old name.
+ $(<).exe = $(<:S=$($(>).exe:S)) ;
+ LOCATE on $($(<).exe) = $(locate-target) ;
+ .copy $($(<).exe) : $($(>).exe) ;
+ DEPENDS all : $($(<).exe) ;
+}
+.jam bjam : b2 ;
+
+
+# Scan sources for header dependencies.
+# WARNING: Yes those are *REAL TABS* below. DO NOT CHANGE,
+# under any circumstances, to spaces!! And the tabs
+# indenting this are so that if someone is in the mood to
+# replace tabs they hit this comment, and hopefully notice
+# their error.
+rule .scan
+{
+ HDRRULE on $(<:D=) = .hdr.scan ;
+ HDRSCAN on $(<:D=) = "^[ ]*#[ ]*include[ ]*([<\"][^\">]*[\">]).*$" ;
+}
+rule .hdr.scan
+{
+ local hdrs = [ GLOB . : $(>:D=) ] ;
+ INCLUDES $(<:D=) : $(hdrs:D=) ;
+ HDRRULE on $(>:D=) = .hdr.scan ;
+ HDRSCAN on $(>:D=) = "^[ ]*#[ ]*include[ ]*([<\"][^\">]*[\">]).*$" ;
+}
+.scan [ GLOB . : *.c ] ;
+
+# Distribution making from here on out. Assumes that
+# the docs are already built as html at ../doc/html. If
+# they aren't, then the docs are not included in the dist
+# archive.
+dist.license =
+ [ GLOB . : $(LICENSE).txt ]
+ ;
+dist.license = $(dist.license:D=)
+ [ GLOB [ .path .. .. .. ] : $(LICENSE).txt ]
+ [ GLOB [ .path .. boost ] : $(LICENSE).txt ] ;
+dist.docs =
+ [ GLOB . : *.png *.css *.html ]
+ ;
+dist.docs = $(dist.docs:D=)
+ [ GLOB [ .path images ] : *.png ]
+ [ GLOB [ .path jam ] : *.html ]
+ ;
+dist.source =
+ [ GLOB . : *.c *.h ]
+ ;
+dist.source = $(dist.source:D=)
+ $(dist.license[1])
+ $(dist.docs)
+ build.jam build.bat build.sh build_vms.com
+ Jambase
+ jamgram.y jamgram.yy
+ [ .path modules set.c ]
+ [ .path modules path.c ]
+ [ .path modules regex.c ]
+ [ .path modules property-set.c ]
+ [ .path modules sequence.c ]
+ [ .path modules order.c ]
+ [ GLOB [ .path boehm_gc ] : * ]
+ [ GLOB [ .path boehm_gc include ] : * ]
+ [ GLOB [ .path boehm_gc include private ] : * ]
+ [ GLOB [ .path boehm_gc cord ] : * ]
+ [ GLOB [ .path boehm_gc Mac_files ] : * ]
+ [ GLOB [ .path boehm_gc tests ] : * ]
+ [ GLOB [ .path boehm_gc doc ] : * ]
+ ;
+dist.bin =
+ bjam
+ ;
+dist.bin =
+ $(dist.license[1])
+ $(dist.bin:S=$(bjam.exe:S))
+ ;
+
+if $(OS) = NT
+{
+ zip ?= [ GLOB "$(ProgramFiles:J= )\\7-ZIP" "C:\\Program Files\\7-ZIP" : "7z.exe" ] ;
+ zip ?= [ GLOB "$(ProgramFiles:J= )\\7-ZIP" "C:\\Program Files\\7-ZIP" : "7zn.exe" ] ;
+ zip ?= [ GLOB $(PATH) : zip.exe ] ;
+ zip ?= zip ;
+ zip = $(zip[1]) ;
+ switch $(zip:D=:S=)
+ {
+ case 7z* : zip += a -r -tzip -mx=9 ;
+ case zip : zip += -9r ;
+ }
+ actions piecemeal [PACK] {
+ "$(zip)" "$(<)" "$(>)"
+ }
+ actions piecemeal [ZIP] {
+ "$(zip)" "$(<)" "$(>)"
+ }
+ actions piecemeal [COPY] {
+ copy /Y "$(>)" "$(<)" >NUL:
+ }
+}
+if $(UNIX) = true
+{
+ tar ?= [ GLOB $(PATH) : star bsdtar tar ] ;
+ tar = $(tar[1]) ;
+ switch $(tar:D=:S=)
+ {
+ case star : tar += -c artype=pax -D -d -to-stdout ;
+ case * : tar += -c -f - ;
+ }
+ actions [PACK] {
+ "$(tar)" "$(>)" | gzip -c9 > "$(<)"
+ }
+ #~ actions [PACK] {
+ #~ tar cf "$(<:S=.tar)" "$(>)"
+ #~ }
+ actions [ZIP] {
+ gzip -c9 "$(>)" > "$(<)"
+ }
+ actions [COPY] {
+ cp -Rpf "$(>)" "$(<)"
+ }
+}
+
+# The single binary, compressed.
+rule .binary
+{
+ local zip = ;
+ if $(OS) = NT { zip = $($(<).exe:S=.zip) ; }
+ if $(UNIX) = true { zip = $($(<).exe:S=.tgz) ; }
+ zip = $(zip:S=)-$(VERSION)-$(RELEASE)-$(platform)$(zip:S) ;
+ DEPENDS $(zip) : $($(<).exe) ;
+ DEPENDS dist : $(zip) ;
+ #~ LOCATE on $(zip) = $(locate-target) ;
+ if $(OS) = NT { [ZIP] $(zip) : $($(<).exe) ; }
+ if $(UNIX) = true { [PACK] $(zip) : $($(<).exe) ; }
+ .clean $(zip) ;
+}
+
+# Package some file.
+rule .package ( dst-dir : src-files + )
+{
+ local dst-files ;
+ local src-files-actual ;
+ for local src-path in $(src-files)
+ {
+ if ! [ GLOB $(src-path:P) : $(src-path:B) ] || [ CHECK_IF_FILE $(src-path) ]
+ {
+ local src-subdir = $(src-path:D) ;
+ local src-file = $(src-path) ;
+ while $(src-subdir:D) { src-subdir = $(src-subdir:D) ; }
+ if $(src-subdir) = ".."
+ {
+ src-file = $(src-file:D=) ;
+ }
+ dst-files += $(src-file:R=$(dst-dir)) ;
+ src-files-actual += $(src-path) ;
+ }
+ }
+
+ local pack = ;
+ if $(OS) = NT { pack = $(dst-dir).zip ; }
+ if $(UNIX) = true { pack = $(dst-dir).tgz ; }
+
+ DEPENDS dist : $(pack) ;
+ DEPENDS $(pack) : $(dst-files) ;
+
+ local dst-files-queue = $(dst-files) ;
+ for local src-path in $(src-files-actual)
+ {
+ local dst-file = $(dst-files-queue[1]) ;
+ dst-files-queue = $(dst-files-queue[2-]) ;
+ DEPENDS $(dst-file) : $(src-path) $(dst-file:D) ;
+ .mkdir $(dst-file:D) ;
+
+ [COPY] $(dst-file) : $(src-path) ;
+ .clean $(dst-file) ;
+ }
+
+ [PACK] $(pack) : $(dst-files) ;
+ .clean $(pack) ;
+}
+
+# RPM distro file.
+rpm-tool = [ GLOB $(PATH) : "rpmbuild" ] ;
+rpm-tool ?= [ GLOB $(PATH) : "rpm" ] ;
+rpm-tool = $(rpm-tool[1]) ;
+rule .rpm ( name : source )
+{
+ local rpm-arch = ;
+ switch $(OSPLAT)
+ {
+ case X86 : rpm-arch ?= i386 ;
+ case PPC : rpm-arch ?= ppc ;
+ case AXP : rpm-arch ?= alpha ;
+ # no guaranty for these:
+ case IA64 : rpm-arch ?= ia64 ;
+ case ARM : rpm-arch ?= arm ;
+ case SPARC : rpm-arch ?= sparc ;
+ case * : rpm-arch ?= other ;
+ }
+ local target = $(name)-rpm ;
+ NOTFILE $(target) ;
+ DEPENDS dist : $(target) ;
+ DEPENDS $(target) : $(name).$(rpm-arch).rpm $(name).src.rpm ;
+ DEPENDS $(name).$(rpm-arch).rpm : $(source) ;
+ DEPENDS $(name).src.rpm : $(name).$(rpm-arch).rpm ;
+ docs on $(target) = $(dist.docs:J=" ") ;
+ arch on $(target) = $(rpm-arch) ;
+ if $(rpm-arch) = ppc { target-opt on $(target) = --target= ; }
+ else { target-opt on $(target) = "--target " ; }
+ [RPM] $(target) : $(source) ;
+ .clean $(name).$(rpm-arch).rpm $(name).src.rpm ;
+}
+actions [RPM] {
+ set -e
+ export BOOST_JAM_TOOLSET="$(toolset)"
+ $(rpm-tool) -ta $(target-opt)$(arch) $(>) | tee rpm.out
+ cp `grep -e '^Wrote:' rpm.out | sed 's/^Wrote: //'` .
+ rm -f rpm.out
+}
+
+# The distribution targets. Don't bother with the targets if
+# distribution build not requested.
+if dist in $(ARGV)
+{
+ #~ .binary bjam ;
+ .package $(NAME)-$(VERSION) : $(dist.source) ;
+ .package $(NAME)-$(VERSION)-$(RELEASE)-$(platform) : $(dist.bin) ;
+ if $(rpm-tool)
+ {
+ #~ .rpm $(NAME)-$(VERSION)-$(RELEASE) : $(NAME)-$(VERSION).tgz ;
+ }
+}
diff --git a/jam-files/engine/build.sh b/jam-files/engine/build.sh
new file mode 100755
index 00000000..f1fb806d
--- /dev/null
+++ b/jam-files/engine/build.sh
@@ -0,0 +1,303 @@
+#!/bin/sh
+
+#~ Copyright 2002-2005 Rene Rivera.
+#~ Distributed under the Boost Software License, Version 1.0.
+#~ (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+
+# Reset the toolset.
+BOOST_JAM_TOOLSET=
+
+# Run a command, and echo before doing so. Also checks the exit
+# status and quits if there was an error.
+echo_run ()
+{
+ echo "$@"
+ $@
+ r=$?
+ if test $r -ne 0 ; then
+ exit $r
+ fi
+}
+
+# Print an error message, and exit with a status of 1.
+error_exit ()
+{
+ echo "###"
+ echo "###" "$@"
+ echo "###"
+ echo "### You can specify the toolset as the argument, i.e.:"
+ echo "### ./build.sh gcc"
+ echo "###"
+ echo "### Toolsets supported by this script are:"
+ echo "### acc, como, darwin, gcc, intel-darwin, intel-linux, kcc, kylix,"
+ echo "### mipspro, mingw(msys), pathscale, pgi, qcc, sun, sunpro, tru64cxx, vacpp"
+ echo "###"
+ echo "### A special toolset; cc, is available which is used as a fallback"
+ echo "### when a more specific toolset is not found and the cc command is"
+ echo "### detected. The 'cc' toolset will use the CC, CFLAGS, and LIBS"
+ echo "### envrironment variables, if present."
+ echo "###"
+ exit 1
+}
+
+# Check that a command is in the PATH.
+test_path ()
+{
+ if `command -v command 1>/dev/null 2>/dev/null`; then
+ command -v $1 1>/dev/null 2>/dev/null
+ else
+ hash $1 1>/dev/null 2>/dev/null
+ fi
+}
+
+# Check that the OS name, as returned by "uname", is as given.
+test_uname ()
+{
+ if test_path uname; then
+ test `uname` = $*
+ fi
+}
+
+# Try and guess the toolset to bootstrap the build with...
+Guess_Toolset ()
+{
+ if test -r /mingw/bin/gcc ; then
+ BOOST_JAM_TOOLSET=mingw
+ BOOST_JAM_TOOLSET_ROOT=/mingw/
+ elif test_uname Darwin ; then BOOST_JAM_TOOLSET=darwin
+ elif test_uname IRIX ; then BOOST_JAM_TOOLSET=mipspro
+ elif test_uname IRIX64 ; then BOOST_JAM_TOOLSET=mipspro
+ elif test_uname OSF1 ; then BOOST_JAM_TOOLSET=tru64cxx
+ elif test_uname QNX && test_path qcc ; then BOOST_JAM_TOOLSET=qcc
+ elif test_path gcc ; then BOOST_JAM_TOOLSET=gcc
+ elif test_path icc ; then BOOST_JAM_TOOLSET=intel-linux
+ elif test -r /opt/intel/cc/9.0/bin/iccvars.sh ; then
+ BOOST_JAM_TOOLSET=intel-linux
+ BOOST_JAM_TOOLSET_ROOT=/opt/intel/cc/9.0
+ elif test -r /opt/intel_cc_80/bin/iccvars.sh ; then
+ BOOST_JAM_TOOLSET=intel-linux
+ BOOST_JAM_TOOLSET_ROOT=/opt/intel_cc_80
+ elif test -r /opt/intel/compiler70/ia32/bin/iccvars.sh ; then
+ BOOST_JAM_TOOLSET=intel-linux
+ BOOST_JAM_TOOLSET_ROOT=/opt/intel/compiler70/ia32/
+ elif test -r /opt/intel/compiler60/ia32/bin/iccvars.sh ; then
+ BOOST_JAM_TOOLSET=intel-linux
+ BOOST_JAM_TOOLSET_ROOT=/opt/intel/compiler60/ia32/
+ elif test -r /opt/intel/compiler50/ia32/bin/iccvars.sh ; then
+ BOOST_JAM_TOOLSET=intel-linux
+ BOOST_JAM_TOOLSET_ROOT=/opt/intel/compiler50/ia32/
+ elif test_path pgcc ; then BOOST_JAM_TOOLSET=pgi
+ elif test_path pathcc ; then BOOST_JAM_TOOLSET=pathscale
+ elif test_path xlc ; then BOOST_JAM_TOOLSET=vacpp
+ elif test_path como ; then BOOST_JAM_TOOLSET=como
+ elif test_path KCC ; then BOOST_JAM_TOOLSET=kcc
+ elif test_path bc++ ; then BOOST_JAM_TOOLSET=kylix
+ elif test_path aCC ; then BOOST_JAM_TOOLSET=acc
+ elif test_uname HP-UX ; then BOOST_JAM_TOOLSET=acc
+ elif test -r /opt/SUNWspro/bin/cc ; then
+ BOOST_JAM_TOOLSET=sunpro
+ BOOST_JAM_TOOLSET_ROOT=/opt/SUNWspro/
+ # Test for "cc" as the default fallback.
+ elif test_path $CC ; then BOOST_JAM_TOOLSET=cc
+ elif test_path cc ; then
+ BOOST_JAM_TOOLSET=cc
+ CC=cc
+ fi
+ if test "$BOOST_JAM_TOOLSET" = "" ; then
+ error_exit "Could not find a suitable toolset."
+ fi
+}
+
+# The one option we support in the invocation
+# is the name of the toolset to force building
+# with.
+case "$1" in
+ --guess-toolset) Guess_Toolset ; echo "$BOOST_JAM_TOOLSET" ; exit 1 ;;
+ -*) Guess_Toolset ;;
+ ?*) BOOST_JAM_TOOLSET=$1 ; shift ;;
+ *) Guess_Toolset ;;
+esac
+BOOST_JAM_OPT_JAM="-o bootstrap/jam0"
+BOOST_JAM_OPT_MKJAMBASE="-o bootstrap/mkjambase0"
+BOOST_JAM_OPT_YYACC="-o bootstrap/yyacc0"
+case $BOOST_JAM_TOOLSET in
+ mingw)
+ if test -r ${BOOST_JAM_TOOLSET_ROOT}bin/gcc ; then
+ export PATH=${BOOST_JAM_TOOLSET_ROOT}bin:$PATH
+ fi
+ BOOST_JAM_CC="gcc -DNT"
+ ;;
+
+ gcc)
+ BOOST_JAM_CC=gcc
+ ;;
+
+ darwin)
+ BOOST_JAM_CC=cc
+ ;;
+
+ intel-darwin)
+ BOOST_JAM_CC=icc
+ ;;
+
+ intel-linux)
+ if test -r /opt/intel/cc/9.0/bin/iccvars.sh ; then
+ BOOST_JAM_TOOLSET_ROOT=/opt/intel/cc/9.0/
+ elif test -r /opt/intel_cc_80/bin/iccvars.sh ; then
+ BOOST_JAM_TOOLSET_ROOT=/opt/intel_cc_80/
+ elif test -r /opt/intel/compiler70/ia32/bin/iccvars.sh ; then
+ BOOST_JAM_TOOLSET_ROOT=/opt/intel/compiler70/ia32/
+ elif test -r /opt/intel/compiler60/ia32/bin/iccvars.sh ; then
+ BOOST_JAM_TOOLSET_ROOT=/opt/intel/compiler60/ia32/
+ elif test -r /opt/intel/compiler50/ia32/bin/iccvars.sh ; then
+ BOOST_JAM_TOOLSET_ROOT=/opt/intel/compiler50/ia32/
+ fi
+ if test -r ${BOOST_JAM_TOOLSET_ROOT}bin/iccvars.sh ; then
+ # iccvars doesn't change LD_RUN_PATH. We adjust LD_RUN_PATH
+ # here in order not to have to rely on ld.so.conf knowing the
+ # icc library directory. We do this before running iccvars.sh
+ # in order to allow a user to add modifications to LD_RUN_PATH
+ # in iccvars.sh.
+ if test -z "${LD_RUN_PATH}"; then
+ LD_RUN_PATH="${BOOST_JAM_TOOLSET_ROOT}lib"
+ else
+ LD_RUN_PATH="${BOOST_JAM_TOOLSET_ROOT}lib:${LD_RUN_PATH}"
+ fi
+ export LD_RUN_PATH
+ . ${BOOST_JAM_TOOLSET_ROOT}bin/iccvars.sh
+ fi
+ BOOST_JAM_CC=icc
+ ;;
+
+ vacpp)
+ BOOST_JAM_CC=xlc
+ ;;
+
+ como)
+ BOOST_JAM_CC="como --c"
+ ;;
+
+ kcc)
+ BOOST_JAM_CC=KCC
+ ;;
+
+ kylix)
+ BOOST_JAM_CC=bc++
+ ;;
+
+ mipspro)
+ BOOST_JAM_CC=cc
+ ;;
+
+ pathscale)
+ BOOST_JAM_CC=pathcc
+ ;;
+
+ pgi)
+ BOOST_JAM_CC=pgcc
+ ;;
+
+ sun*)
+ if test -z "${BOOST_JAM_TOOLSET_ROOT}" -a -r /opt/SUNWspro/bin/cc ; then
+ BOOST_JAM_TOOLSET_ROOT=/opt/SUNWspro/
+ fi
+ if test -r "${BOOST_JAM_TOOLSET_ROOT}bin/cc" ; then
+ PATH=${BOOST_JAM_TOOLSET_ROOT}bin:${PATH}
+ export PATH
+ fi
+ BOOST_JAM_CC=cc
+ ;;
+
+ clang*)
+ BOOST_JAM_CC="clang -Wno-unused -Wno-format"
+ BOOST_JAM_TOOLSET=clang
+ ;;
+
+ tru64cxx)
+ BOOST_JAM_CC=cc
+ ;;
+
+ acc)
+ BOOST_JAM_CC="cc -Ae"
+ ;;
+
+ cc)
+ if test -z "$CC" ; then CC=cc ; fi
+ BOOST_JAM_CC=$CC
+ BOOST_JAM_OPT_JAM="$BOOST_JAM_OPT_JAM $CFLAGS $LIBS"
+ BOOST_JAM_OPT_MKJAMBASE="$BOOST_JAM_OPT_MKJAMBASE $CFLAGS $LIBS"
+ BOOST_JAM_OPT_YYACC="$BOOST_JAM_OPT_YYACC $CFLAGS $LIBS"
+ ;;
+
+ qcc)
+ BOOST_JAM_CC=qcc
+ ;;
+
+ *)
+ error_exit "Unknown toolset: $BOOST_JAM_TOOLSET"
+ ;;
+esac
+
+echo "###"
+echo "### Using '$BOOST_JAM_TOOLSET' toolset."
+echo "###"
+
+YYACC_SOURCES="yyacc.c"
+MKJAMBASE_SOURCES="mkjambase.c"
+BJAM_SOURCES="\
+ command.c compile.c debug.c expand.c glob.c hash.c\
+ hdrmacro.c headers.c jam.c jambase.c jamgram.c lists.c make.c make1.c\
+ newstr.c option.c output.c parse.c pathunix.c pathvms.c regexp.c\
+ rules.c scan.c search.c subst.c timestamp.c variable.c modules.c\
+ strings.c filesys.c builtins.c pwd.c class.c native.c md5.c w32_getreg.c\
+ modules/set.c modules/path.c modules/regex.c modules/property-set.c\
+ modules/sequence.c modules/order.c"
+case $BOOST_JAM_TOOLSET in
+ mingw)
+ BJAM_SOURCES="${BJAM_SOURCES} execnt.c filent.c"
+ ;;
+
+ *)
+ BJAM_SOURCES="${BJAM_SOURCES} execunix.c fileunix.c"
+ ;;
+esac
+
+BJAM_UPDATE=
+if test "$1" = "--update" -o "$2" = "--update" -o "$3" = "--update" -o "$4" = "--update" ; then
+ BJAM_UPDATE="update"
+fi
+if test "${BJAM_UPDATE}" = "update" -a ! -x "./bootstrap/jam0" ; then
+ BJAM_UPDATE=
+fi
+
+if test "${BJAM_UPDATE}" != "update" ; then
+ echo_run rm -rf bootstrap
+ echo_run mkdir bootstrap
+ if test ! -r jamgram.y -o ! -r jamgramtab.h ; then
+ echo_run ${BOOST_JAM_CC} ${BOOST_JAM_OPT_YYACC} ${YYACC_SOURCES}
+ if test -x "./bootstrap/yyacc0" ; then
+ echo_run ./bootstrap/yyacc0 jamgram.y jamgramtab.h jamgram.yy
+ fi
+ fi
+ if test ! -r jamgram.c -o ! -r jamgram.h ; then
+ if test_path yacc ; then YACC="yacc -d"
+ elif test_path bison ; then YACC="bison -y -d --yacc"
+ fi
+ echo_run $YACC jamgram.y
+ mv -f y.tab.c jamgram.c
+ mv -f y.tab.h jamgram.h
+ fi
+ if test ! -r jambase.c ; then
+ echo_run ${BOOST_JAM_CC} ${BOOST_JAM_OPT_MKJAMBASE} ${MKJAMBASE_SOURCES}
+ if test -x "./bootstrap/mkjambase0" ; then
+ echo_run ./bootstrap/mkjambase0 jambase.c Jambase
+ fi
+ fi
+ echo_run ${BOOST_JAM_CC} ${BOOST_JAM_OPT_JAM} ${BJAM_SOURCES}
+fi
+if test -x "./bootstrap/jam0" ; then
+ if test "${BJAM_UPDATE}" != "update" ; then
+ echo_run ./bootstrap/jam0 -f build.jam --toolset=$BOOST_JAM_TOOLSET "--toolset-root=$BOOST_JAM_TOOLSET_ROOT" clean
+ fi
+ echo_run ./bootstrap/jam0 -f build.jam --toolset=$BOOST_JAM_TOOLSET "--toolset-root=$BOOST_JAM_TOOLSET_ROOT" "$@"
+fi
diff --git a/jam-files/engine/build_vms.com b/jam-files/engine/build_vms.com
new file mode 100644
index 00000000..965b6342
--- /dev/null
+++ b/jam-files/engine/build_vms.com
@@ -0,0 +1,105 @@
+$ ! Copyright 2002-2003 Rene Rivera, Johan Nilsson.
+$ ! Distributed under the Boost Software License, Version 1.0.
+$ ! (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+$ !
+$ ! bootstrap build script for Jam
+$ !
+$ SAY :== WRITE SYS$OUTPUT
+$ !
+$ ON WARNING THEN CONTINUE
+$ !
+$ IF "" .NES. F$SEARCH("[.bootstrap_vms]*.*")
+$ THEN
+$ SAY "Cleaning previous boostrap files..."
+$ !
+$ SET FILE/PROTECTION=(S:RWED) [.bootstrap_vms]*.*;*
+$ DELETE [.bootstrap_vms]*.*;*
+$ ENDIF
+$ !
+$ IF "" .NES. F$SEARCH("bootstrap_vms.dir")
+$ THEN
+$ SAY "Removing previous boostrap directory..."
+$ !
+$ SET FILE/PROT=(S:RWED) bootstrap_vms.dir
+$ DELETE bootstrap_vms.dir;
+$ ENDIF
+$ !
+$ SAY "Creating boostrap directory..."
+$ !
+$ CREATE/DIR [.bootstrap_vms]
+$ !
+$ SAY "Building bootstrap jam..."
+$ !
+$ CC_FLAGS = "/DEFINE=VMS /STANDARD=VAXC /PREFIX_LIBRARY_ENTRIES=ALL_ENTRIES "
+$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]builtins.obj builtins.c
+$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]command.obj command.c
+$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]compile.obj compile.c
+$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]execvms.obj execvms.c
+$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]expand.obj expand.c
+$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]filesys.obj filesys.c
+$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]filevms.obj filevms.c
+$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]glob.obj glob.c
+$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]hash.obj hash.c
+$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]hdrmacro.obj hdrmacro.c
+$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]headers.obj headers.c
+$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]jam.obj jam.c
+$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]jambase.obj jambase.c
+$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]jamgram.obj jamgram.c
+$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]lists.obj lists.c
+$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]make.obj make.c
+$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]make1.obj make1.c
+$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]modules.obj modules.c
+$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]newstr.obj newstr.c
+$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]option.obj option.c
+$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]parse.obj parse.c
+$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]pathvms.obj pathvms.c
+$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]pwd.obj pwd.c
+$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]regexp.obj regexp.c
+$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]rules.obj rules.c
+$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]scan.obj scan.c
+$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]search.obj search.c
+$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]strings.obj strings.c
+$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]subst.obj subst.c
+$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]timestamp.obj timestamp.c
+$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]variable.obj variable.c
+$ link -
+ /EXECUTABLE=[.bootstrap_vms]jam0.exe -
+ [.bootstrap_vms]builtins.obj, -
+ [.bootstrap_vms]command.obj, -
+ [.bootstrap_vms]compile.obj, -
+ [.bootstrap_vms]execvms.obj, -
+ [.bootstrap_vms]expand.obj, -
+ [.bootstrap_vms]filesys.obj, -
+ [.bootstrap_vms]filevms.obj, -
+ [.bootstrap_vms]glob.obj, -
+ [.bootstrap_vms]hash.obj, -
+ [.bootstrap_vms]hdrmacro.obj, -
+ [.bootstrap_vms]headers.obj, -
+ [.bootstrap_vms]jam.obj, -
+ [.bootstrap_vms]jambase.obj, -
+ [.bootstrap_vms]jamgram.obj, -
+ [.bootstrap_vms]lists.obj, -
+ [.bootstrap_vms]make.obj, -
+ [.bootstrap_vms]make1.obj, -
+ [.bootstrap_vms]modules.obj, -
+ [.bootstrap_vms]newstr.obj, -
+ [.bootstrap_vms]option.obj, -
+ [.bootstrap_vms]parse.obj, -
+ [.bootstrap_vms]pathvms.obj, -
+ [.bootstrap_vms]pwd.obj, -
+ [.bootstrap_vms]regexp.obj, -
+ [.bootstrap_vms]rules.obj, -
+ [.bootstrap_vms]scan.obj, -
+ [.bootstrap_vms]search.obj, -
+ [.bootstrap_vms]strings.obj, -
+ [.bootstrap_vms]subst.obj, -
+ [.bootstrap_vms]timestamp.obj, -
+ [.bootstrap_vms]variable.obj
+$ !
+$ SAY "Cleaning any previous build..."
+$ !
+$ MCR [.bootstrap_vms]jam0.exe -f build.jam --toolset=vmsdecc clean
+$ !
+$ SAY "Building Boost.Jam..."
+$ !
+$ MCR [.bootstrap_vms]jam0.exe -f build.jam --toolset=vmsdecc
diff --git a/jam-files/engine/builtins.c b/jam-files/engine/builtins.c
new file mode 100644
index 00000000..b28a484e
--- /dev/null
+++ b/jam-files/engine/builtins.c
@@ -0,0 +1,2310 @@
+/*
+ * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+#include "jam.h"
+
+#include "lists.h"
+#include "parse.h"
+#include "builtins.h"
+#include "rules.h"
+#include "filesys.h"
+#include "newstr.h"
+#include "regexp.h"
+#include "frames.h"
+#include "hash.h"
+#include "strings.h"
+#include "pwd.h"
+#include "pathsys.h"
+#include "make.h"
+#include "hdrmacro.h"
+#include "compile.h"
+#include "native.h"
+#include "variable.h"
+#include "timestamp.h"
+#include "md5.h"
+#include <ctype.h>
+
+#if defined(USE_EXECUNIX)
+# include <sys/types.h>
+# include <sys/wait.h>
+#else
+/*
+ NT does not have wait() and associated macros, it uses the return value
+ of system() instead. Status code group are documented at
+ http://msdn.microsoft.com/en-gb/library/ff565436.aspx
+*/
+# define WIFEXITED(w) (((w) & 0XFFFFFF00) == 0)
+# define WEXITSTATUS(w)(w)
+#endif
+
+/*
+ * builtins.c - builtin jam rules
+ *
+ * External routines:
+ *
+ * load_builtin() - define builtin rules
+ *
+ * Internal routines:
+ *
+ * builtin_depends() - DEPENDS/INCLUDES rule.
+ * builtin_echo() - ECHO rule.
+ * builtin_exit() - EXIT rule.
+ * builtin_flags() - NOCARE, NOTFILE, TEMPORARY rule.
+ * builtin_glob() - GLOB rule.
+ * builtin_match() - MATCH rule.
+ *
+ * 01/10/01 (seiwald) - split from compile.c
+ */
+
+
+/*
+ * compile_builtin() - define builtin rules
+ */
+
+#define P0 (PARSE *)0
+#define C0 (char *)0
+
+#if defined( OS_NT ) || defined( OS_CYGWIN )
+ LIST * builtin_system_registry ( PARSE *, FRAME * );
+ LIST * builtin_system_registry_names( PARSE *, FRAME * );
+#endif
+
+int glob( char * s, char * c );
+
+void backtrace ( FRAME * );
+void backtrace_line ( FRAME * );
+void print_source_line( PARSE * );
+
+
+RULE * bind_builtin( char * name, LIST * (* f)( PARSE *, FRAME * ), int flags, char * * args )
+{
+ argument_list* arg_list = 0;
+
+ if ( args )
+ {
+ arg_list = args_new();
+ lol_build( arg_list->data, args );
+ }
+
+ return new_rule_body( root_module(), name, arg_list,
+ parse_make( f, P0, P0, P0, C0, C0, flags ), 1 );
+}
+
+
+RULE * duplicate_rule( char * name, RULE * other )
+{
+ return import_rule( other, root_module(), name );
+}
+
+
+void load_builtins()
+{
+ duplicate_rule( "Always",
+ bind_builtin( "ALWAYS",
+ builtin_flags, T_FLAG_TOUCHED, 0 ) );
+
+ duplicate_rule( "Depends",
+ bind_builtin( "DEPENDS",
+ builtin_depends, 0, 0 ) );
+
+ duplicate_rule( "echo",
+ duplicate_rule( "Echo",
+ bind_builtin( "ECHO",
+ builtin_echo, 0, 0 ) ) );
+
+ {
+ char * args[] = { "message", "*", ":", "result-value", "?", 0 };
+ duplicate_rule( "exit",
+ duplicate_rule( "Exit",
+ bind_builtin( "EXIT",
+ builtin_exit, 0, args ) ) );
+ }
+
+ {
+ char * args[] = { "directories", "*", ":", "patterns", "*", ":", "case-insensitive", "?", 0 };
+ duplicate_rule( "Glob",
+ bind_builtin( "GLOB", builtin_glob, 0, args ) );
+ }
+
+ {
+ char * args[] = { "patterns", "*", 0 };
+ bind_builtin( "GLOB-RECURSIVELY",
+ builtin_glob_recursive, 0, args );
+ }
+
+ duplicate_rule( "Includes",
+ bind_builtin( "INCLUDES",
+ builtin_depends, 1, 0 ) );
+
+ {
+ char * args[] = { "targets", "*", ":", "targets-to-rebuild", "*", 0 };
+ bind_builtin( "REBUILDS",
+ builtin_rebuilds, 0, args );
+ }
+
+ duplicate_rule( "Leaves",
+ bind_builtin( "LEAVES",
+ builtin_flags, T_FLAG_LEAVES, 0 ) );
+
+ duplicate_rule( "Match",
+ bind_builtin( "MATCH",
+ builtin_match, 0, 0 ) );
+
+ {
+ char * args[] = { "string", ":", "delimiters" };
+ bind_builtin( "SPLIT_BY_CHARACTERS",
+ builtin_split_by_characters, 0, 0 );
+ }
+
+ duplicate_rule( "NoCare",
+ bind_builtin( "NOCARE",
+ builtin_flags, T_FLAG_NOCARE, 0 ) );
+
+ duplicate_rule( "NOTIME",
+ duplicate_rule( "NotFile",
+ bind_builtin( "NOTFILE",
+ builtin_flags, T_FLAG_NOTFILE, 0 ) ) );
+
+ duplicate_rule( "NoUpdate",
+ bind_builtin( "NOUPDATE",
+ builtin_flags, T_FLAG_NOUPDATE, 0 ) );
+
+ duplicate_rule( "Temporary",
+ bind_builtin( "TEMPORARY",
+ builtin_flags, T_FLAG_TEMP, 0 ) );
+
+ bind_builtin( "ISFILE",
+ builtin_flags, T_FLAG_ISFILE, 0 );
+
+ duplicate_rule( "HdrMacro",
+ bind_builtin( "HDRMACRO",
+ builtin_hdrmacro, 0, 0 ) );
+
+ /* FAIL_EXPECTED is used to indicate that the result of a target build
+ * action should be inverted (ok <=> fail) this can be useful when
+ * performing test runs from Jamfiles.
+ */
+ bind_builtin( "FAIL_EXPECTED",
+ builtin_flags, T_FLAG_FAIL_EXPECTED, 0 );
+
+ bind_builtin( "RMOLD",
+ builtin_flags, T_FLAG_RMOLD, 0 );
+
+ {
+ char * args[] = { "targets", "*", 0 };
+ bind_builtin( "UPDATE",
+ builtin_update, 0, args );
+ }
+
+ {
+ char * args[] = { "targets", "*",
+ ":", "log", "?",
+ ":", "ignore-minus-n", "?",
+ ":", "ignore-minus-q", "?", 0 };
+ bind_builtin( "UPDATE_NOW",
+ builtin_update_now, 0, args );
+ }
+
+ {
+ char * args[] = { "string", "pattern", "replacements", "+", 0 };
+ duplicate_rule( "subst",
+ bind_builtin( "SUBST",
+ builtin_subst, 0, args ) );
+ }
+
+ {
+ char * args[] = { "module", "?", 0 };
+ bind_builtin( "RULENAMES",
+ builtin_rulenames, 0, args );
+ }
+
+
+ {
+ char * args[] = { "module", "?", 0 };
+ bind_builtin( "VARNAMES",
+ builtin_varnames, 0, args );
+ }
+
+ {
+ char * args[] = { "module", "?", 0 };
+ bind_builtin( "DELETE_MODULE",
+ builtin_delete_module, 0, args );
+ }
+
+ {
+ char * args[] = { "source_module", "?",
+ ":", "source_rules", "*",
+ ":", "target_module", "?",
+ ":", "target_rules", "*",
+ ":", "localize", "?", 0 };
+ bind_builtin( "IMPORT",
+ builtin_import, 0, args );
+ }
+
+ {
+ char * args[] = { "module", "?", ":", "rules", "*", 0 };
+ bind_builtin( "EXPORT",
+ builtin_export, 0, args );
+ }
+
+ {
+ char * args[] = { "levels", "?", 0 };
+ bind_builtin( "CALLER_MODULE",
+ builtin_caller_module, 0, args );
+ }
+
+ {
+ char * args[] = { "levels", "?", 0 };
+ bind_builtin( "BACKTRACE",
+ builtin_backtrace, 0, args );
+ }
+
+ {
+ char * args[] = { 0 };
+ bind_builtin( "PWD",
+ builtin_pwd, 0, args );
+ }
+
+ {
+ char * args[] = { "target", "*", ":", "path", "*", 0 };
+ bind_builtin( "SEARCH_FOR_TARGET",
+ builtin_search_for_target, 0, args );
+ }
+
+ {
+ char * args[] = { "modules_to_import", "+", ":", "target_module", "?", 0 };
+ bind_builtin( "IMPORT_MODULE",
+ builtin_import_module, 0, args );
+ }
+
+ {
+ char * args[] = { "module", "?", 0 };
+ bind_builtin( "IMPORTED_MODULES",
+ builtin_imported_modules, 0, args );
+ }
+
+ {
+ char * args[] = { "instance_module", ":", "class_module", 0 };
+ bind_builtin( "INSTANCE",
+ builtin_instance, 0, args );
+ }
+
+ {
+ char * args[] = { "sequence", "*", 0 };
+ bind_builtin( "SORT",
+ builtin_sort, 0, args );
+ }
+
+ {
+ char * args[] = { "path_parts", "*", 0 };
+ bind_builtin( "NORMALIZE_PATH",
+ builtin_normalize_path, 0, args );
+ }
+
+ {
+ char * args[] = { "args", "*", 0 };
+ bind_builtin( "CALC",
+ builtin_calc, 0, args );
+ }
+
+ {
+ char * args[] = { "module", ":", "rule", 0 };
+ bind_builtin( "NATIVE_RULE",
+ builtin_native_rule, 0, args );
+ }
+
+ {
+ char * args[] = { "module", ":", "rule", ":", "version", 0 };
+ bind_builtin( "HAS_NATIVE_RULE",
+ builtin_has_native_rule, 0, args );
+ }
+
+ {
+ char * args[] = { "module", "*", 0 };
+ bind_builtin( "USER_MODULE",
+ builtin_user_module, 0, args );
+ }
+
+ {
+ char * args[] = { 0 };
+ bind_builtin( "NEAREST_USER_LOCATION",
+ builtin_nearest_user_location, 0, args );
+ }
+
+ {
+ char * args[] = { "file", 0 };
+ bind_builtin( "CHECK_IF_FILE",
+ builtin_check_if_file, 0, args );
+ }
+
+#ifdef HAVE_PYTHON
+ {
+ char * args[] = { "python-module", ":", "function", ":",
+ "jam-module", ":", "rule-name", 0 };
+ bind_builtin( "PYTHON_IMPORT_RULE",
+ builtin_python_import_rule, 0, args );
+ }
+#endif
+
+# if defined( OS_NT ) || defined( OS_CYGWIN )
+ {
+ char * args[] = { "key_path", ":", "data", "?", 0 };
+ bind_builtin( "W32_GETREG",
+ builtin_system_registry, 0, args );
+ }
+
+ {
+ char * args[] = { "key_path", ":", "result-type", 0 };
+ bind_builtin( "W32_GETREGNAMES",
+ builtin_system_registry_names, 0, args );
+ }
+# endif
+
+ {
+ char * args[] = { "command", ":", "*", 0 };
+ duplicate_rule( "SHELL",
+ bind_builtin( "COMMAND",
+ builtin_shell, 0, args ) );
+ }
+
+ {
+ char * args[] = { "string", 0 };
+ bind_builtin( "MD5",
+ builtin_md5, 0, args ) ;
+ }
+
+ {
+ char * args[] = { "name", ":", "mode", 0 };
+ bind_builtin( "FILE_OPEN",
+ builtin_file_open, 0, args );
+ }
+
+ {
+ char * args[] = { "string", ":", "width", 0 };
+ bind_builtin( "PAD",
+ builtin_pad, 0, args );
+ }
+
+ {
+ char * args[] = { "targets", "*", 0 };
+ bind_builtin( "PRECIOUS",
+ builtin_precious, 0, args );
+ }
+
+ {
+ char * args [] = { 0 };
+ bind_builtin( "SELF_PATH", builtin_self_path, 0, args );
+ }
+
+ {
+ char * args [] = { "path", 0 };
+ bind_builtin( "MAKEDIR", builtin_makedir, 0, args );
+ }
+
+ /* Initialize builtin modules. */
+ init_set();
+ init_path();
+ init_regex();
+ init_property_set();
+ init_sequence();
+ init_order();
+}
+
+
+/*
+ * builtin_calc() - CALC rule.
+ *
+ * The CALC rule performs simple mathematical operations on two arguments.
+ */
+
+LIST * builtin_calc( PARSE * parse, FRAME * frame )
+{
+ LIST * arg = lol_get( frame->args, 0 );
+
+ LIST * result = 0;
+ long lhs_value;
+ long rhs_value;
+ long result_value;
+ char buffer [ 16 ];
+ char const * lhs;
+ char const * op;
+ char const * rhs;
+
+ if ( arg == 0 ) return L0;
+ lhs = arg->string;
+
+ arg = list_next( arg );
+ if ( arg == 0 ) return L0;
+ op = arg->string;
+
+ arg = list_next( arg );
+ if ( arg == 0 ) return L0;
+ rhs = arg->string;
+
+ lhs_value = atoi( lhs );
+ rhs_value = atoi( rhs );
+
+ if ( strcmp( "+", op ) == 0 )
+ {
+ result_value = lhs_value + rhs_value;
+ }
+ else if ( strcmp( "-", op ) == 0 )
+ {
+ result_value = lhs_value - rhs_value;
+ }
+ else
+ {
+ return L0;
+ }
+
+ sprintf( buffer, "%ld", result_value );
+ result = list_new( result, newstr( buffer ) );
+ return result;
+}
+
+
+/*
+ * builtin_depends() - DEPENDS/INCLUDES rule.
+ *
+ * The DEPENDS/INCLUDES builtin rule appends each of the listed sources on the
+ * dependency/includes list of each of the listed targets. It binds both the
+ * targets and sources as TARGETs.
+ */
+
+LIST * builtin_depends( PARSE * parse, FRAME * frame )
+{
+ LIST * targets = lol_get( frame->args, 0 );
+ LIST * sources = lol_get( frame->args, 1 );
+ LIST * l;
+
+ for ( l = targets; l; l = list_next( l ) )
+ {
+ TARGET * t = bindtarget( l->string );
+
+ /* If doing INCLUDES, switch to the TARGET's include */
+ /* TARGET, creating it if needed. The internal include */
+ /* TARGET shares the name of its parent. */
+
+ if ( parse->num )
+ {
+ if ( !t->includes )
+ {
+ t->includes = copytarget( t );
+ t->includes->original_target = t;
+ }
+ t = t->includes;
+ }
+
+ t->depends = targetlist( t->depends, sources );
+ }
+
+ /* Enter reverse links */
+ for ( l = sources; l; l = list_next( l ) )
+ {
+ TARGET * s = bindtarget( l->string );
+ s->dependants = targetlist( s->dependants, targets );
+ }
+
+ return L0;
+}
+
+
+/*
+ * builtin_rebuilds() - REBUILDS rule.
+ *
+ * The REBUILDS builtin rule appends each of the listed rebuild-targets in its
+ * 2nd argument on the rebuilds list of each of the listed targets in its first
+ * argument.
+ */
+
+LIST * builtin_rebuilds( PARSE * parse, FRAME * frame )
+{
+ LIST * targets = lol_get( frame->args, 0 );
+ LIST * rebuilds = lol_get( frame->args, 1 );
+ LIST * l;
+
+ for ( l = targets; l; l = list_next( l ) )
+ {
+ TARGET * t = bindtarget( l->string );
+ t->rebuilds = targetlist( t->rebuilds, rebuilds );
+ }
+
+ return L0;
+}
+
+
+/*
+ * builtin_echo() - ECHO rule.
+ *
+ * The ECHO builtin rule echoes the targets to the user. No other actions are
+ * taken.
+ */
+
+LIST * builtin_echo( PARSE * parse, FRAME * frame )
+{
+ list_print( lol_get( frame->args, 0 ) );
+ printf( "\n" );
+ fflush( stdout );
+ return L0;
+}
+
+
+/*
+ * builtin_exit() - EXIT rule.
+ *
+ * The EXIT builtin rule echoes the targets to the user and exits the program
+ * with a failure status.
+ */
+
+LIST * builtin_exit( PARSE * parse, FRAME * frame )
+{
+ list_print( lol_get( frame->args, 0 ) );
+ printf( "\n" );
+ if ( lol_get( frame->args, 1 ) )
+ {
+ exit( atoi( lol_get( frame->args, 1 )->string ) );
+ }
+ else
+ {
+ exit( EXITBAD ); /* yeech */
+ }
+ return L0;
+}
+
+
+/*
+ * builtin_flags() - NOCARE, NOTFILE, TEMPORARY rule.
+ *
+ * Builtin_flags() marks the target with the appropriate flag, for use by make0().
+ * It binds each target as a TARGET.
+ */
+
+LIST * builtin_flags( PARSE * parse, FRAME * frame )
+{
+ LIST * l = lol_get( frame->args, 0 );
+ for ( ; l; l = list_next( l ) )
+ bindtarget( l->string )->flags |= parse->num;
+ return L0;
+}
+
+
+/*
+ * builtin_globbing() - GLOB rule.
+ */
+
+struct globbing
+{
+ LIST * patterns;
+ LIST * results;
+ LIST * case_insensitive;
+};
+
+
+static void downcase_inplace( char * p )
+{
+ for ( ; *p; ++p )
+ *p = tolower( *p );
+}
+
+
+static void builtin_glob_back
+(
+ void * closure,
+ char * file,
+ int status,
+ time_t time
+)
+{
+ PROFILE_ENTER( BUILTIN_GLOB_BACK );
+
+ struct globbing * globbing = (struct globbing *)closure;
+ LIST * l;
+ PATHNAME f;
+ string buf[ 1 ];
+
+ /* Null out directory for matching. We wish we had file_dirscan() pass up a
+ * PATHNAME.
+ */
+ path_parse( file, &f );
+ f.f_dir.len = 0;
+
+ /* For globbing, we unconditionally ignore current and parent directory
+ * items. Since they items always exist, there is no reason why caller of
+ * GLOB would want to see them. We could also change file_dirscan(), but
+ * then paths with embedded "." and ".." would not work anywhere.
+ */
+ if ( !strcmp( f.f_base.ptr, "." ) || !strcmp( f.f_base.ptr, ".." ) )
+ {
+ PROFILE_EXIT( BUILTIN_GLOB_BACK );
+ return;
+ }
+
+ string_new( buf );
+ path_build( &f, buf, 0 );
+
+ if ( globbing->case_insensitive )
+ downcase_inplace( buf->value );
+
+ for ( l = globbing->patterns; l; l = l->next )
+ {
+ if ( !glob( l->string, buf->value ) )
+ {
+ globbing->results = list_new( globbing->results, newstr( file ) );
+ break;
+ }
+ }
+
+ string_free( buf );
+
+ PROFILE_EXIT( BUILTIN_GLOB_BACK );
+}
+
+
+static LIST * downcase_list( LIST * in )
+{
+ LIST * result = 0;
+
+ string s[ 1 ];
+ string_new( s );
+
+ while ( in )
+ {
+ string_copy( s, in->string );
+ downcase_inplace( s->value );
+ result = list_append( result, list_new( 0, newstr( s->value ) ) );
+ in = in->next;
+ }
+
+ string_free( s );
+ return result;
+}
+
+
+LIST * builtin_glob( PARSE * parse, FRAME * frame )
+{
+ LIST * l = lol_get( frame->args, 0 );
+ LIST * r = lol_get( frame->args, 1 );
+
+ struct globbing globbing;
+
+ globbing.results = L0;
+ globbing.patterns = r;
+
+ globbing.case_insensitive
+# if defined( OS_NT ) || defined( OS_CYGWIN )
+ = l; /* Always case-insensitive if any files can be found. */
+# else
+ = lol_get( frame->args, 2 );
+# endif
+
+ if ( globbing.case_insensitive )
+ globbing.patterns = downcase_list( r );
+
+ for ( ; l; l = list_next( l ) )
+ file_dirscan( l->string, builtin_glob_back, &globbing );
+
+ if ( globbing.case_insensitive )
+ list_free( globbing.patterns );
+
+ return globbing.results;
+}
+
+
+static int has_wildcards( char const * str )
+{
+ size_t const index = strcspn( str, "[]*?" );
+ return str[ index ] == '\0' ? 0 : 1;
+}
+
+
+/*
+ * If 'file' exists, append 'file' to 'list'. Returns 'list'.
+ */
+
+static LIST * append_if_exists( LIST * list, char * file )
+{
+ time_t time;
+ timestamp( file, &time );
+ return time > 0
+ ? list_new( list, newstr( file ) )
+ : list;
+}
+
+
+LIST * glob1( char * dirname, char * pattern )
+{
+ LIST * plist = list_new( L0, pattern );
+ struct globbing globbing;
+
+ globbing.results = L0;
+ globbing.patterns = plist;
+
+ globbing.case_insensitive
+# if defined( OS_NT ) || defined( OS_CYGWIN )
+ = plist; /* always case-insensitive if any files can be found */
+# else
+ = L0;
+# endif
+
+ if ( globbing.case_insensitive )
+ globbing.patterns = downcase_list( plist );
+
+ file_dirscan( dirname, builtin_glob_back, &globbing );
+
+ if ( globbing.case_insensitive )
+ list_free( globbing.patterns );
+
+ list_free( plist );
+
+ return globbing.results;
+}
+
+
+LIST * glob_recursive( char * pattern )
+{
+ LIST * result = L0;
+
+ /* Check if there's metacharacters in pattern */
+ if ( !has_wildcards( pattern ) )
+ {
+ /* No metacharacters. Check if the path exists. */
+ result = append_if_exists(result, pattern);
+ }
+ else
+ {
+ /* Have metacharacters in the pattern. Split into dir/name. */
+ PATHNAME path[ 1 ];
+ path_parse( pattern, path );
+
+ if ( path->f_dir.ptr )
+ {
+ LIST * dirs = L0;
+ string dirname[ 1 ];
+ string basename[ 1 ];
+ string_new( dirname );
+ string_new( basename );
+
+ string_append_range( dirname, path->f_dir.ptr,
+ path->f_dir.ptr + path->f_dir.len );
+
+ path->f_grist.ptr = 0;
+ path->f_grist.len = 0;
+ path->f_dir.ptr = 0;
+ path->f_dir.len = 0;
+ path_build( path, basename, 0 );
+
+ dirs = has_wildcards( dirname->value )
+ ? glob_recursive( dirname->value )
+ : list_new( dirs, dirname->value );
+
+ if ( has_wildcards( basename->value ) )
+ {
+ for ( ; dirs; dirs = dirs->next )
+ result = list_append( result, glob1( dirs->string,
+ basename->value ) );
+ }
+ else
+ {
+ string file_string[ 1 ];
+ string_new( file_string );
+
+ /* No wildcard in basename. */
+ for ( ; dirs; dirs = dirs->next )
+ {
+ path->f_dir.ptr = dirs->string;
+ path->f_dir.len = strlen( dirs->string );
+ path_build( path, file_string, 0 );
+
+ result = append_if_exists( result, file_string->value );
+
+ string_truncate( file_string, 0 );
+ }
+
+ string_free( file_string );
+ }
+
+ string_free( dirname );
+ string_free( basename );
+ }
+ else
+ {
+ /** No directory, just a pattern. */
+ result = list_append( result, glob1( ".", pattern ) );
+ }
+ }
+
+ return result;
+}
+
+
+LIST * builtin_glob_recursive( PARSE * parse, FRAME * frame )
+{
+ LIST * result = L0;
+ LIST * l = lol_get( frame->args, 0 );
+ for ( ; l; l = l->next )
+ result = list_append( result, glob_recursive( l->string ) );
+ return result;
+}
+
+
+/*
+ * builtin_match() - MATCH rule, regexp matching.
+ */
+
+LIST * builtin_match( PARSE * parse, FRAME * frame )
+{
+ LIST * l;
+ LIST * r;
+ LIST * result = 0;
+
+ string buf[ 1 ];
+ string_new( buf );
+
+ /* For each pattern */
+
+ for ( l = lol_get( frame->args, 0 ); l; l = l->next )
+ {
+ /* Result is cached and intentionally never freed. */
+ regexp * re = regex_compile( l->string );
+
+ /* For each string to match against. */
+ for ( r = lol_get( frame->args, 1 ); r; r = r->next )
+ {
+ if ( regexec( re, r->string ) )
+ {
+ int i;
+ int top;
+
+ /* Find highest parameter */
+
+ for ( top = NSUBEXP; top-- > 1; )
+ if ( re->startp[ top ] )
+ break;
+
+ /* And add all parameters up to highest onto list. */
+ /* Must have parameters to have results! */
+ for ( i = 1; i <= top; ++i )
+ {
+ string_append_range( buf, re->startp[ i ], re->endp[ i ] );
+ result = list_new( result, newstr( buf->value ) );
+ string_truncate( buf, 0 );
+ }
+ }
+ }
+ }
+
+ string_free( buf );
+ return result;
+}
+
+LIST * builtin_split_by_characters( PARSE * parse, FRAME * frame )
+{
+ LIST * l1 = lol_get( frame->args, 0 );
+ LIST * l2 = lol_get( frame->args, 1 );
+
+ LIST * result = 0;
+
+ char* s = strdup (l1->string);
+ char* delimiters = l2->string;
+ char* t;
+
+ t = strtok (s, delimiters);
+ while (t)
+ {
+ result = list_new(result, newstr(t));
+ t = strtok (NULL, delimiters);
+ }
+
+ free (s);
+
+ return result;
+}
+
+LIST * builtin_hdrmacro( PARSE * parse, FRAME * frame )
+{
+ LIST * l = lol_get( frame->args, 0 );
+
+ for ( ; l; l = list_next( l ) )
+ {
+ TARGET * t = bindtarget( l->string );
+
+ /* Scan file for header filename macro definitions. */
+ if ( DEBUG_HEADER )
+ printf( "scanning '%s' for header file macro definitions\n",
+ l->string );
+
+ macro_headers( t );
+ }
+
+ return L0;
+}
+
+
+/*
+ * builtin_rulenames() - RULENAMES ( MODULE ? ).
+ *
+ * Returns a list of the non-local rule names in the given MODULE. If MODULE is
+ * not supplied, returns the list of rule names in the global module.
+ */
+
+static void add_rule_name( void * r_, void * result_ )
+{
+ RULE * r = (RULE *)r_;
+ LIST * * result = (LIST * *)result_;
+ if ( r->exported )
+ *result = list_new( *result, copystr( r->name ) );
+}
+
+
+LIST * builtin_rulenames( PARSE * parse, FRAME * frame )
+{
+ LIST * arg0 = lol_get( frame->args, 0 );
+ LIST * result = L0;
+ module_t * source_module = bindmodule( arg0 ? arg0->string : 0 );
+
+ if ( source_module->rules )
+ hashenumerate( source_module->rules, add_rule_name, &result );
+ return result;
+}
+
+
+/*
+ * builtin_varnames() - VARNAMES ( MODULE ? ).
+ *
+ * Returns a list of the variable names in the given MODULE. If MODULE is not
+ * supplied, returns the list of variable names in the global module.
+ */
+
+/* helper function for builtin_varnames(), below. Used with hashenumerate, will
+ * prepend the key of each element to the list
+ */
+static void add_hash_key( void * np, void * result_ )
+{
+ LIST * * result = (LIST * *)result_;
+ *result = list_new( *result, copystr( *(char * *)np ) );
+}
+
+
+static struct hash * get_running_module_vars()
+{
+ struct hash * dummy;
+ struct hash * vars = NULL;
+ /* Get the global variables pointer (that of the currently running module).
+ */
+ var_hash_swap( &vars );
+ dummy = vars;
+ /* Put the global variables pointer in its right place. */
+ var_hash_swap( &dummy );
+ return vars;
+}
+
+
+LIST * builtin_varnames( PARSE * parse, FRAME * frame )
+{
+ LIST * arg0 = lol_get( frame->args, 0 );
+ LIST * result = L0;
+ module_t * source_module = bindmodule( arg0 ? arg0->string : 0 );
+
+ /* The running module _always_ has its 'variables' member set to NULL due to
+ * the way enter_module() and var_hash_swap() work.
+ */
+ struct hash * vars = source_module == frame->module
+ ? get_running_module_vars()
+ : source_module->variables;
+
+ if ( vars )
+ hashenumerate( vars, add_hash_key, &result );
+ return result;
+}
+
+
+/*
+ * builtin_delete_module() - MODULE ?.
+ *
+ * Clears all rules and variables from the given module.
+ */
+
+LIST * builtin_delete_module( PARSE * parse, FRAME * frame )
+{
+ LIST * arg0 = lol_get( frame->args, 0 );
+ LIST * result = L0;
+ module_t * source_module = bindmodule( arg0 ? arg0->string : 0 );
+ delete_module( source_module );
+ return result;
+}
+
+
+static void unknown_rule( FRAME * frame, char * key, char * module_name, char * rule_name )
+{
+ backtrace_line( frame->prev );
+ printf( "%s error: rule \"%s\" unknown in module \"%s\"\n", key, rule_name, module_name );
+ backtrace( frame->prev );
+ exit( 1 );
+}
+
+
+/*
+ * builtin_import() - IMPORT
+ * (
+ * SOURCE_MODULE ? :
+ * SOURCE_RULES * :
+ * TARGET_MODULE ? :
+ * TARGET_RULES * :
+ * LOCALIZE ?
+ * )
+ *
+ * The IMPORT rule imports rules from the SOURCE_MODULE into the TARGET_MODULE
+ * as local rules. If either SOURCE_MODULE or TARGET_MODULE is not supplied, it
+ * refers to the global module. SOURCE_RULES specifies which rules from the
+ * SOURCE_MODULE to import; TARGET_RULES specifies the names to give those rules
+ * in TARGET_MODULE. If SOURCE_RULES contains a name which doesn't correspond to
+ * a rule in SOURCE_MODULE, or if it contains a different number of items than
+ * TARGET_RULES, an error is issued. If LOCALIZE is specified, the rules will be
+ * executed in TARGET_MODULE, with corresponding access to its module local
+ * variables.
+ */
+
+LIST * builtin_import( PARSE * parse, FRAME * frame )
+{
+ LIST * source_module_list = lol_get( frame->args, 0 );
+ LIST * source_rules = lol_get( frame->args, 1 );
+ LIST * target_module_list = lol_get( frame->args, 2 );
+ LIST * target_rules = lol_get( frame->args, 3 );
+ LIST * localize = lol_get( frame->args, 4 );
+
+ module_t * target_module =
+ bindmodule( target_module_list ? target_module_list->string : 0 );
+ module_t * source_module =
+ bindmodule( source_module_list ? source_module_list->string : 0 );
+
+ LIST * source_name;
+ LIST * target_name;
+
+ for ( source_name = source_rules, target_name = target_rules;
+ source_name && target_name;
+ source_name = list_next( source_name ),
+ target_name = list_next( target_name ) )
+ {
+ RULE r_;
+ RULE * r = &r_;
+ RULE * imported;
+ r_.name = source_name->string;
+
+ if ( !source_module->rules ||
+ !hashcheck( source_module->rules, (HASHDATA * *)&r ) )
+ unknown_rule( frame, "IMPORT", source_module->name, r_.name );
+
+ imported = import_rule( r, target_module, target_name->string );
+ if ( localize )
+ imported->module = target_module;
+ /* This rule is really part of some other module. Just refer to it here,
+ * but do not let it out.
+ */
+ imported->exported = 0;
+ }
+
+ if ( source_name || target_name )
+ {
+ backtrace_line( frame->prev );
+ printf( "import error: length of source and target rule name lists don't match!\n" );
+ printf( " source: " );
+ list_print( source_rules );
+ printf( "\n target: " );
+ list_print( target_rules );
+ printf( "\n" );
+ backtrace( frame->prev );
+ exit( 1 );
+ }
+
+ return L0;
+}
+
+
+/*
+ * builtin_export() - EXPORT ( MODULE ? : RULES * ).
+ *
+ * The EXPORT rule marks RULES from the SOURCE_MODULE as non-local (and thus
+ * exportable). If an element of RULES does not name a rule in MODULE, an error
+ * is issued.
+ */
+
+LIST * builtin_export( PARSE * parse, FRAME * frame )
+{
+ LIST * module_list = lol_get( frame->args, 0 );
+ LIST * rules = lol_get( frame->args, 1 );
+ module_t * m = bindmodule( module_list ? module_list->string : 0 );
+
+ for ( ; rules; rules = list_next( rules ) )
+ {
+ RULE r_;
+ RULE * r = &r_;
+ r_.name = rules->string;
+
+ if ( !m->rules || !hashcheck( m->rules, (HASHDATA * *)&r ) )
+ unknown_rule( frame, "EXPORT", m->name, r_.name );
+
+ r->exported = 1;
+ }
+ return L0;
+}
+
+
+/*
+ * get_source_line() - Retrieve the file and line number that should be
+ * indicated for a given procedure in debug output or an error backtrace.
+ */
+
+static void get_source_line( PARSE * procedure, char * * file, int * line )
+{
+ if ( procedure )
+ {
+ char * f = procedure->file;
+ int l = procedure->line;
+ if ( !strcmp( f, "+" ) )
+ {
+ f = "jambase.c";
+ l += 3;
+ }
+ *file = f;
+ *line = l;
+ }
+ else
+ {
+ *file = "(builtin)";
+ *line = -1;
+ }
+}
+
+
+void print_source_line( PARSE * p )
+{
+ char * file;
+ int line;
+
+ get_source_line( p, &file, &line );
+ if ( line < 0 )
+ printf( "(builtin):" );
+ else
+ printf( "%s:%d:", file, line );
+}
+
+
+/*
+ * backtrace_line() - print a single line of error backtrace for the given
+ * frame.
+ */
+
+void backtrace_line( FRAME * frame )
+{
+ if ( frame == 0 )
+ {
+ printf( "(no frame):" );
+ }
+ else
+ {
+ print_source_line( frame->procedure );
+ printf( " in %s\n", frame->rulename );
+ }
+}
+
+
+/*
+ * backtrace() - Print the entire backtrace from the given frame to the Jambase
+ * which invoked it.
+ */
+
+void backtrace( FRAME * frame )
+{
+ if ( !frame ) return;
+ while ( ( frame = frame->prev ) )
+ backtrace_line( frame );
+}
+
+
+/*
+ * builtin_backtrace() - A Jam version of the backtrace function, taking no
+ * arguments and returning a list of quadruples: FILENAME LINE MODULE. RULENAME
+ * describing each frame. Note that the module-name is always followed by a
+ * period.
+ */
+
+LIST * builtin_backtrace( PARSE * parse, FRAME * frame )
+{
+ LIST * levels_arg = lol_get( frame->args, 0 );
+ int levels = levels_arg ? atoi( levels_arg->string ) : ( (unsigned int)(-1) >> 1 ) ;
+
+ LIST * result = L0;
+ for ( ; ( frame = frame->prev ) && levels ; --levels )
+ {
+ char * file;
+ int line;
+ char buf[32];
+ get_source_line( frame->procedure, &file, &line );
+ sprintf( buf, "%d", line );
+ result = list_new( result, newstr( file ) );
+ result = list_new( result, newstr( buf ) );
+ result = list_new( result, newstr( frame->module->name ) );
+ result = list_new( result, newstr( frame->rulename ) );
+ }
+ return result;
+}
+
+
+/*
+ * builtin_caller_module() - CALLER_MODULE ( levels ? )
+ *
+ * If levels is not supplied, returns the name of the module of the rule which
+ * called the one calling this one. If levels is supplied, it is interpreted as
+ * an integer specifying a number of additional levels of call stack to traverse
+ * in order to locate the module in question. If no such module exists, returns
+ * the empty list. Also returns the empty list when the module in question is
+ * the global module. This rule is needed for implementing module import
+ * behavior.
+ */
+
+LIST * builtin_caller_module( PARSE * parse, FRAME * frame )
+{
+ LIST * levels_arg = lol_get( frame->args, 0 );
+ int levels = levels_arg ? atoi( levels_arg->string ) : 0 ;
+
+ int i;
+ for ( i = 0; ( i < levels + 2 ) && frame->prev; ++i )
+ frame = frame->prev;
+
+ if ( frame->module == root_module() )
+ return L0;
+
+ {
+ LIST * result;
+ string name;
+ string_copy( &name, frame->module->name );
+ string_pop_back( &name );
+ result = list_new( L0, newstr(name.value) );
+ string_free( &name );
+ return result;
+ }
+}
+
+
+/*
+ * Return the current working directory.
+ *
+ * Usage: pwd = [ PWD ] ;
+ */
+
+LIST * builtin_pwd( PARSE * parse, FRAME * frame )
+{
+ return pwd();
+}
+
+
+/*
+ * Adds targets to the list of target that jam will attempt to update.
+ */
+
+LIST * builtin_update( PARSE * parse, FRAME * frame )
+{
+ LIST * result = list_copy( L0, targets_to_update() );
+ LIST * arg1 = lol_get( frame->args, 0 );
+ clear_targets_to_update();
+ for ( ; arg1; arg1 = list_next( arg1 ) )
+ mark_target_for_updating( newstr( arg1->string ) );
+ return result;
+}
+
+extern int anyhow;
+int last_update_now_status;
+
+/* Takes a list of target names as first argument, and immediately
+ updates them.
+ Second parameter, if specified, if the descriptor (converted to a string)
+ of a log file where all build output is redirected.
+ Third parameter, if non-empty, specifies that the -n option should have
+ no effect -- that is, all out-of-date targets should be rebuild.
+*/
+LIST * builtin_update_now( PARSE * parse, FRAME * frame )
+{
+ LIST * targets = lol_get( frame->args, 0 );
+ LIST * log = lol_get( frame->args, 1 );
+ LIST * force = lol_get (frame->args, 2);
+ LIST * continue_ = lol_get(frame->args, 3);
+ int status = 0;
+ int original_stdout;
+ int original_stderr;
+ int n;
+ int targets_count;
+ const char** targets2;
+ int i;
+ int original_noexec;
+ int original_quitquick;
+
+
+ if (log)
+ {
+ int fd = atoi(log->string);
+ /* Redirect stdout and stderr, temporary, to the log file. */
+ original_stdout = dup (0);
+ original_stderr = dup (1);
+ dup2 (fd, 0);
+ dup2 (fd, 1);
+ }
+
+ if (force)
+ {
+ original_noexec = globs.noexec;
+ globs.noexec = 0;
+ original_quitquick = globs.quitquick;
+ globs.quitquick = 0;
+ }
+
+ if (continue_)
+ {
+ original_quitquick = globs.quitquick;
+ globs.quitquick = 0;
+ }
+
+ targets_count = list_length( targets );
+ targets2 = (const char * *)BJAM_MALLOC( targets_count * sizeof( char * ) );
+ for (i = 0 ; targets; targets = list_next( targets ) )
+ targets2[ i++ ] = targets->string;
+ status |= make( targets_count, targets2, anyhow);
+ free( targets );
+
+ if (force)
+ {
+ globs.noexec = original_noexec;
+ globs.quitquick = original_quitquick;
+ }
+
+ if (continue_)
+ {
+ globs.quitquick = original_quitquick;
+ }
+
+ if (log)
+ {
+ /* Flush whatever stdio might have buffered, while descriptions
+ 0 and 1 still refer to the log file. */
+ fflush (stdout);
+ fflush (stderr);
+ dup2 (original_stdout, 0);
+ dup2 (original_stderr, 1);
+ close (original_stdout);
+ close (original_stderr);
+ }
+
+ last_update_now_status = status;
+
+ if (status == 0)
+ return list_new (L0, newstr ("ok"));
+ else
+ return L0;
+}
+
+LIST * builtin_search_for_target( PARSE * parse, FRAME * frame )
+{
+ LIST * arg1 = lol_get( frame->args, 0 );
+ LIST * arg2 = lol_get( frame->args, 1 );
+ TARGET * t = search_for_target( arg1->string, arg2 );
+ return list_new( L0, t->name );
+}
+
+
+LIST * builtin_import_module( PARSE * parse, FRAME * frame )
+{
+ LIST * arg1 = lol_get( frame->args, 0 );
+ LIST * arg2 = lol_get( frame->args, 1 );
+ module_t * m = arg2 ? bindmodule( arg2->string ) : root_module();
+ import_module( arg1, m );
+ return L0;
+}
+
+
+LIST * builtin_imported_modules( PARSE * parse, FRAME * frame )
+{
+ LIST * arg0 = lol_get( frame->args, 0 );
+ return imported_modules( bindmodule( arg0 ? arg0->string : 0 ) );
+}
+
+
+LIST * builtin_instance( PARSE * parse, FRAME * frame )
+{
+ LIST * arg1 = lol_get( frame->args, 0 );
+ LIST * arg2 = lol_get( frame->args, 1 );
+ module_t * const instance = bindmodule( arg1->string );
+ module_t * const class_module = bindmodule( arg2->string );
+ instance->class_module = class_module;
+ return L0;
+}
+
+
+LIST * builtin_sort( PARSE * parse, FRAME * frame )
+{
+ LIST * arg1 = lol_get( frame->args, 0 );
+ return list_sort( arg1 );
+}
+
+
+LIST * builtin_normalize_path( PARSE * parse, FRAME * frame )
+{
+ LIST * arg = lol_get( frame->args, 0 );
+
+ /* First, we iterate over all '/'-separated elements, starting from the end
+ * of string. If we see a '..', we remove a previous path elements. If we
+ * see '.', we remove it. The removal is done by overwriting data using '\1'
+ * in the string. After the whole string has been processed, we do a second
+ * pass, removing all the entered '\1' characters.
+ */
+
+ string in[ 1 ];
+ string out[ 1 ];
+ /* Last character of the part of string still to be processed. */
+ char * end;
+ /* Working pointer. */
+ char * current;
+ /* Number of '..' elements seen and not processed yet. */
+ int dotdots = 0;
+ int rooted = 0;
+ char * result = 0;
+
+ /* Make a copy of input: we should not change it. Prepend a '/' before it as
+ * a guard for the algorithm later on and remember whether it was originally
+ * rooted or not.
+ */
+ string_new( in );
+ string_push_back( in, '/' );
+ for ( ; arg; arg = list_next( arg ) )
+ {
+ if ( arg->string[ 0 ] != '\0' )
+ {
+ if ( in->size == 1 )
+ rooted = ( ( arg->string[ 0 ] == '/' ) ||
+ ( arg->string[ 0 ] == '\\' ) );
+ else
+ string_append( in, "/" );
+ string_append( in, arg->string );
+ }
+ }
+
+ /* Convert \ into /. On Windows, paths using / and \ are equivalent, and we
+ * want this function to obtain a canonic representation.
+ */
+ for ( current = in->value, end = in->value + in->size;
+ current < end; ++current )
+ if ( *current == '\\' )
+ *current = '/';
+
+ /* Now we remove any extra path elements by overwriting them with '\1'
+ * characters and cound how many more unused '..' path elements there are
+ * remaining. Note that each remaining path element with always starts with
+ * a '/' character.
+ */
+ for ( end = in->value + in->size - 1; end >= in->value; )
+ {
+ /* Set 'current' to the next occurence of '/', which always exists. */
+ for ( current = end; *current != '/'; --current );
+
+ if ( current == end )
+ {
+ /* Found a trailing or duplicate '/'. Remove it. */
+ *current = '\1';
+ }
+ else if ( ( end - current == 1 ) && ( *(current + 1) == '.' ) )
+ {
+ /* Found '/.'. Remove them all. */
+ *current = '\1';
+ *(current + 1) = '\1';
+ }
+ else if ( ( end - current == 2 ) && ( *(current + 1) == '.' ) && ( *(current + 2) == '.' ) )
+ {
+ /* Found '/..'. Remove them all. */
+ *current = '\1';
+ *(current + 1) = '\1';
+ *(current + 2) = '\1';
+ ++dotdots;
+ }
+ else if ( dotdots )
+ {
+ memset( current, '\1', end - current + 1 );
+ --dotdots;
+ }
+ end = current - 1;
+ }
+
+ string_new( out );
+
+ /* Now we know that we need to add exactly dotdots '..' path elements to the
+ * front and that our string is either empty or has a '/' as its first
+ * significant character. If we have any dotdots remaining then the passed
+ * path must not have been rooted or else it is invalid we return an empty
+ * list.
+ */
+ if ( dotdots )
+ {
+ if ( rooted ) return L0;
+ do
+ string_append( out, "/.." );
+ while ( --dotdots );
+ }
+
+ /* Now we actually remove all the path characters marked for removal. */
+ for ( current = in->value; *current; ++current )
+ if ( *current != '\1' )
+ string_push_back( out, *current );
+
+ /* Here we know that our string contains no '\1' characters and is either
+ * empty or has a '/' as its initial character. If the original path was not
+ * rooted and we have a non-empty path we need to drop the initial '/'. If
+ * the original path was rooted and we have an empty path we need to add
+ * back the '/'.
+ */
+ result = newstr( out->size ? out->value + !rooted : ( rooted ? "/" : "." ) );
+
+ string_free( out );
+ string_free( in );
+
+ return list_new( 0, result );
+}
+
+
+LIST * builtin_native_rule( PARSE * parse, FRAME * frame )
+{
+ LIST * module_name = lol_get( frame->args, 0 );
+ LIST * rule_name = lol_get( frame->args, 1 );
+
+ module_t * module = bindmodule( module_name->string );
+
+ native_rule_t n;
+ native_rule_t * np = &n;
+ n.name = rule_name->string;
+ if ( module->native_rules && hashcheck( module->native_rules, (HASHDATA * *)&np ) )
+ {
+ new_rule_body( module, np->name, np->arguments, np->procedure, 1 );
+ }
+ else
+ {
+ backtrace_line( frame->prev );
+ printf( "error: no native rule \"%s\" defined in module \"%s\"\n",
+ n.name, module->name );
+ backtrace( frame->prev );
+ exit( 1 );
+ }
+ return L0;
+}
+
+
+LIST * builtin_has_native_rule( PARSE * parse, FRAME * frame )
+{
+ LIST * module_name = lol_get( frame->args, 0 );
+ LIST * rule_name = lol_get( frame->args, 1 );
+ LIST * version = lol_get( frame->args, 2 );
+
+ module_t * module = bindmodule( module_name->string );
+
+ native_rule_t n;
+ native_rule_t * np = &n;
+ n.name = rule_name->string;
+ if ( module->native_rules && hashcheck( module->native_rules, (HASHDATA * *)&np ) )
+ {
+ int expected_version = atoi( version->string );
+ if ( np->version == expected_version )
+ return list_new( 0, newstr( "true" ) );
+ }
+ return L0;
+}
+
+
+LIST * builtin_user_module( PARSE * parse, FRAME * frame )
+{
+ LIST * module_name = lol_get( frame->args, 0 );
+ for ( ; module_name; module_name = module_name->next )
+ {
+ module_t * m = bindmodule( module_name->string );
+ m->user_module = 1;
+ }
+ return L0;
+}
+
+
+LIST * builtin_nearest_user_location( PARSE * parse, FRAME * frame )
+{
+ FRAME * nearest_user_frame =
+ frame->module->user_module ? frame : frame->prev_user;
+ if ( !nearest_user_frame )
+ return L0;
+
+ {
+ LIST * result = 0;
+ char * file;
+ int line;
+ char buf[32];
+
+ get_source_line( nearest_user_frame->procedure, &file, &line );
+ sprintf( buf, "%d", line );
+ result = list_new( result, newstr( file ) );
+ result = list_new( result, newstr( buf ) );
+ return result;
+ }
+}
+
+
+LIST * builtin_check_if_file( PARSE * parse, FRAME * frame )
+{
+ LIST * name = lol_get( frame->args, 0 );
+ return file_is_file( name->string ) == 1
+ ? list_new( 0, newstr( "true" ) )
+ : L0 ;
+}
+
+
+LIST * builtin_md5( PARSE * parse, FRAME * frame )
+{
+ LIST * l = lol_get( frame->args, 0 );
+ char* s = l->string;
+
+ md5_state_t state;
+ md5_byte_t digest[16];
+ char hex_output[16*2 + 1];
+
+ int di;
+
+ md5_init(&state);
+ md5_append(&state, (const md5_byte_t *)s, strlen(s));
+ md5_finish(&state, digest);
+
+ for (di = 0; di < 16; ++di)
+ sprintf(hex_output + di * 2, "%02x", digest[di]);
+
+ return list_new (0, newstr(hex_output));
+}
+
+LIST *builtin_file_open( PARSE *parse, FRAME *frame )
+{
+ char* name = lol_get(frame->args, 0)->string;
+ char* mode = lol_get(frame->args, 1)->string;
+ int fd;
+ char buffer[sizeof("4294967295")];
+
+ if (strcmp(mode, "w") == 0)
+ {
+ fd = open(name, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+ }
+ else
+ {
+ fd = open(name, O_RDONLY);
+ }
+
+ if (fd != -1)
+ {
+ sprintf(buffer, "%d", fd);
+ return list_new(L0, newstr(buffer));
+ }
+ else
+ {
+ return L0;
+ }
+}
+
+LIST *builtin_pad( PARSE *parse, FRAME *frame )
+{
+ char *string = lol_get(frame->args, 0)->string;
+ char *width_s = lol_get(frame->args, 1)->string;
+
+ int current = strlen (string);
+ int desired = atoi(width_s);
+ if (current >= desired)
+ return list_new (L0, string);
+ else
+ {
+ char *buffer = malloc (desired + 1);
+ int i;
+ LIST *result;
+
+ strcpy (buffer, string);
+ for (i = current; i < desired; ++i)
+ buffer[i] = ' ';
+ buffer[desired] = '\0';
+ result = list_new (L0, newstr (buffer));
+ free (buffer);
+ return result;
+ }
+}
+
+LIST *builtin_precious( PARSE *parse, FRAME *frame )
+{
+ LIST* targets = lol_get(frame->args, 0);
+
+ for ( ; targets; targets = list_next( targets ) )
+ {
+ TARGET* t = bindtarget (targets->string);
+ t->flags |= T_FLAG_PRECIOUS;
+ }
+
+ return L0;
+}
+
+LIST *builtin_self_path( PARSE *parse, FRAME *frame )
+{
+ extern char *saved_argv0;
+ char *p = executable_path (saved_argv0);
+ if (p)
+ {
+ LIST* result = list_new (0, newstr (p));
+ free(p);
+ return result;
+ }
+ else
+ {
+ return L0;
+ }
+}
+
+LIST *builtin_makedir( PARSE *parse, FRAME *frame )
+{
+ LIST *path = lol_get(frame->args, 0);
+
+ if (file_mkdir(path->string) == 0)
+ {
+ LIST *result = list_new (0, newstr(path->string));
+ return result;
+ }
+ else
+ {
+ return L0;
+ }
+}
+
+#ifdef HAVE_PYTHON
+
+LIST * builtin_python_import_rule( PARSE * parse, FRAME * frame )
+{
+ static int first_time = 1;
+ char * python_module = lol_get( frame->args, 0 )->string;
+ char * python_function = lol_get( frame->args, 1 )->string;
+ char * jam_module = lol_get( frame->args, 2 )->string;
+ char * jam_rule = lol_get( frame->args, 3 )->string;
+
+ PyObject * pName;
+ PyObject * pModule;
+ PyObject * pDict;
+ PyObject * pFunc;
+
+ if ( first_time )
+ {
+ /* At the first invocation, we add the value of the global
+ * EXTRA_PYTHONPATH to the sys.path Python variable.
+ */
+ LIST * extra = 0;
+ module_t * outer_module = frame->module;
+
+ first_time = 0;
+
+ if ( outer_module != root_module() )
+ {
+ exit_module( outer_module );
+ enter_module( root_module() );
+ }
+
+ extra = var_get( "EXTRA_PYTHONPATH" );
+
+ if ( outer_module != root_module() )
+ {
+ exit_module( root_module() );
+ enter_module( outer_module );
+ }
+
+ for ( ; extra; extra = extra->next )
+ {
+ string buf[ 1 ];
+ string_new( buf );
+ string_append( buf, "import sys\nsys.path.append(\"" );
+ string_append( buf, extra->string );
+ string_append( buf, "\")\n" );
+ PyRun_SimpleString( buf->value );
+ string_free( buf );
+ }
+ }
+
+ pName = PyString_FromString( python_module );
+ pModule = PyImport_Import( pName );
+ Py_DECREF( pName );
+
+ if ( pModule != NULL )
+ {
+ pDict = PyModule_GetDict( pModule );
+ pFunc = PyDict_GetItemString( pDict, python_function );
+
+ if ( pFunc && PyCallable_Check( pFunc ) )
+ {
+ module_t * m = bindmodule( jam_module );
+ RULE * r = bindrule( jam_rule, m );
+
+ /* Make pFunc owned. */
+ Py_INCREF( pFunc );
+
+ r->python_function = pFunc;
+ }
+ else
+ {
+ if ( PyErr_Occurred() )
+ PyErr_Print();
+ fprintf( stderr, "Cannot find function \"%s\"\n", python_function );
+ }
+ Py_DECREF( pModule );
+ }
+ else
+ {
+ PyErr_Print();
+ fprintf( stderr, "Failed to load \"%s\"\n", python_module );
+ }
+ return L0;
+
+}
+
+#endif
+
+void lol_build( LOL * lol, char * * elements )
+{
+ LIST * l = L0;
+ lol_init( lol );
+
+ while ( elements && *elements )
+ {
+ if ( !strcmp( *elements, ":" ) )
+ {
+ lol_add( lol, l );
+ l = L0 ;
+ }
+ else
+ {
+ l = list_new( l, newstr( *elements ) );
+ }
+ ++elements;
+ }
+
+ if ( l != L0 )
+ lol_add( lol, l );
+}
+
+
+#ifdef HAVE_PYTHON
+
+/*
+ * Calls the bjam rule specified by name passed in 'args'. The name is looked up
+ * in the context of bjam's 'python_interface' module. Returns the list of
+ * string retured by the rule.
+ */
+
+PyObject* bjam_call( PyObject * self, PyObject * args )
+{
+ FRAME inner[ 1 ];
+ LIST * result;
+ PARSE * p;
+ char * rulename;
+
+ /* Build up the list of arg lists. */
+ frame_init( inner );
+ inner->prev = 0;
+ inner->prev_user = 0;
+ inner->module = bindmodule( "python_interface" );
+ inner->procedure = 0;
+
+ /* Extract the rule name and arguments from 'args'. */
+
+ /* PyTuple_GetItem returns borrowed reference. */
+ rulename = PyString_AsString( PyTuple_GetItem( args, 0 ) );
+ {
+ int i = 1;
+ int size = PyTuple_Size( args );
+ for ( ; i < size; ++i )
+ {
+ PyObject * a = PyTuple_GetItem( args, i );
+ if ( PyString_Check( a ) )
+ {
+ lol_add( inner->args, list_new( 0, newstr(
+ PyString_AsString( a ) ) ) );
+ }
+ else if ( PySequence_Check( a ) )
+ {
+ LIST * l = 0;
+ int s = PySequence_Size( a );
+ int i = 0;
+ for ( ; i < s; ++i )
+ {
+ /* PySequence_GetItem returns new reference. */
+ PyObject * e = PySequence_GetItem( a, i );
+ char * s = PyString_AsString( e );
+ if ( !s )
+ {
+ printf( "Invalid parameter type passed from Python\n" );
+ exit( 1 );
+ }
+ l = list_new( l, newstr( s ) );
+ Py_DECREF( e );
+ }
+ lol_add( inner->args, l );
+ }
+ }
+ }
+
+ result = evaluate_rule( rulename, inner );
+
+ frame_free( inner );
+
+ /* Convert the bjam list into a Python list result. */
+ {
+ PyObject * pyResult = PyList_New( list_length( result ) );
+ int i = 0;
+ while ( result )
+ {
+ PyList_SetItem( pyResult, i, PyString_FromString( result->string ) );
+ result = list_next( result );
+ i += 1;
+ }
+ list_free( result );
+ return pyResult;
+ }
+}
+
+
+/*
+ * Accepts four arguments:
+ * - module name
+ * - rule name,
+ * - Python callable.
+ * - (optional) bjam language function signature.
+ * Creates a bjam rule with the specified name in the specified module, which will
+ * invoke the Python callable.
+ */
+
+PyObject * bjam_import_rule( PyObject * self, PyObject * args )
+{
+ char * module;
+ char * rule;
+ PyObject * func;
+ PyObject * bjam_signature = NULL;
+ module_t * m;
+ RULE * r;
+
+ if ( !PyArg_ParseTuple( args, "ssO|O:import_rule",
+ &module, &rule, &func, &bjam_signature ) )
+ return NULL;
+
+ if ( !PyCallable_Check( func ) )
+ {
+ PyErr_SetString( PyExc_RuntimeError,
+ "Non-callable object passed to bjam.import_rule" );
+ return NULL;
+ }
+
+ m = bindmodule( *module ? module : 0 );
+ r = bindrule( rule, m );
+
+ /* Make pFunc owned. */
+ Py_INCREF( func );
+
+ r->python_function = func;
+ r->arguments = 0;
+
+ if (bjam_signature)
+ {
+ argument_list * arg_list = args_new();
+ Py_ssize_t i;
+
+ Py_ssize_t s = PySequence_Size (bjam_signature);
+ for (i = 0; i < s; ++i)
+ {
+ PyObject* v = PySequence_GetItem (bjam_signature, i);
+ lol_add(arg_list->data, list_from_python (v));
+ Py_DECREF(v);
+ }
+ r->arguments = arg_list;
+ }
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+
+/*
+ * Accepts four arguments:
+ * - an action name
+ * - an action body
+ * - a list of variable that will be bound inside the action
+ * - integer flags.
+ * Defines an action on bjam side.
+ */
+
+PyObject * bjam_define_action( PyObject * self, PyObject * args )
+{
+ char * name;
+ char * body;
+ module_t * m;
+ PyObject * bindlist_python;
+ int flags;
+ LIST * bindlist = L0;
+ int n;
+ int i;
+
+ if ( !PyArg_ParseTuple( args, "ssO!i:define_action", &name, &body,
+ &PyList_Type, &bindlist_python, &flags ) )
+ return NULL;
+
+ n = PyList_Size( bindlist_python );
+ for ( i = 0; i < n; ++i )
+ {
+ PyObject * next = PyList_GetItem( bindlist_python, i );
+ if ( !PyString_Check( next ) )
+ {
+ PyErr_SetString( PyExc_RuntimeError,
+ "bind list has non-string type" );
+ return NULL;
+ }
+ bindlist = list_new( bindlist, PyString_AsString( next ) );
+ }
+
+ new_rule_actions( root_module(), name, newstr( body ), bindlist, flags );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+
+/*
+ * Returns the value of a variable in root Jam module.
+ */
+
+PyObject * bjam_variable( PyObject * self, PyObject * args )
+{
+ char * name;
+ LIST * value;
+ PyObject * result;
+ int i;
+
+ if ( !PyArg_ParseTuple( args, "s", &name ) )
+ return NULL;
+
+ enter_module( root_module() );
+ value = var_get( name );
+ exit_module( root_module() );
+
+ result = PyList_New( list_length( value ) );
+ for ( i = 0; value; value = list_next( value ), ++i )
+ PyList_SetItem( result, i, PyString_FromString( value->string ) );
+
+ return result;
+}
+
+
+PyObject * bjam_backtrace( PyObject * self, PyObject * args )
+{
+ PyObject * result = PyList_New( 0 );
+ struct frame * f = frame_before_python_call;
+
+ for ( ; f = f->prev; )
+ {
+ PyObject * tuple = PyTuple_New( 4 );
+ char * file;
+ int line;
+ char buf[ 32 ];
+
+ get_source_line( f->procedure, &file, &line );
+ sprintf( buf, "%d", line );
+
+ /* PyTuple_SetItem steals reference. */
+ PyTuple_SetItem( tuple, 0, PyString_FromString( file ) );
+ PyTuple_SetItem( tuple, 1, PyString_FromString( buf ) );
+ PyTuple_SetItem( tuple, 2, PyString_FromString( f->module->name ) );
+ PyTuple_SetItem( tuple, 3, PyString_FromString( f->rulename ) );
+
+ PyList_Append( result, tuple );
+ Py_DECREF( tuple );
+ }
+ return result;
+}
+
+PyObject * bjam_caller( PyObject * self, PyObject * args )
+{
+ PyObject *result = PyString_FromString(
+ frame_before_python_call->prev->module->name);
+ return result;
+}
+
+#endif /* #ifdef HAVE_PYTHON */
+
+
+#ifdef HAVE_POPEN
+
+#if defined(_MSC_VER) || defined(__BORLANDC__)
+ #define popen windows_popen_wrapper
+ #define pclose _pclose
+
+ /*
+ * This wrapper is a workaround for a funny _popen() feature on Windows
+ * where it eats external quotes in some cases. The bug seems to be related
+ * to the quote stripping functionality used by the Windows cmd.exe
+ * interpreter when its /S is not specified.
+ *
+ * Cleaned up quote from the cmd.exe help screen as displayed on Windows XP
+ * SP3:
+ *
+ * 1. If all of the following conditions are met, then quote characters on
+ * the command line are preserved:
+ *
+ * - no /S switch
+ * - exactly two quote characters
+ * - no special characters between the two quote characters, where
+ * special is one of: &<>()@^|
+ * - there are one or more whitespace characters between the two quote
+ * characters
+ * - the string between the two quote characters is the name of an
+ * executable file.
+ *
+ * 2. Otherwise, old behavior is to see if the first character is a quote
+ * character and if so, strip the leading character and remove the last
+ * quote character on the command line, preserving any text after the
+ * last quote character.
+ *
+ * This causes some commands containing quotes not to be executed correctly.
+ * For example:
+ *
+ * "\Long folder name\aaa.exe" --name="Jurko" --no-surname
+ *
+ * would get its outermost quotes stripped and would be executed as:
+ *
+ * \Long folder name\aaa.exe" --name="Jurko --no-surname
+ *
+ * which would report an error about '\Long' not being a valid command.
+ *
+ * cmd.exe help seems to indicate it would be enough to add an extra space
+ * character in front of the command to avoid this but this does not work,
+ * most likely due to the shell first stripping all leading whitespace
+ * characters from the command.
+ *
+ * Solution implemented here is to quote the whole command in case it
+ * contains any quote characters. Note thought this will not work correctly
+ * should Windows ever 'fix' this feature.
+ * (03.06.2008.) (Jurko)
+ */
+ static FILE * windows_popen_wrapper( char * command, char * mode )
+ {
+ int extra_command_quotes_needed = ( strchr( command, '"' ) != 0 );
+ string quoted_command;
+ FILE * result;
+
+ if ( extra_command_quotes_needed )
+ {
+ string_new( &quoted_command );
+ string_append( &quoted_command, "\"" );
+ string_append( &quoted_command, command );
+ string_append( &quoted_command, "\"" );
+ command = quoted_command.value;
+ }
+
+ result = _popen( command, "r" );
+
+ if ( extra_command_quotes_needed )
+ string_free( &quoted_command );
+
+ return result;
+ }
+#endif
+
+
+static char * rtrim(char *s)
+{
+ char *p = s;
+ while(*p) ++p;
+ for(--p; p >= s && isspace(*p); *p-- = 0);
+ return s;
+}
+
+LIST * builtin_shell( PARSE * parse, FRAME * frame )
+{
+ LIST * command = lol_get( frame->args, 0 );
+ LIST * result = 0;
+ string s;
+ int ret;
+ char buffer[ 1024 ];
+ FILE * p = NULL;
+ int exit_status = -1;
+ int exit_status_opt = 0;
+ int no_output_opt = 0;
+ int strip_eol_opt = 0;
+
+ /* Process the variable args options. */
+ {
+ int a = 1;
+ LIST * arg = lol_get( frame->args, a );
+ while ( arg )
+ {
+ if ( strcmp( "exit-status", arg->string ) == 0 )
+ {
+ exit_status_opt = 1;
+ }
+ else if ( strcmp( "no-output", arg->string ) == 0 )
+ {
+ no_output_opt = 1;
+ }
+ else if ( strcmp("strip-eol", arg->string) == 0 )
+ {
+ strip_eol_opt = 1;
+ }
+ arg = lol_get( frame->args, ++a );
+ }
+ }
+
+ /* The following fflush() call seems to be indicated as a workaround for a
+ * popen() bug on POSIX implementations related to synhronizing input
+ * stream positions for the called and the calling process.
+ */
+ fflush( NULL );
+
+ p = popen( command->string, "r" );
+ if ( p == NULL )
+ return L0;
+
+ string_new( &s );
+
+ while ( ( ret = fread( buffer, sizeof( char ), sizeof( buffer ) - 1, p ) ) > 0 )
+ {
+ buffer[ret] = 0;
+ if ( !no_output_opt )
+ {
+ if ( strip_eol_opt )
+ rtrim(buffer);
+ string_append( &s, buffer );
+ }
+ }
+
+ exit_status = pclose( p );
+
+ /* The command output is returned first. */
+ result = list_new( L0, newstr( s.value ) );
+ string_free( &s );
+
+ /* The command exit result next. */
+ if ( exit_status_opt )
+ {
+ if ( WIFEXITED(exit_status) )
+ exit_status = WEXITSTATUS(exit_status);
+ else
+ exit_status = -1;
+ sprintf( buffer, "%d", exit_status );
+ result = list_new( result, newstr( buffer ) );
+ }
+
+ return result;
+}
+
+#else /* #ifdef HAVE_POPEN */
+
+LIST * builtin_shell( PARSE * parse, FRAME * frame )
+{
+ return L0;
+}
+
+#endif /* #ifdef HAVE_POPEN */
diff --git a/jam-files/engine/builtins.h b/jam-files/engine/builtins.h
new file mode 100644
index 00000000..5fed07c9
--- /dev/null
+++ b/jam-files/engine/builtins.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+#ifndef JAM_BUILTINS_H
+# define JAM_BUILTINS_H
+
+# include "frames.h"
+
+/*
+ * builtins.h - compile parsed jam statements
+ */
+
+void load_builtins();
+void init_set();
+void init_path();
+void init_regex();
+void init_property_set();
+void init_sequence();
+void init_order();
+
+LIST *builtin_calc( PARSE *parse, FRAME *args );
+LIST *builtin_depends( PARSE *parse, FRAME *args );
+LIST *builtin_rebuilds( PARSE *parse, FRAME *args );
+LIST *builtin_echo( PARSE *parse, FRAME *args );
+LIST *builtin_exit( PARSE *parse, FRAME *args );
+LIST *builtin_flags( PARSE *parse, FRAME *args );
+LIST *builtin_glob( PARSE *parse, FRAME *args );
+LIST *builtin_glob_recursive( PARSE *parse, FRAME *frame );
+LIST *builtin_subst( PARSE *parse, FRAME *args );
+LIST *builtin_match( PARSE *parse, FRAME *args );
+LIST *builtin_split_by_characters( PARSE *parse, FRAME *args );
+LIST *builtin_hdrmacro( PARSE *parse, FRAME *args );
+LIST *builtin_rulenames( PARSE *parse, FRAME *args );
+LIST *builtin_varnames( PARSE *parse, FRAME *args );
+LIST *builtin_delete_module( PARSE *parse, FRAME *args );
+LIST *builtin_import( PARSE *parse, FRAME *args );
+LIST *builtin_export( PARSE *parse, FRAME *args );
+LIST *builtin_caller_module( PARSE *parse, FRAME *args );
+LIST *builtin_backtrace( PARSE *parse, FRAME *args );
+LIST *builtin_pwd( PARSE *parse, FRAME *args );
+LIST *builtin_update( PARSE *parse, FRAME *args );
+LIST *builtin_update_now( PARSE *parse, FRAME *args );
+LIST *builtin_search_for_target( PARSE *parse, FRAME *args );
+LIST *builtin_import_module( PARSE *parse, FRAME *args );
+LIST *builtin_imported_modules( PARSE *parse, FRAME *frame );
+LIST *builtin_instance( PARSE *parse, FRAME *frame );
+LIST *builtin_sort( PARSE *parse, FRAME *frame );
+LIST *builtin_normalize_path( PARSE *parse, FRAME *frame );
+LIST *builtin_native_rule( PARSE *parse, FRAME *frame );
+LIST *builtin_has_native_rule( PARSE *parse, FRAME *frame );
+LIST *builtin_user_module( PARSE *parse, FRAME *frame );
+LIST *builtin_nearest_user_location( PARSE *parse, FRAME *frame );
+LIST *builtin_check_if_file( PARSE *parse, FRAME *frame );
+LIST *builtin_python_import_rule( PARSE *parse, FRAME *frame );
+LIST *builtin_shell( PARSE *parse, FRAME *frame );
+LIST *builtin_md5( PARSE *parse, FRAME *frame );
+LIST *builtin_file_open( PARSE *parse, FRAME *frame );
+LIST *builtin_pad( PARSE *parse, FRAME *frame );
+LIST *builtin_precious( PARSE *parse, FRAME *frame );
+LIST *builtin_self_path( PARSE *parse, FRAME *frame );
+LIST *builtin_makedir( PARSE *parse, FRAME *frame );
+
+void backtrace( FRAME *frame );
+extern int last_update_now_status;
+
+#endif
diff --git a/jam-files/engine/bump_version.py b/jam-files/engine/bump_version.py
new file mode 100644
index 00000000..9423c4c7
--- /dev/null
+++ b/jam-files/engine/bump_version.py
@@ -0,0 +1,80 @@
+#!/usr/bin/python
+
+# This script is used to bump version of bjam. It takes a single argument, e.g
+#
+# ./bump_version.py 3.1.9
+#
+# and updates all necessary files. For the time being, it's assumes presense
+# of 'perl' executable and Debian-specific 'dch' executable.
+#
+
+
+import os
+import os.path
+import re
+import string
+import sys
+
+srcdir = os.path.abspath(os.path.dirname(__file__ ))
+docdir = os.path.abspath(os.path.join(srcdir,"..","doc"))
+
+def edit(file,replacements):
+ print " '%s'..." %(file)
+ text = open(file,'r').read()
+ while len(replacements) > 0:
+ #~ print " '%s' ==> '%s'" % (replacements[0],replacements[1])
+ text = re.compile(replacements[0],re.M).subn(replacements[1],text)[0]
+ replacements = replacements[2:]
+ #~ print text
+ open(file,'w').write(text)
+
+def make_edits(version):
+ edit(os.path.join(srcdir,"boost-jam.spec"), [
+ '^Version:.*$','Version: %s' % string.join(version, "."),
+ ])
+
+ edit(os.path.join(srcdir,"build.jam"), [
+ '^_VERSION_ = .* ;$','_VERSION_ = %s %s %s ;' % (version[0], version[1], version[2]),
+ ])
+
+ edit(os.path.join(docdir,"bjam.qbk"), [
+ '\[version.*\]','[version: %s]' % string.join(version, '.'),
+ '\[def :version:.*\]','[def :version: %s]' % string.join(version, '.'),
+ ])
+
+ edit(os.path.join(srcdir,"patchlevel.h"), [
+ '^#define VERSION_MAJOR .*$',
+ '#define VERSION_MAJOR %s' % (version[0]),
+ '^#define VERSION_MINOR .*$',
+ '#define VERSION_MINOR %s' % (version[1]),
+ '^#define VERSION_PATCH .*$',
+ '#define VERSION_PATCH %s' % (version[2]),
+ '^#define VERSION_MAJOR_SYM .*$',
+ '#define VERSION_MAJOR_SYM "0%s"' % (version[0]),
+ '^#define VERSION_MINOR_SYM .*$',
+ '#define VERSION_MINOR_SYM "%s"' % (version[1]),
+ '^#define VERSION_PATCH_SYM .*$',
+ '#define VERSION_PATCH_SYM "%s"' % (version[2]),
+ '^#define VERSION .*$',
+ '#define VERSION "%s"' % string.join(version, '.'),
+ '^#define JAMVERSYM .*$',
+ '#define JAMVERSYM "JAMVERSION=%s.%s"' % (version[0],version[1]),
+ ])
+
+def main():
+
+ if len(sys.argv) < 2:
+ print "Expect new version as argument"
+ sys.exit(1)
+
+ version = string.split(sys.argv[1], ".")
+ print "Setting version to", version
+ make_edits(version)
+
+if __name__ == '__main__':
+ main()
+
+#~ Copyright 2006 Rene Rivera.
+#~ Copyright 2005-2006 Vladimir Prus.
+#~ Distributed under the Boost Software License, Version 1.0.
+#~ (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
diff --git a/jam-files/engine/class.c b/jam-files/engine/class.c
new file mode 100644
index 00000000..ff4ec568
--- /dev/null
+++ b/jam-files/engine/class.c
@@ -0,0 +1,141 @@
+/* Copyright Vladimir Prus 2003. Distributed under the Boost */
+/* Software License, Version 1.0. (See accompanying */
+/* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */
+
+#include "class.h"
+#include "strings.h"
+#include "variable.h"
+#include "frames.h"
+#include "rules.h"
+#include "newstr.h"
+
+#include "hash.h"
+
+
+static struct hash * classes = 0;
+
+
+static void check_defined( LIST * class_names )
+{
+ for ( ; class_names; class_names = class_names->next )
+ {
+ char * * p = &class_names->string;
+ if ( !hashcheck( classes, (HASHDATA * *)&p ) )
+ {
+ printf( "Class %s is not defined\n", class_names->string );
+ abort();
+ }
+ }
+}
+
+
+static char * class_module_name( char * declared_name )
+{
+ string name[ 1 ];
+ char * result;
+
+ string_new( name );
+ string_append( name, "class@" );
+ string_append( name, declared_name );
+
+ result = newstr( name->value );
+ string_free( name );
+
+ return result;
+}
+
+
+struct import_base_data
+{
+ char * base_name;
+ module_t * base_module;
+ module_t * class_module;
+};
+
+
+static void import_base_rule( void * r_, void * d_ )
+{
+ RULE * r = (RULE *)r_;
+ RULE * ir1;
+ RULE * ir2;
+ struct import_base_data * d = (struct import_base_data *)d_;
+ string qualified_name[ 1 ];
+
+ string_new ( qualified_name );
+ string_append ( qualified_name, d->base_name );
+ string_push_back( qualified_name, '.' );
+ string_append ( qualified_name, r->name );
+
+ ir1 = import_rule( r, d->class_module, r->name );
+ ir2 = import_rule( r, d->class_module, qualified_name->value );
+
+ /* Copy 'exported' flag. */
+ ir1->exported = ir2->exported = r->exported;
+
+ /* If we are importing a class method, localize it. */
+ if ( ( r->module == d->base_module ) || ( r->module->class_module &&
+ ( r->module->class_module == d->base_module ) ) )
+ ir1->module = ir2->module = d->class_module;
+
+ string_free( qualified_name );
+}
+
+
+/*
+ * For each exported rule 'n', declared in class module for base, imports that
+ * rule in 'class' as 'n' and as 'base.n'. Imported rules are localized and
+ * marked as exported.
+ */
+
+static void import_base_rules( module_t * class, char * base )
+{
+ module_t * base_module = bindmodule( class_module_name( base ) );
+ struct import_base_data d;
+ d.base_name = base;
+ d.base_module = base_module;
+ d.class_module = class;
+
+ if ( base_module->rules )
+ hashenumerate( base_module->rules, import_base_rule, &d );
+
+ import_module( imported_modules( base_module ), class );
+}
+
+
+char * make_class_module( LIST * xname, LIST * bases, FRAME * frame )
+{
+ char * name = class_module_name( xname->string );
+ char * * pp = &xname->string;
+ module_t * class_module = 0;
+ module_t * outer_module = frame->module;
+
+ if ( !classes )
+ classes = hashinit( sizeof( char * ), "classes" );
+
+ if ( hashcheck( classes, (HASHDATA * *)&pp ) )
+ {
+ printf( "Class %s already defined\n", xname->string );
+ abort();
+ }
+ else
+ {
+ hashenter( classes, (HASHDATA * *)&pp );
+ }
+ check_defined( bases );
+
+ class_module = bindmodule( name );
+
+ exit_module( outer_module );
+ enter_module( class_module );
+
+ var_set( "__name__", xname, VAR_SET );
+ var_set( "__bases__", bases, VAR_SET );
+
+ exit_module( class_module );
+ enter_module( outer_module );
+
+ for ( ; bases; bases = bases->next )
+ import_base_rules( class_module, bases->string );
+
+ return name;
+}
diff --git a/jam-files/engine/class.h b/jam-files/engine/class.h
new file mode 100644
index 00000000..f7faeff6
--- /dev/null
+++ b/jam-files/engine/class.h
@@ -0,0 +1,13 @@
+/* Copyright Vladimir Prus 2003. Distributed under the Boost */
+/* Software License, Version 1.0. (See accompanying */
+/* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */
+
+#ifndef CLASS_H_VP_2003_08_01
+#define CLASS_H_VP_2003_08_01
+
+#include "lists.h"
+#include "frames.h"
+
+char* make_class_module(LIST* xname, LIST* bases, FRAME* frame);
+
+#endif
diff --git a/jam-files/engine/command.c b/jam-files/engine/command.c
new file mode 100644
index 00000000..d2ea0681
--- /dev/null
+++ b/jam-files/engine/command.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/* This file is ALSO:
+ * Copyright 2001-2004 David Abrahams.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+/*
+ * command.c - maintain lists of commands
+ */
+
+#include "jam.h"
+
+#include "lists.h"
+#include "parse.h"
+#include "variable.h"
+#include "rules.h"
+
+#include "command.h"
+#include <limits.h>
+#include <string.h>
+
+
+/*
+ * cmd_new() - return a new CMD or 0 if too many args
+ */
+
+CMD * cmd_new( RULE * rule, LIST * targets, LIST * sources, LIST * shell )
+{
+ CMD * cmd = (CMD *)BJAM_MALLOC( sizeof( CMD ) );
+ /* Lift line-length limitation entirely when JAMSHELL is just "%". */
+ int no_limit = ( shell && !strcmp(shell->string,"%") && !list_next(shell) );
+ int max_line = MAXLINE;
+ int allocated = -1;
+
+ cmd->rule = rule;
+ cmd->shell = shell;
+ cmd->next = 0;
+
+ lol_init( &cmd->args );
+ lol_add( &cmd->args, targets );
+ lol_add( &cmd->args, sources );
+ cmd->buf = 0;
+
+ do
+ {
+ BJAM_FREE( cmd->buf ); /* free any buffer from previous iteration */
+
+ cmd->buf = (char*)BJAM_MALLOC_ATOMIC( max_line + 1 );
+
+ if ( cmd->buf == 0 )
+ break;
+
+ allocated = var_string( rule->actions->command, cmd->buf, max_line, &cmd->args );
+
+ max_line = max_line * 2;
+ }
+ while ( ( allocated < 0 ) && ( max_line < INT_MAX / 2 ) );
+
+ if ( !no_limit )
+ {
+ /* Bail if the result will not fit in MAXLINE. */
+ char * s = cmd->buf;
+ while ( *s )
+ {
+ size_t l = strcspn( s, "\n" );
+
+ if ( l > MAXLINE )
+ {
+ /* We do not free targets/sources/shell if bailing. */
+ cmd_free( cmd );
+ return 0;
+ }
+
+ s += l;
+ if ( *s )
+ ++s;
+ }
+ }
+
+ return cmd;
+}
+
+
+/*
+ * cmd_free() - free a CMD
+ */
+
+void cmd_free( CMD * cmd )
+{
+ lol_free( &cmd->args );
+ list_free( cmd->shell );
+ BJAM_FREE( cmd->buf );
+ BJAM_FREE( (char *)cmd );
+}
diff --git a/jam-files/engine/command.h b/jam-files/engine/command.h
new file mode 100644
index 00000000..ddd38e68
--- /dev/null
+++ b/jam-files/engine/command.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 1994 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * command.h - the CMD structure and routines to manipulate them
+ *
+ * Both ACTION and CMD contain a rule, targets, and sources. An
+ * ACTION describes a rule to be applied to the given targets and
+ * sources; a CMD is what actually gets executed by the shell. The
+ * differences are due to:
+ *
+ * ACTIONS must be combined if 'actions together' is given.
+ * ACTIONS must be split if 'actions piecemeal' is given.
+ * ACTIONS must have current sources omitted for 'actions updated'.
+ *
+ * The CMD datatype holds a single command that is to be executed
+ * against a target, and they can chain together to represent the
+ * full collection of commands used to update a target.
+ *
+ * Structures:
+ *
+ * CMD - an action, ready to be formatted into a buffer and executed.
+ *
+ * External routines:
+ *
+ * cmd_new() - return a new CMD or 0 if too many args.
+ * cmd_free() - delete CMD and its parts.
+ * cmd_next() - walk the CMD chain.
+ */
+
+
+/*
+ * CMD - an action, ready to be formatted into a buffer and executed.
+ */
+
+typedef struct _cmd CMD;
+
+struct _cmd
+{
+ CMD * next;
+ CMD * tail; /* valid on in head */
+ RULE * rule; /* rule->actions contains shell script */
+ LIST * shell; /* $(SHELL) value */
+ LOL args; /* LISTs for $(<), $(>) */
+ char * buf; /* actual commands */
+};
+
+CMD * cmd_new
+(
+ RULE * rule, /* rule (referenced) */
+ LIST * targets, /* $(<) (freed) */
+ LIST * sources, /* $(>) (freed) */
+ LIST * shell /* $(SHELL) (freed) */
+);
+
+void cmd_free( CMD * );
+
+#define cmd_next( c ) ( ( c )->next )
diff --git a/jam-files/engine/compile.c b/jam-files/engine/compile.c
new file mode 100644
index 00000000..2c049aae
--- /dev/null
+++ b/jam-files/engine/compile.c
@@ -0,0 +1,1424 @@
+/*
+ * Copyright 1993, 2000 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/* This file is ALSO:
+ * Copyright 2001-2004 David Abrahams.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+# include "jam.h"
+
+# include "lists.h"
+# include "parse.h"
+# include "compile.h"
+# include "variable.h"
+# include "expand.h"
+# include "rules.h"
+# include "newstr.h"
+# include "make.h"
+# include "search.h"
+# include "hdrmacro.h"
+# include "hash.h"
+# include "modules.h"
+# include "strings.h"
+# include "builtins.h"
+# include "class.h"
+
+# include <assert.h>
+# include <string.h>
+# include <stdarg.h>
+
+/*
+ * compile.c - compile parsed jam statements
+ *
+ * External routines:
+ *
+ * compile_append() - append list results of two statements
+ * compile_eval() - evaluate if to determine which leg to compile
+ * compile_foreach() - compile the "for x in y" statement
+ * compile_if() - compile 'if' rule
+ * compile_while() - compile 'while' rule
+ * compile_include() - support for 'include' - call include() on file
+ * compile_list() - expand and return a list
+ * compile_local() - declare (and set) local variables
+ * compile_null() - do nothing -- a stub for parsing
+ * compile_on() - run rule under influence of on-target variables
+ * compile_rule() - compile a single user defined rule
+ * compile_rules() - compile a chain of rules
+ * compile_set() - compile the "set variable" statement
+ * compile_setcomp() - support for `rule` - save parse tree
+ * compile_setexec() - support for `actions` - save execution string
+ * compile_settings() - compile the "on =" (set variable on exec) statement
+ * compile_switch() - compile 'switch' rule
+ *
+ * Internal routines:
+ *
+ * debug_compile() - printf with indent to show rule expansion.
+ * evaluate_rule() - execute a rule invocation
+ *
+ * builtin_depends() - DEPENDS/INCLUDES rule
+ * builtin_echo() - ECHO rule
+ * builtin_exit() - EXIT rule
+ * builtin_flags() - NOCARE, NOTFILE, TEMPORARY rule
+ *
+ * 02/03/94 (seiwald) - Changed trace output to read "setting" instead of
+ * the awkward sounding "settings".
+ * 04/12/94 (seiwald) - Combined build_depends() with build_includes().
+ * 04/12/94 (seiwald) - actionlist() now just appends a single action.
+ * 04/13/94 (seiwald) - added shorthand L0 for null list pointer
+ * 05/13/94 (seiwald) - include files are now bound as targets, and thus
+ * can make use of $(SEARCH)
+ * 06/01/94 (seiwald) - new 'actions existing' does existing sources
+ * 08/23/94 (seiwald) - Support for '+=' (append to variable)
+ * 12/20/94 (seiwald) - NOTIME renamed NOTFILE.
+ * 01/22/95 (seiwald) - Exit rule.
+ * 02/02/95 (seiwald) - Always rule; LEAVES rule.
+ * 02/14/95 (seiwald) - NoUpdate rule.
+ * 09/11/00 (seiwald) - new evaluate_rule() for headers().
+ * 09/11/00 (seiwald) - compile_xxx() now return LIST *.
+ * New compile_append() and compile_list() in
+ * support of building lists here, rather than
+ * in jamgram.yy.
+ * 01/10/00 (seiwald) - built-ins split out to builtin.c.
+ */
+
+static void debug_compile( int which, char *s, FRAME* frame );
+int glob( char *s, char *c );
+/* Internal functions from builtins.c */
+void backtrace( FRAME *frame );
+void backtrace_line( FRAME *frame );
+void print_source_line( PARSE* p );
+
+struct frame * frame_before_python_call;
+
+void frame_init( FRAME* frame )
+{
+ frame->prev = 0;
+ frame->prev_user = 0;
+ lol_init(frame->args);
+ frame->module = root_module();
+ frame->rulename = "module scope";
+ frame->procedure = 0;
+}
+
+
+void frame_free( FRAME* frame )
+{
+ lol_free( frame->args );
+}
+
+
+/*
+ * compile_append() - append list results of two statements
+ *
+ * parse->left more compile_append() by left-recursion
+ * parse->right single rule
+ */
+
+LIST * compile_append( PARSE * parse, FRAME * frame )
+{
+ /* Append right to left. */
+ return list_append(
+ parse_evaluate( parse->left, frame ),
+ parse_evaluate( parse->right, frame ) );
+}
+
+
+/*
+ * compile_eval() - evaluate if to determine which leg to compile
+ *
+ * Returns:
+ * list if expression true - compile 'then' clause
+ * L0 if expression false - compile 'else' clause
+ */
+
+static int lcmp( LIST * t, LIST * s )
+{
+ int status = 0;
+
+ while ( !status && ( t || s ) )
+ {
+ char *st = t ? t->string : "";
+ char *ss = s ? s->string : "";
+
+ status = strcmp( st, ss );
+
+ t = t ? list_next( t ) : t;
+ s = s ? list_next( s ) : s;
+ }
+
+ return status;
+}
+
+LIST * compile_eval( PARSE * parse, FRAME * frame )
+{
+ LIST * ll;
+ LIST * lr;
+ LIST * s;
+ LIST * t;
+ int status = 0;
+
+ /* Short circuit lr eval for &&, ||, and 'in'. */
+
+ ll = parse_evaluate( parse->left, frame );
+ lr = 0;
+
+ switch ( parse->num )
+ {
+ case EXPR_AND:
+ case EXPR_IN : if ( ll ) goto eval; break;
+ case EXPR_OR : if ( !ll ) goto eval; break;
+ default: eval: lr = parse_evaluate( parse->right, frame );
+ }
+
+ /* Now eval. */
+ switch ( parse->num )
+ {
+ case EXPR_NOT: if ( !ll ) status = 1; break;
+ case EXPR_AND: if ( ll && lr ) status = 1; break;
+ case EXPR_OR : if ( ll || lr ) status = 1; break;
+
+ case EXPR_IN:
+ /* "a in b": make sure each of ll is equal to something in lr. */
+ for ( t = ll; t; t = list_next( t ) )
+ {
+ for ( s = lr; s; s = list_next( s ) )
+ if ( !strcmp( t->string, s->string ) )
+ break;
+ if ( !s ) break;
+ }
+ /* No more ll? Success. */
+ if ( !t ) status = 1;
+ break;
+
+ case EXPR_EXISTS: if ( lcmp( ll, L0 ) != 0 ) status = 1; break;
+ case EXPR_EQUALS: if ( lcmp( ll, lr ) == 0 ) status = 1; break;
+ case EXPR_NOTEQ : if ( lcmp( ll, lr ) != 0 ) status = 1; break;
+ case EXPR_LESS : if ( lcmp( ll, lr ) < 0 ) status = 1; break;
+ case EXPR_LESSEQ: if ( lcmp( ll, lr ) <= 0 ) status = 1; break;
+ case EXPR_MORE : if ( lcmp( ll, lr ) > 0 ) status = 1; break;
+ case EXPR_MOREEQ: if ( lcmp( ll, lr ) >= 0 ) status = 1; break;
+ }
+
+ if ( DEBUG_IF )
+ {
+ debug_compile( 0, "if", frame );
+ list_print( ll );
+ printf( "(%d) ", status );
+ list_print( lr );
+ printf( "\n" );
+ }
+
+ /* Find something to return. */
+ /* In odd circumstances (like "" = "") */
+ /* we'll have to return a new string. */
+
+ if ( !status ) t = 0;
+ else if ( ll ) t = ll, ll = 0;
+ else if ( lr ) t = lr, lr = 0;
+ else t = list_new( L0, newstr( "1" ) );
+
+ if ( ll ) list_free( ll );
+ if ( lr ) list_free( lr );
+ return t;
+}
+
+
+/*
+ * compile_foreach() - compile the "for x in y" statement
+ *
+ * Compile_foreach() resets the given variable name to each specified
+ * value, executing the commands enclosed in braces for each iteration.
+ *
+ * parse->string index variable
+ * parse->left variable values
+ * parse->right rule to compile
+ */
+
+LIST * compile_foreach( PARSE * parse, FRAME * frame )
+{
+ LIST * nv = parse_evaluate( parse->left, frame );
+ LIST * l;
+ SETTINGS * s = 0;
+
+ if ( parse->num )
+ {
+ s = addsettings( s, VAR_SET, parse->string, L0 );
+ pushsettings( s );
+ }
+
+ /* Call var_set to reset $(parse->string) for each val. */
+
+ for ( l = nv; l; l = list_next( l ) )
+ {
+ LIST * val = list_new( L0, copystr( l->string ) );
+ var_set( parse->string, val, VAR_SET );
+ list_free( parse_evaluate( parse->right, frame ) );
+ }
+
+ if ( parse->num )
+ {
+ popsettings( s );
+ freesettings( s );
+ }
+
+ list_free( nv );
+
+ return L0;
+}
+
+/*
+ * compile_if() - compile 'if' rule
+ *
+ * parse->left condition tree
+ * parse->right then tree
+ * parse->third else tree
+ */
+
+LIST * compile_if( PARSE * p, FRAME * frame )
+{
+ LIST * l = parse_evaluate( p->left, frame );
+ if ( l )
+ {
+ list_free( l );
+ return parse_evaluate( p->right, frame );
+ }
+ return parse_evaluate( p->third, frame );
+}
+
+
+LIST * compile_while( PARSE * p, FRAME * frame )
+{
+ LIST * r = 0;
+ LIST * l;
+ while ( ( l = parse_evaluate( p->left, frame ) ) )
+ {
+ list_free( l );
+ if ( r ) list_free( r );
+ r = parse_evaluate( p->right, frame );
+ }
+ return r;
+}
+
+
+/*
+ * compile_include() - support for 'include' - call include() on file
+ *
+ * parse->left list of files to include (can only do 1)
+ */
+
+LIST * compile_include( PARSE * parse, FRAME * frame )
+{
+ LIST * nt = parse_evaluate( parse->left, frame );
+
+ if ( DEBUG_COMPILE )
+ {
+ debug_compile( 0, "include", frame);
+ list_print( nt );
+ printf( "\n" );
+ }
+
+ if ( nt )
+ {
+ TARGET * t = bindtarget( nt->string );
+
+ /* DWA 2001/10/22 - Perforce Jam cleared the arguments here, which
+ * prevents an included file from being treated as part of the body of a
+ * rule. I did not see any reason to do that, so I lifted the
+ * restriction.
+ */
+
+ /* Bind the include file under the influence of */
+ /* "on-target" variables. Though they are targets, */
+ /* include files are not built with make(). */
+
+ pushsettings( t->settings );
+ /* We don't expect that file to be included is generated by some
+ action. Therefore, pass 0 as third argument.
+ If the name resolves to directory, let it error out. */
+ t->boundname = search( t->name, &t->time, 0, 0 );
+ popsettings( t->settings );
+
+ parse_file( t->boundname, frame );
+ }
+
+ list_free( nt );
+
+ return L0;
+}
+
+static LIST* evaluate_in_module ( char* module_name, PARSE * p, FRAME* frame)
+{
+ LIST* result;
+
+ module_t* outer_module = frame->module;
+ frame->module = module_name ? bindmodule( module_name ) : root_module();
+
+ if ( outer_module != frame->module )
+ {
+ exit_module( outer_module );
+ enter_module( frame->module );
+ }
+
+ result = parse_evaluate( p, frame );
+
+ if ( outer_module != frame->module )
+ {
+ exit_module( frame->module );
+ enter_module( outer_module );
+ frame->module = outer_module;
+ }
+
+ return result;
+}
+
+
+LIST * compile_module( PARSE * p, FRAME * frame )
+{
+ /* Here we are entering a module declaration block. */
+ LIST * module_name = parse_evaluate( p->left, frame );
+ LIST * result = evaluate_in_module( module_name ? module_name->string : 0,
+ p->right, frame );
+ list_free( module_name );
+ return result;
+}
+
+
+LIST * compile_class( PARSE * p, FRAME * frame )
+{
+ /** Todo: check for empty class name.
+ Check for class redeclaration. */
+
+ char * class_module = 0;
+
+ LIST * name = parse_evaluate( p->left->right, frame );
+ LIST * bases = 0;
+
+ if ( p->left->left )
+ bases = parse_evaluate( p->left->left->right, frame );
+
+ class_module = make_class_module( name, bases, frame );
+ evaluate_in_module( class_module, p->right, frame );
+
+ return L0;
+}
+
+
+/*
+ * compile_list() - expand and return a list.
+ *
+ * parse->string - character string to expand.
+ */
+
+LIST * compile_list( PARSE * parse, FRAME * frame )
+{
+ /* s is a copyable string */
+ char * s = parse->string;
+ return var_expand( L0, s, s + strlen( s ), frame->args, 1 );
+}
+
+
+/*
+ * compile_local() - declare (and set) local variables.
+ *
+ * parse->left list of variables
+ * parse->right list of values
+ * parse->third rules to execute
+ */
+
+LIST * compile_local( PARSE * parse, FRAME * frame )
+{
+ LIST * l;
+ SETTINGS * s = 0;
+ LIST * nt = parse_evaluate( parse->left, frame );
+ LIST * ns = parse_evaluate( parse->right, frame );
+ LIST * result;
+
+ if ( DEBUG_COMPILE )
+ {
+ debug_compile( 0, "local", frame );
+ list_print( nt );
+ printf( " = " );
+ list_print( ns );
+ printf( "\n" );
+ }
+
+ /* Initial value is ns. */
+ for ( l = nt; l; l = list_next( l ) )
+ s = addsettings( s, VAR_SET, l->string, list_copy( (LIST *)0, ns ) );
+
+ list_free( ns );
+ list_free( nt );
+
+ /* Note that callees of the current context get this "local" variable,
+ * making it not so much local as layered.
+ */
+
+ pushsettings( s );
+ result = parse_evaluate( parse->third, frame );
+ popsettings( s );
+
+ freesettings( s );
+
+ return result;
+}
+
+
+/*
+ * compile_null() - do nothing -- a stub for parsing.
+ */
+
+LIST * compile_null( PARSE * parse, FRAME * frame )
+{
+ return L0;
+}
+
+
+/*
+ * compile_on() - run rule under influence of on-target variables
+ *
+ * parse->left list of files to include (can only do 1).
+ * parse->right rule to run.
+ *
+ * EXPERIMENTAL!
+ */
+
+LIST * compile_on( PARSE * parse, FRAME * frame )
+{
+ LIST * nt = parse_evaluate( parse->left, frame );
+ LIST * result = 0;
+
+ if ( DEBUG_COMPILE )
+ {
+ debug_compile( 0, "on", frame );
+ list_print( nt );
+ printf( "\n" );
+ }
+
+ if ( nt )
+ {
+ TARGET * t = bindtarget( nt->string );
+ pushsettings( t->settings );
+ result = parse_evaluate( parse->right, frame );
+ popsettings( t->settings );
+ }
+
+ list_free( nt );
+
+ return result;
+}
+
+
+/*
+ * compile_rule() - compile a single user defined rule.
+ *
+ * parse->string name of user defined rule.
+ * parse->left parameters (list of lists) to rule, recursing left.
+ *
+ * Wrapped around evaluate_rule() so that headers() can share it.
+ */
+
+LIST * compile_rule( PARSE * parse, FRAME * frame )
+{
+ FRAME inner[ 1 ];
+ LIST * result;
+ PARSE * p;
+
+ /* Build up the list of arg lists. */
+ frame_init( inner );
+ inner->prev = frame;
+ inner->prev_user = frame->module->user_module ? frame : frame->prev_user;
+ inner->module = frame->module; /* This gets fixed up in evaluate_rule(), below. */
+ inner->procedure = parse;
+ /* Special-case LOL of length 1 where the first list is totally empty.
+ This is created when calling functions with no parameters, due to
+ the way jam grammar is written. This is OK when one jam function
+ calls another, but really not good when Jam function calls Python. */
+ if ( parse->left->left == NULL && parse->left->right->func == compile_null)
+ ;
+ else
+ for ( p = parse->left; p; p = p->left )
+ lol_add( inner->args, parse_evaluate( p->right, frame ) );
+
+ /* And invoke the rule. */
+ result = evaluate_rule( parse->string, inner );
+ frame_free( inner );
+ return result;
+}
+
+
+static void argument_error( char * message, RULE * rule, FRAME * frame, LIST* arg )
+{
+ LOL * actual = frame->args;
+ assert( frame->procedure != 0 );
+ backtrace_line( frame->prev );
+ printf( "*** argument error\n* rule %s ( ", frame->rulename );
+ lol_print( rule->arguments->data );
+ printf( " )\n* called with: ( " );
+ lol_print( actual );
+ printf( " )\n* %s %s\n", message, arg ? arg->string : "" );
+ print_source_line( rule->procedure );
+ printf( "see definition of rule '%s' being called\n", rule->name );
+ backtrace( frame->prev );
+ exit( 1 );
+}
+
+
+/* Define delimiters for type check elements in argument lists (and return type
+ * specifications, eventually).
+ */
+# define TYPE_OPEN_DELIM '['
+# define TYPE_CLOSE_DELIM ']'
+
+/*
+ * is_type_name() - true iff the given string represents a type check
+ * specification.
+ */
+
+static int is_type_name( char * s )
+{
+ return ( s[ 0 ] == TYPE_OPEN_DELIM ) &&
+ ( s[ strlen( s ) - 1 ] == TYPE_CLOSE_DELIM );
+}
+
+
+/*
+ * arg_modifier - if the next element of formal is a single character, return
+ * that; return 0 otherwise. Used to extract "*+?" modifiers * from argument
+ * lists.
+ */
+
+static char arg_modifier( LIST * formal )
+{
+ if ( formal->next )
+ {
+ char * next = formal->next->string;
+ if ( next && ( next[ 0 ] != 0 ) && ( next[ 1 ] == 0 ) )
+ return next[ 0 ];
+ }
+ return 0;
+}
+
+
+/*
+ * type_check() - checks that each element of values satisfies the requirements
+ * of type_name.
+ *
+ * caller - the frame of the rule calling the rule whose arguments are
+ * being checked
+ *
+ * called - the rule being called
+ *
+ * arg_name - a list element containing the name of the argument being
+ * checked
+ */
+
+static void type_check
+(
+ char * type_name,
+ LIST * values,
+ FRAME * caller,
+ RULE * called,
+ LIST * arg_name
+)
+{
+ static module_t * typecheck = 0;
+
+ /* If nothing to check, bail now. */
+ if ( !values || !type_name )
+ return;
+
+ if ( !typecheck )
+ typecheck = bindmodule( ".typecheck" );
+
+ /* If the checking rule can not be found, also bail. */
+ {
+ RULE checker_, *checker = &checker_;
+
+ checker->name = type_name;
+ if ( !typecheck->rules || !hashcheck( typecheck->rules, (HASHDATA * *)&checker ) )
+ return;
+ }
+
+ exit_module( caller->module );
+
+ while ( values != 0 )
+ {
+ LIST *error;
+ FRAME frame[1];
+ frame_init( frame );
+ frame->module = typecheck;
+ frame->prev = caller;
+ frame->prev_user = caller->module->user_module ? caller : caller->prev_user;
+
+ enter_module( typecheck );
+ /* Prepare the argument list */
+ lol_add( frame->args, list_new( L0, values->string ) );
+ error = evaluate_rule( type_name, frame );
+
+ exit_module( typecheck );
+
+ if ( error )
+ argument_error( error->string, called, caller, arg_name );
+
+ frame_free( frame );
+ values = values->next;
+ }
+
+ enter_module( caller->module );
+}
+
+/*
+ * collect_arguments() - local argument checking and collection
+ */
+static SETTINGS *
+collect_arguments( RULE* rule, FRAME* frame )
+{
+ SETTINGS *locals = 0;
+
+ LOL * all_actual = frame->args;
+ LOL * all_formal = rule->arguments ? rule->arguments->data : 0;
+ if ( all_formal ) /* Nothing to set; nothing to check */
+ {
+ int max = all_formal->count > all_actual->count
+ ? all_formal->count
+ : all_actual->count;
+
+ int n;
+ for ( n = 0; n < max ; ++n )
+ {
+ LIST *actual = lol_get( all_actual, n );
+ char *type_name = 0;
+
+ LIST *formal;
+ for ( formal = lol_get( all_formal, n ); formal; formal = formal->next )
+ {
+ char* name = formal->string;
+
+ if ( is_type_name(name) )
+ {
+ if ( type_name )
+ argument_error( "missing argument name before type name:", rule, frame, formal );
+
+ if ( !formal->next )
+ argument_error( "missing argument name after type name:", rule, frame, formal );
+
+ type_name = formal->string;
+ }
+ else
+ {
+ LIST* value = 0;
+ char modifier;
+ LIST* arg_name = formal; /* hold the argument name for type checking */
+ int multiple = 0;
+
+ /* Stop now if a variable number of arguments are specified */
+ if ( name[0] == '*' && name[1] == 0 )
+ return locals;
+
+ modifier = arg_modifier( formal );
+
+ if ( !actual && modifier != '?' && modifier != '*' )
+ argument_error( "missing argument", rule, frame, formal );
+
+ switch ( modifier )
+ {
+ case '+':
+ case '*':
+ value = list_copy( 0, actual );
+ multiple = 1;
+ actual = 0;
+ /* skip an extra element for the modifier */
+ formal = formal->next;
+ break;
+ case '?':
+ /* skip an extra element for the modifier */
+ formal = formal->next;
+ /* fall through */
+ default:
+ if ( actual ) /* in case actual is missing */
+ {
+ value = list_new( 0, actual->string );
+ actual = actual->next;
+ }
+ }
+
+ locals = addsettings(locals, VAR_SET, name, value);
+ locals->multiple = multiple;
+ type_check( type_name, value, frame, rule, arg_name );
+ type_name = 0;
+ }
+ }
+
+ if ( actual )
+ {
+ argument_error( "extra argument", rule, frame, actual );
+ }
+ }
+ }
+ return locals;
+}
+
+RULE *
+enter_rule( char *rulename, module_t *target_module );
+
+#ifdef HAVE_PYTHON
+
+static int python_instance_number = 0;
+
+
+/* Given a Python object, return a string to use in Jam
+ code instead of said object.
+ If the object is string, use the string value
+ If the object implemenets __jam_repr__ method, use that.
+ Otherwise return 0.
+
+ The result value is newstr-ed. */
+char *python_to_string(PyObject* value)
+{
+ if (PyString_Check(value))
+ {
+ return newstr(PyString_AsString(value));
+ }
+ else
+ {
+ /* See if this is an instance that defines special __jam_repr__
+ method. */
+ if (PyInstance_Check(value)
+ && PyObject_HasAttrString(value, "__jam_repr__"))
+ {
+ PyObject* repr = PyObject_GetAttrString(value, "__jam_repr__");
+ if (repr)
+ {
+ PyObject* arguments2 = PyTuple_New(0);
+ PyObject* value2 = PyObject_Call(repr, arguments2, 0);
+ Py_DECREF(repr);
+ Py_DECREF(arguments2);
+ if (PyString_Check(value2))
+ {
+ return newstr(PyString_AsString(value2));
+ }
+ Py_DECREF(value2);
+ }
+ }
+ return 0;
+ }
+}
+
+static LIST*
+call_python_function(RULE* r, FRAME* frame)
+{
+ LIST * result = 0;
+ PyObject * arguments = 0;
+ PyObject * kw = NULL;
+ int i ;
+ PyObject * py_result;
+
+ if (r->arguments)
+ {
+ SETTINGS * args;
+
+ arguments = PyTuple_New(0);
+ kw = PyDict_New();
+
+ for (args = collect_arguments(r, frame); args; args = args->next)
+ {
+ PyObject *key = PyString_FromString(args->symbol);
+ PyObject *value = 0;
+ if (args->multiple)
+ value = list_to_python(args->value);
+ else {
+ if (args->value)
+ value = PyString_FromString(args->value->string);
+ }
+
+ if (value)
+ PyDict_SetItem(kw, key, value);
+ Py_DECREF(key);
+ Py_XDECREF(value);
+ }
+ }
+ else
+ {
+ arguments = PyTuple_New( frame->args->count );
+ for ( i = 0; i < frame->args->count; ++i )
+ {
+ PyObject * arg = PyList_New(0);
+ LIST* l = lol_get( frame->args, i);
+
+ for ( ; l; l = l->next )
+ {
+ PyObject * v = PyString_FromString(l->string);
+ PyList_Append( arg, v );
+ Py_DECREF(v);
+ }
+ /* Steals reference to 'arg' */
+ PyTuple_SetItem( arguments, i, arg );
+ }
+ }
+
+ frame_before_python_call = frame;
+ py_result = PyObject_Call( r->python_function, arguments, kw );
+ Py_DECREF(arguments);
+ Py_XDECREF(kw);
+ if ( py_result != NULL )
+ {
+ if ( PyList_Check( py_result ) )
+ {
+ int size = PyList_Size( py_result );
+ int i;
+ for ( i = 0; i < size; ++i )
+ {
+ PyObject * item = PyList_GetItem( py_result, i );
+ char *s = python_to_string (item);
+ if (!s) {
+ fprintf( stderr, "Non-string object returned by Python call.\n" );
+ } else {
+ result = list_new (result, s);
+ }
+ }
+ }
+ else if ( py_result == Py_None )
+ {
+ result = L0;
+ }
+ else
+ {
+ char *s = python_to_string(py_result);
+ if (s)
+ result = list_new(0, s);
+ else
+ /* We have tried all we could. Return empty list. There are
+ cases, e.g. feature.feature function that should return
+ value for the benefit of Python code and which also can be
+ called by Jam code, where no sensible value can be
+ returned. We cannot even emit a warning, since there will
+ be a pile of them. */
+ result = L0;
+ }
+
+ Py_DECREF( py_result );
+ }
+ else
+ {
+ PyErr_Print();
+ fprintf(stderr,"Call failed\n");
+ }
+
+ return result;
+}
+
+
+module_t * python_module()
+{
+ static module_t * python = 0;
+ if ( !python )
+ python = bindmodule("__python__");
+ return python;
+}
+
+#endif
+
+
+/*
+ * evaluate_rule() - execute a rule invocation.
+ */
+
+LIST *
+evaluate_rule(
+ char * rulename,
+ FRAME * frame )
+{
+ LIST * result = L0;
+ RULE * rule;
+ profile_frame prof[1];
+ module_t * prev_module = frame->module;
+
+ LIST * l;
+ {
+ LOL arg_context_, * arg_context = &arg_context_;
+ if ( !frame->prev )
+ lol_init(arg_context);
+ else
+ arg_context = frame->prev->args;
+ l = var_expand( L0, rulename, rulename+strlen(rulename), arg_context, 0 );
+ }
+
+ if ( !l )
+ {
+ backtrace_line( frame->prev );
+ printf( "warning: rulename %s expands to empty string\n", rulename );
+ backtrace( frame->prev );
+ return result;
+ }
+
+ rulename = l->string;
+ rule = bindrule( l->string, frame->module );
+
+#ifdef HAVE_PYTHON
+ if ( rule->python_function )
+ {
+ /* The below messing with modules is due to the way modules are
+ * implemented in Jam. Suppose we are in module M1 now. The global
+ * variable map actually holds 'M1' variables, and M1->variables hold
+ * global variables.
+ *
+ * If we call Python right away, Python calls back Jam and then Jam
+ * does 'module M1 { }' then Jam will try to swap the current global
+ * variables with M1->variables. The result will be that global
+ * variables map will hold global variables, and any variable settings
+ * we do will go to the global module, not M1.
+ *
+ * By restoring basic state, where the global variable map holds global
+ * variable, we make sure any future 'module M1' entry will work OK.
+ */
+
+ LIST * result;
+ module_t * m = python_module();
+
+ frame->module = m;
+
+ exit_module( prev_module );
+ enter_module( m );
+
+ result = call_python_function( rule, frame );
+
+ exit_module( m );
+ enter_module ( prev_module );
+
+ return result;
+ }
+#endif
+
+ /* Drop the rule name. */
+ l = list_pop_front( l );
+
+ /* Tack the rest of the expansion onto the front of the first argument. */
+ frame->args->list[0] = list_append( l, lol_get( frame->args, 0 ) );
+
+ if ( DEBUG_COMPILE )
+ {
+ /* Try hard to indicate in which module the rule is going to execute. */
+ if ( rule->module != frame->module
+ && rule->procedure != 0 && strcmp( rulename, rule->procedure->rulename ) )
+ {
+ char buf[256] = "";
+ strncat( buf, rule->module->name, sizeof( buf ) - 1 );
+ strncat( buf, rule->name, sizeof( buf ) - 1 );
+ debug_compile( 1, buf, frame );
+ }
+ else
+ {
+ debug_compile( 1, rulename, frame );
+ }
+
+ lol_print( frame->args );
+ printf( "\n" );
+ }
+
+ if ( rule->procedure && rule->module != prev_module )
+ {
+ /* Propagate current module to nested rule invocations. */
+ frame->module = rule->module;
+
+ /* Swap variables. */
+ exit_module( prev_module );
+ enter_module( rule->module );
+ }
+
+ /* Record current rule name in frame. */
+ if ( rule->procedure )
+ {
+ frame->rulename = rulename;
+ /* And enter record profile info. */
+ if ( DEBUG_PROFILE )
+ profile_enter( rule->procedure->rulename, prof );
+ }
+
+ /* Check traditional targets $(<) and sources $(>). */
+ if ( !rule->actions && !rule->procedure )
+ {
+ backtrace_line( frame->prev );
+ printf( "rule %s unknown in module %s\n", rule->name, frame->module->name );
+ backtrace( frame->prev );
+ exit( 1 );
+ }
+
+ /* If this rule will be executed for updating the targets then construct the
+ * action for make().
+ */
+ if ( rule->actions )
+ {
+ TARGETS * t;
+ ACTION * action;
+
+ /* The action is associated with this instance of this rule. */
+ action = (ACTION *)BJAM_MALLOC( sizeof( ACTION ) );
+ memset( (char *)action, '\0', sizeof( *action ) );
+
+ action->rule = rule;
+ action->targets = targetlist( (TARGETS *)0, lol_get( frame->args, 0 ) );
+ action->sources = targetlist( (TARGETS *)0, lol_get( frame->args, 1 ) );
+
+ /* If we have a group of targets all being built using the same action
+ * then we must not allow any of them to be used as sources unless they
+ * had all already been built in the first place or their joined action
+ * has had a chance to finish its work and build all of them anew.
+ *
+ * Without this it might be possible, in case of a multi-process build,
+ * for their action, triggered by buiding one of the targets, to still
+ * be running when another target in the group reports as done in order
+ * to avoid triggering the same action again and gets used prematurely.
+ *
+ * As a quick-fix to achieve this effect we make all the targets list
+ * each other as 'included targets'. More precisely, we mark the first
+ * listed target as including all the other targets in the list and vice
+ * versa. This makes anyone depending on any of those targets implicitly
+ * depend on all of them, thus making sure none of those targets can be
+ * used as sources until all of them have been built. Note that direct
+ * dependencies could not have been used due to the 'circular
+ * dependency' issue.
+ *
+ * TODO: Although the current implementation solves the problem of one
+ * of the targets getting used before its action completes its work it
+ * also forces the action to run whenever any of the targets in the
+ * group is not up to date even though some of them might not actually
+ * be used by the targets being built. We should see how we can
+ * correctly recognize such cases and use that to avoid running the
+ * action if possible and not rebuild targets not actually depending on
+ * targets that are not up to date.
+ *
+ * TODO: Using the 'include' feature might have side-effects due to
+ * interaction with the actual 'inclusion scanning' system. This should
+ * be checked.
+ */
+ if ( action->targets )
+ {
+ TARGET * t0 = action->targets->target;
+ for ( t = action->targets->next; t; t = t->next )
+ {
+ target_include( t->target, t0 );
+ target_include( t0, t->target );
+ }
+ }
+
+ /* Append this action to the actions of each target. */
+ for ( t = action->targets; t; t = t->next )
+ t->target->actions = actionlist( t->target->actions, action );
+ }
+
+ /* Now recursively compile any parse tree associated with this rule.
+ * parse_refer()/parse_free() call pair added to ensure rule not freed
+ * during use.
+ */
+ if ( rule->procedure )
+ {
+ SETTINGS * local_args = collect_arguments( rule, frame );
+ PARSE * parse = rule->procedure;
+ parse_refer( parse );
+
+ pushsettings( local_args );
+ result = parse_evaluate( parse, frame );
+ popsettings( local_args );
+ freesettings( local_args );
+
+ parse_free( parse );
+ }
+
+ if ( frame->module != prev_module )
+ {
+ exit_module( frame->module );
+ enter_module( prev_module );
+ }
+
+ if ( DEBUG_PROFILE && rule->procedure )
+ profile_exit( prof );
+
+ if ( DEBUG_COMPILE )
+ debug_compile( -1, 0, frame);
+
+ return result;
+}
+
+
+/*
+ * Call the given rule with the specified parameters. The parameters should be
+ * of type LIST* and end with a NULL pointer. This differs from 'evaluate_rule'
+ * in that frame for the called rule is prepared inside 'call_rule'.
+ *
+ * This function is useful when a builtin rule (in C) wants to call another rule
+ * which might be implemented in Jam.
+ */
+
+LIST * call_rule( char * rulename, FRAME * caller_frame, ... )
+{
+ va_list va;
+ LIST * result;
+
+ FRAME inner[1];
+ frame_init( inner );
+ inner->prev = caller_frame;
+ inner->prev_user = caller_frame->module->user_module ?
+ caller_frame : caller_frame->prev_user;
+ inner->module = caller_frame->module;
+ inner->procedure = 0;
+
+ va_start( va, caller_frame );
+ for ( ; ; )
+ {
+ LIST * l = va_arg( va, LIST* );
+ if ( !l )
+ break;
+ lol_add( inner->args, l );
+ }
+ va_end( va );
+
+ result = evaluate_rule( rulename, inner );
+
+ frame_free( inner );
+
+ return result;
+}
+
+
+/*
+ * compile_rules() - compile a chain of rules
+ *
+ * parse->left single rule
+ * parse->right more compile_rules() by right-recursion
+ */
+
+LIST * compile_rules( PARSE * parse, FRAME * frame )
+{
+ /* Ignore result from first statement; return the 2nd. */
+ /* Optimize recursion on the right by looping. */
+ do list_free( parse_evaluate( parse->left, frame ) );
+ while ( ( parse = parse->right )->func == compile_rules );
+ return parse_evaluate( parse, frame );
+}
+
+
+/*
+ * assign_var_mode() - convert ASSIGN_XXX compilation flag into corresponding
+ * VAR_XXX variable set flag.
+ */
+
+static int assign_var_mode( int parsenum, char const * * tracetext )
+{
+ char const * trace;
+ int setflag;
+ switch ( parsenum )
+ {
+ case ASSIGN_SET : setflag = VAR_SET ; trace = "=" ; break;
+ case ASSIGN_APPEND : setflag = VAR_APPEND ; trace = "+="; break;
+ case ASSIGN_DEFAULT: setflag = VAR_DEFAULT; trace = "?="; break;
+ default: setflag = VAR_SET ; trace = "" ; break;
+ }
+ if ( tracetext )
+ *tracetext = trace ;
+ return setflag;
+}
+
+/*
+ * compile_set() - compile the "set variable" statement
+ *
+ * parse->left variable names
+ * parse->right variable values
+ * parse->num ASSIGN_SET/APPEND/DEFAULT
+ */
+
+LIST * compile_set( PARSE * parse, FRAME * frame )
+{
+ LIST * nt = parse_evaluate( parse->left, frame );
+ LIST * ns = parse_evaluate( parse->right, frame );
+ LIST * l;
+ char const * trace;
+ int setflag = assign_var_mode( parse->num, &trace );
+
+ if ( DEBUG_COMPILE )
+ {
+ debug_compile( 0, "set", frame );
+ list_print( nt );
+ printf( " %s ", trace );
+ list_print( ns );
+ printf( "\n" );
+ }
+
+ /* Call var_set to set variable. var_set keeps ns, so need to copy it. */
+ for ( l = nt; l; l = list_next( l ) )
+ var_set( l->string, list_copy( L0, ns ), setflag );
+ list_free( nt );
+ return ns;
+}
+
+
+/*
+ * compile_setcomp() - support for `rule` - save parse tree.
+ *
+ * parse->string rule name
+ * parse->left rules for rule
+ * parse->right optional list-of-lists describing arguments
+ */
+
+LIST * compile_setcomp( PARSE * parse, FRAME * frame )
+{
+ argument_list * arg_list = 0;
+
+ /* Create new LOL describing argument requirements if supplied. */
+ if ( parse->right )
+ {
+ PARSE * p;
+ arg_list = args_new();
+ for ( p = parse->right; p; p = p->left )
+ lol_add( arg_list->data, parse_evaluate( p->right, frame ) );
+ }
+
+ new_rule_body( frame->module, parse->string, arg_list, parse->left, !parse->num );
+ return L0;
+}
+
+
+/*
+ * compile_setexec() - support for `actions` - save execution string.
+ *
+ * parse->string rule name
+ * parse->string1 OS command string
+ * parse->num flags
+ * parse->left `bind` variables
+ *
+ * Note that the parse flags (as defined in compile.h) are transferred directly
+ * to the rule flags (as defined in rules.h).
+ */
+
+LIST * compile_setexec( PARSE * parse, FRAME * frame )
+{
+ LIST * bindlist = parse_evaluate( parse->left, frame );
+ new_rule_actions( frame->module, parse->string, parse->string1, bindlist, parse->num );
+ return L0;
+}
+
+
+/*
+ * compile_settings() - compile the "on =" (set variable on exec) statement.
+ *
+ * parse->left variable names
+ * parse->right target name
+ * parse->third variable value
+ * parse->num ASSIGN_SET/APPEND
+ */
+
+LIST * compile_settings( PARSE * parse, FRAME * frame )
+{
+ LIST * nt = parse_evaluate( parse->left, frame );
+ LIST * ns = parse_evaluate( parse->third, frame );
+ LIST * targets = parse_evaluate( parse->right, frame );
+ LIST * ts;
+ char const * trace;
+ int setflag = assign_var_mode( parse->num, &trace );
+
+ if ( DEBUG_COMPILE )
+ {
+ debug_compile( 0, "set", frame );
+ list_print( nt );
+ printf( " on " );
+ list_print( targets );
+ printf( " %s ", trace );
+ list_print( ns );
+ printf( "\n" );
+ }
+
+ /* Call addsettings() to save variable setting. addsettings() keeps ns, so
+ * need to copy it. Pass append flag to addsettings().
+ */
+ for ( ts = targets; ts; ts = list_next( ts ) )
+ {
+ TARGET * t = bindtarget( ts->string );
+ LIST * l;
+
+ for ( l = nt; l; l = list_next( l ) )
+ t->settings = addsettings( t->settings, setflag, l->string,
+ list_copy( (LIST *)0, ns ) );
+ }
+
+ list_free( nt );
+ list_free( targets );
+ return ns;
+}
+
+
+/*
+ * compile_switch() - compile 'switch' rule.
+ *
+ * parse->left switch value (only 1st used)
+ * parse->right cases
+ *
+ * cases->left 1st case
+ * cases->right next cases
+ *
+ * case->string argument to match
+ * case->left parse tree to execute
+ */
+
+LIST * compile_switch( PARSE * parse, FRAME * frame )
+{
+ LIST * nt = parse_evaluate( parse->left, frame );
+ LIST * result = 0;
+
+ if ( DEBUG_COMPILE )
+ {
+ debug_compile( 0, "switch", frame );
+ list_print( nt );
+ printf( "\n" );
+ }
+
+ /* Step through cases. */
+ for ( parse = parse->right; parse; parse = parse->right )
+ {
+ if ( !glob( parse->left->string, nt ? nt->string : "" ) )
+ {
+ /* Get & exec parse tree for this case. */
+ parse = parse->left->left;
+ result = parse_evaluate( parse, frame );
+ break;
+ }
+ }
+
+ list_free( nt );
+ return result;
+}
+
+
+/*
+ * debug_compile() - printf with indent to show rule expansion.
+ */
+
+static void debug_compile( int which, char * s, FRAME * frame )
+{
+ static int level = 0;
+ static char indent[36] = ">>>>|>>>>|>>>>|>>>>|>>>>|>>>>|>>>>|";
+
+ if ( which >= 0 )
+ {
+ int i;
+
+ print_source_line( frame->procedure );
+
+ i = ( level + 1 ) * 2;
+ while ( i > 35 )
+ {
+ fputs( indent, stdout );
+ i -= 35;
+ }
+
+ printf( "%*.*s ", i, i, indent );
+ }
+
+ if ( s )
+ printf( "%s ", s );
+
+ level += which;
+}
diff --git a/jam-files/engine/compile.h b/jam-files/engine/compile.h
new file mode 100644
index 00000000..7d5191f0
--- /dev/null
+++ b/jam-files/engine/compile.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 1993, 2000 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/* This file is ALSO:
+ * Copyright 2001-2004 David Abrahams.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+#ifndef COMPILE_DWA20011022_H
+# define COMPILE_DWA20011022_H
+
+# include "frames.h"
+# include "parse.h"
+# include "regexp.h"
+
+/*
+ * compile.h - compile parsed jam statements
+ */
+
+void compile_builtins();
+
+LIST *compile_append( PARSE *parse, FRAME *frame );
+LIST *compile_foreach( PARSE *parse, FRAME *frame );
+LIST *compile_if( PARSE *parse, FRAME *frame );
+LIST *compile_eval( PARSE *parse, FRAME *args );
+LIST *compile_include( PARSE *parse, FRAME *frame );
+LIST *compile_list( PARSE *parse, FRAME *frame );
+LIST *compile_local( PARSE *parse, FRAME *frame );
+LIST *compile_module( PARSE *parse, FRAME *frame );
+LIST *compile_class( PARSE *parse, FRAME *frame );
+LIST *compile_null( PARSE *parse, FRAME *frame );
+LIST *compile_on( PARSE *parse, FRAME *frame );
+LIST *compile_rule( PARSE *parse, FRAME *frame );
+LIST *compile_rules( PARSE *parse, FRAME *frame );
+LIST *compile_set( PARSE *parse, FRAME *frame );
+LIST *compile_setcomp( PARSE *parse, FRAME *frame );
+LIST *compile_setexec( PARSE *parse, FRAME *frame );
+LIST *compile_settings( PARSE *parse, FRAME *frame );
+LIST *compile_switch( PARSE *parse, FRAME *frame );
+LIST *compile_while( PARSE *parse, FRAME *frame );
+
+LIST *evaluate_rule( char *rulename, FRAME *frame );
+LIST *call_rule( char *rulename, FRAME* caller_frame, ...);
+
+regexp* regex_compile( const char* pattern );
+
+/* Flags for compile_set(), etc */
+
+# define ASSIGN_SET 0x00 /* = assign variable */
+# define ASSIGN_APPEND 0x01 /* += append variable */
+# define ASSIGN_DEFAULT 0x02 /* set only if unset */
+
+/* Flags for compile_setexec() */
+
+# define EXEC_UPDATED 0x01 /* executes updated */
+# define EXEC_TOGETHER 0x02 /* executes together */
+# define EXEC_IGNORE 0x04 /* executes ignore */
+# define EXEC_QUIETLY 0x08 /* executes quietly */
+# define EXEC_PIECEMEAL 0x10 /* executes piecemeal */
+# define EXEC_EXISTING 0x20 /* executes existing */
+
+/* Conditions for compile_if() */
+
+# define EXPR_NOT 0 /* ! cond */
+# define EXPR_AND 1 /* cond && cond */
+# define EXPR_OR 2 /* cond || cond */
+
+# define EXPR_EXISTS 3 /* arg */
+# define EXPR_EQUALS 4 /* arg = arg */
+# define EXPR_NOTEQ 5 /* arg != arg */
+# define EXPR_LESS 6 /* arg < arg */
+# define EXPR_LESSEQ 7 /* arg <= arg */
+# define EXPR_MORE 8 /* arg > arg */
+# define EXPR_MOREEQ 9 /* arg >= arg */
+# define EXPR_IN 10 /* arg in arg */
+
+#endif
+
diff --git a/jam-files/engine/debian/changelog b/jam-files/engine/debian/changelog
new file mode 100644
index 00000000..29084289
--- /dev/null
+++ b/jam-files/engine/debian/changelog
@@ -0,0 +1,72 @@
+bjam (3.1.12-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Rene Rivera <grafik@redshift-software.com> Sat, 01 Oct 2005 00:00:00 +0000
+
+bjam (3.1.11-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Rene Rivera <grafik@redshift-software.com> Sat, 30 Apr 2005 00:00:00 +0000
+
+bjam (3.1.10-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Rene Rivera <grafik@redshift-software.com> Tue, 1 Jun 2004 05:42:35 +0000
+
+bjam (3.1.9-2) unstable; urgency=low
+
+ * Use default value of BOOST_BUILD_PATH is not is set in environment.
+
+ -- Vladimir Prus <ghost@zigzag.lvk.cs.msu.su> Wed, 17 Dec 2003 16:44:35 +0300
+
+bjam (3.1.9-1) unstable; urgency=low
+
+ * Implement NATIVE_FILE builtin and several native rules.
+
+ -- Vladimir Prus <ghost@zigzag.lvk.cs.msu.su> Thu, 11 Dec 2003 13:15:26 +0300
+
+bjam (3.1.8-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Vladimir Prus <ghost@zigzag.lvk.cs.msu.su> Tue, 4 Nov 2003 20:50:43 +0300
+
+bjam (3.1.7-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Vladimir Prus <ghost@zigzag.lvk.cs.msu.su> Thu, 11 Sep 2003 10:45:44 +0400
+
+bjam (3.1.6-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Vladimir Prus <ghost@zigzag.lvk.cs.msu.su> Tue, 1 Jul 2003 09:12:18 +0400
+
+bjam (3.1.5-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Vladimir Prus <ghost@zigzag.lvk.cs.msu.su> Mon, 19 May 2003 14:05:13 +0400
+
+bjam (3.1.3-2) unstable; urgency=low
+
+ * Changed Debian package to be similar to Jam's package.
+
+ -- Vladimir Prus <ghost@cs.msu.su> Thu, 10 Oct 2002 18:43:26 +0400
+
+bjam (3.1.3-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Vladimir Prus <ghost@zigzag.lvk.cs.msu.su> Fri, 4 Oct 2002 18:16:54 +0400
+
+bjam (3.1.2-1) unstable; urgency=low
+
+ * Initial Release.
+
+ -- Vladimir Prus <ghost@cs.msu.su> Wed, 14 Aug 2002 14:08:00 +0400
+
diff --git a/jam-files/engine/debian/control b/jam-files/engine/debian/control
new file mode 100644
index 00000000..c7f15193
--- /dev/null
+++ b/jam-files/engine/debian/control
@@ -0,0 +1,16 @@
+Source: bjam
+Section: devel
+Priority: optional
+Maintainer: Vladimir Prus <ghost@cs.msu.su>
+Build-Depends: debhelper (>> 3.0.0), docbook-to-man, bison
+Standards-Version: 3.5.2
+
+Package: bjam
+Architecture: any
+Depends: ${shlibs:Depends}
+Description: Build tool
+ Boost.Jam is a portable build tool with its own interpreted language, which
+ allows to implement rather complex logic in a readable way and without
+ resorting to external programs. It is a descendant of Jam/MR tool modified to
+ suit the needs of Boost.Build. In particular, modules and rule parameters
+ were added, as well as several new builtins.
diff --git a/jam-files/engine/debian/copyright b/jam-files/engine/debian/copyright
new file mode 100644
index 00000000..f72e4e3a
--- /dev/null
+++ b/jam-files/engine/debian/copyright
@@ -0,0 +1,25 @@
+This package was debianized by Vladimir Prus <ghost@cs.msu.su> on
+Wed, 17 July 2002, 19:27:00 +0400.
+
+Copyright:
+
+ /+\
+ +\ Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
+ \+/
+
+ This is Release 2.4 of Jam/MR, a make-like program.
+
+ License is hereby granted to use this software and distribute it
+ freely, as long as this copyright notice is retained and modifications
+ are clearly marked.
+
+ ALL WARRANTIES ARE HEREBY DISCLAIMED.
+
+Some portions are also:
+
+ Copyright 2001-2006 David Abrahams.
+ Copyright 2002-2006 Rene Rivera.
+ Copyright 2003-2006 Vladimir Prus.
+
+ Distributed under the Boost Software License, Version 1.0.
+ (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
diff --git a/jam-files/engine/debian/jam.man.sgml b/jam-files/engine/debian/jam.man.sgml
new file mode 100644
index 00000000..ee21d4d8
--- /dev/null
+++ b/jam-files/engine/debian/jam.man.sgml
@@ -0,0 +1,236 @@
+<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [
+
+<!-- Process this file with docbook-to-man to generate an nroff manual
+ page: `docbook-to-man manpage.sgml > manpage.1'. You may view
+ the manual page with: `docbook-to-man manpage.sgml | nroff -man |
+ less'. A typical entry in a Makefile or Makefile.am is:
+
+manpage.1: manpage.sgml
+ docbook-to-man $< > $@
+ -->
+
+ <!ENTITY dhfirstname "<firstname>Yann</firstname>">
+ <!ENTITY dhsurname "<surname>Dirson</surname>">
+ <!-- Please adjust the date whenever revising the manpage. -->
+ <!ENTITY dhdate "<date>mai 23, 2001</date>">
+ <!ENTITY dhemail "<email>dirson@debian.org</email>">
+ <!ENTITY dhusername "Yann Dirson">
+ <!ENTITY dhpackage "jam">
+
+ <!ENTITY debian "<productname>Debian GNU/Linux</productname>">
+ <!ENTITY gnu "<acronym>GNU</acronym>">
+]>
+
+<refentry>
+ <refentryinfo>
+ <address>
+ &dhemail;
+ </address>
+ <author>
+ &dhfirstname;
+ &dhsurname;
+ </author>
+ <copyright>
+ <year>2001</year>
+ <holder>&dhusername;</holder>
+ </copyright>
+ &dhdate;
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>JAM</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>Jam/MR</refname>
+ <refpurpose>Make(1) Redux</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>jam</command>
+
+ <arg><option>-a</option></arg>
+ <arg><option>-n</option></arg>
+ <arg><option>-v</option></arg>
+
+ <arg><option>-d <replaceable/debug/</option></arg>
+ <arg><option>-f <replaceable/jambase/</option></arg>
+ <arg><option>-j <replaceable/jobs/</option></arg>
+ <arg><option>-o <replaceable/actionsfile/</option></arg>
+ <arg><option>-s <replaceable/var/=<replaceable/value/</option></arg>
+ <arg><option>-t <replaceable/target/</option></arg>
+
+ <arg repeat><option><replaceable/target/</option></arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>Jam is a program construction tool, like make(1).</para>
+
+ <para>Jam recursively builds target files from source files, using
+ dependency information and updating actions expressed in the
+ Jambase file, which is written in jam's own interpreted language.
+ The default Jambase is compiled into jam and provides a
+ boilerplate for common use, relying on a user-provide file
+ "Jamfile" to enumerate actual targets and sources.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><option/-a/</term>
+ <listitem>
+ <para>Build all targets anyway, even if they are up-to-date.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-d <replaceable/n/</option></term>
+ <listitem>
+ <para>Enable cummulative debugging levels from 1 to
+ <replaceable/n/. Interesting values are:
+
+ <glosslist>
+ <glossentry><glossterm/1/ <glossdef><simpara/Show
+ actions (the default)/</glossdef></glossentry>
+
+ <glossentry><glossterm/2/ <glossdef><simpara/Show
+ "quiet" actions and display all action
+ text/</glossdef></glossentry>
+
+ <glossentry><glossterm/3/ <glossdef><simpara>Show
+ dependency analysis, and target/source
+ timestamps/paths</simpara></glossdef></glossentry>
+
+ <glossentry><glossterm/4/ <glossdef><simpara/Show shell
+ arguments/</glossdef></glossentry>
+
+ <glossentry><glossterm/5/ <glossdef><simpara/Show rule
+ invocations and variable
+ expansions/</glossdef></glossentry>
+
+ <glossentry><glossterm/6/ <glossdef><simpara>Show
+ directory/header file/archive
+ scans</simpara></glossdef></glossentry>
+
+ <glossentry><glossterm/7/ <glossdef><simpara/Show
+ variable settings/</glossdef></glossentry>
+
+ <glossentry><glossterm/8/ <glossdef><simpara/Show
+ variable fetches/</glossdef></glossentry>
+
+ <glossentry><glossterm/9/ <glossdef><simpara/Show
+ variable manipulation, scanner
+ tokens/</glossdef></glossentry>
+ </glosslist>
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-d +<replaceable/n/</option></term>
+ <listitem>
+ <para>Enable debugging level <replaceable/n/.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option/-d 0/</term>
+ <listitem>
+ <para>Turn off all debugging levels. Only errors are not
+ suppressed.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-f <replaceable/jambase/</option></term>
+ <listitem>
+ <para>Read <replaceable/jambase/ instead of using the
+ built-in Jambase. Only one <option/-f/ flag is permitted,
+ but the <replaceable/jambase/ may explicitly include other
+ files.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-j <replaceable/n/</option></term>
+ <listitem>
+ <para>Run up to <replaceable/n/ shell commands concurrently
+ (UNIX and NT only). The default is 1.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option/-n/</term>
+ <listitem>
+ <para>Don't actually execute the updating actions, but do
+ everything else. This changes the debug level default to
+ <option/-d2/.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-o <replaceable/file/</option></term>
+ <listitem>
+ <para>Write the updating actions to the specified file
+ instead of running them (or outputting them, as on the
+ Mac).</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-s <replaceable/var/=<replaceable/value/</option></term>
+ <listitem>
+ <para>Set the variable <replaceable/var/ to
+ <replaceable/value/, overriding both internal variables and
+ variables imported from the environment. </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-t <replaceable/target/</option></term>
+ <listitem>
+ <para>Rebuild <replaceable/target/ and everything that
+ depends on it, even if it is up-to-date.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option/-v/</term>
+ <listitem>
+ <para>Print the version of jam and exit.</para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>SEE ALSO</title>
+
+ <para>Jam is documented fully in HTML pages available on Debian
+ systems from
+ <filename>/usr/share/doc/jam/Jam.html</filename>.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>AUTHOR</title>
+
+ <para>This manual page was created by &dhusername; &dhemail; from
+ the <filename/Jam.html/ documentation, for the &debian; system
+ (but may be used by others).</para>
+ </refsect1>
+</refentry>
+
+<!-- Keep this comment at the end of the file
+Local variables:
+sgml-omittag:t
+sgml-shorttag:t
+End:
+-->
diff --git a/jam-files/engine/debian/rules b/jam-files/engine/debian/rules
new file mode 100755
index 00000000..756052a3
--- /dev/null
+++ b/jam-files/engine/debian/rules
@@ -0,0 +1,73 @@
+#!/usr/bin/make -f
+# Sample debian/rules that uses debhelper.
+# GNU copyright 1997 to 1999 by Joey Hess.
+# GNU copyright 2001 by Yann Dirson.
+
+# This is the debian/rules file for packages jam and ftjam
+# It should be usable with both packages without any change
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+# This is the debhelper compatability version to use.
+export DH_COMPAT=3
+
+topdir=$(shell pwd)
+
+jam=bjam
+binname=bjam
+
+build: build-stamp
+build-stamp: debian/jam.1
+ dh_testdir
+
+ ./build.sh
+
+ touch build-stamp
+
+%.1: %.man.sgml
+ /usr/bin/docbook-to-man $< > $@
+
+clean:
+ dh_testdir
+ dh_testroot
+ rm -f build-stamp
+ rm -rf bin.*
+ rm -f jam0 debian/jam.1
+ dh_clean
+
+install: build
+ dh_testdir
+ dh_testroot
+ dh_clean -k
+ dh_installdirs
+
+ install -d ${topdir}/debian/${jam}/usr/bin
+ install -m755 bin.linuxx86/bjam ${topdir}/debian/${jam}/usr/bin/
+ install -d ${topdir}/debian/${jam}/usr/share/man/man1/
+ install -m644 debian/jam.1 ${topdir}/debian/${jam}/usr/share/man/man1/${binname}.1
+
+
+# Build architecture-independent files here.
+binary-indep: build install
+# We have nothing to do by default.
+
+# Build architecture-dependent files here.
+binary-arch: build install
+ dh_testdir
+ dh_testroot
+ dh_installdocs README RELNOTES Jambase *.html
+# dh_installemacsen
+# dh_undocumented
+ dh_installchangelogs
+ dh_strip
+ dh_compress
+ dh_fixperms
+ dh_installdeb
+ dh_shlibdeps
+ dh_gencontrol
+ dh_md5sums
+ dh_builddeb
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install configure
diff --git a/jam-files/engine/debug.c b/jam-files/engine/debug.c
new file mode 100644
index 00000000..7290555a
--- /dev/null
+++ b/jam-files/engine/debug.c
@@ -0,0 +1,132 @@
+/*
+ Copyright Rene Rivera 2005.
+ Distributed under the Boost Software License, Version 1.0.
+ (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+*/
+
+#include "jam.h"
+
+#include "hash.h"
+
+#include <time.h>
+#include <assert.h>
+
+
+static profile_frame * profile_stack = 0;
+static struct hash * profile_hash = 0;
+static profile_info profile_other = { "[OTHER]", 0, 0, 0, 0, 0 };
+static profile_info profile_total = { "[TOTAL]", 0, 0, 0, 0, 0 };
+
+
+profile_frame * profile_init( char * rulename, profile_frame * frame )
+{
+ if ( DEBUG_PROFILE ) profile_enter( rulename, frame );
+ return frame;
+}
+
+
+void profile_enter( char * rulename, profile_frame * frame )
+{
+ if ( DEBUG_PROFILE )
+ {
+ clock_t start = clock();
+ profile_info info;
+ profile_info * p = &info;
+
+ if ( !rulename ) p = &profile_other;
+
+ if ( !profile_hash && rulename )
+ profile_hash = hashinit( sizeof( profile_info ), "profile" );
+
+ info.name = rulename;
+
+ if ( rulename && hashenter( profile_hash, (HASHDATA * *)&p ) )
+ p->cumulative = p->net = p->num_entries = p->stack_count = p->memory = 0;
+
+ ++p->num_entries;
+ ++p->stack_count;
+
+ frame->info = p;
+
+ frame->caller = profile_stack;
+ profile_stack = frame;
+
+ frame->entry_time = clock();
+ frame->overhead = 0;
+ frame->subrules = 0;
+
+ /* caller pays for the time it takes to play with the hash table */
+ if ( frame->caller )
+ frame->caller->overhead += frame->entry_time - start;
+ }
+}
+
+
+void profile_memory( long mem )
+{
+ if ( DEBUG_PROFILE )
+ if ( profile_stack && profile_stack->info )
+ profile_stack->info->memory += mem;
+}
+
+
+void profile_exit( profile_frame * frame )
+{
+ if ( DEBUG_PROFILE )
+ {
+ /* Cumulative time for this call. */
+ clock_t t = clock() - frame->entry_time - frame->overhead;
+ /* If this rule is already present on the stack, don't add the time for
+ * this instance.
+ */
+ if ( frame->info->stack_count == 1 )
+ frame->info->cumulative += t;
+ /* Net time does not depend on presense of the same rule in call stack.
+ */
+ frame->info->net += t - frame->subrules;
+
+ if ( frame->caller )
+ {
+ /* Caller's cumulative time must account for this overhead. */
+ frame->caller->overhead += frame->overhead;
+ frame->caller->subrules += t;
+ }
+ /* Pop this stack frame. */
+ --frame->info->stack_count;
+ profile_stack = frame->caller;
+ }
+}
+
+
+static void dump_profile_entry( void * p_, void * ignored )
+{
+ profile_info * p = (profile_info *)p_;
+ unsigned long mem_each = ( p->memory / ( p->num_entries ? p->num_entries : 1 ) );
+ double cumulative = p->cumulative;
+ double net = p->net;
+ double q = p->net;
+ q /= ( p->num_entries ? p->num_entries : 1 );
+ cumulative /= CLOCKS_PER_SEC;
+ net /= CLOCKS_PER_SEC;
+ q /= CLOCKS_PER_SEC;
+ if ( !ignored )
+ {
+ profile_total.cumulative += p->net;
+ profile_total.memory += p->memory;
+ }
+ printf( "%10ld %12.6f %12.6f %12.8f %10ld %10ld %s\n", p->num_entries,
+ cumulative, net, q, p->memory, mem_each, p->name );
+}
+
+
+void profile_dump()
+{
+ if ( profile_hash )
+ {
+ printf( "%10s %12s %12s %12s %10s %10s %s\n", "--count--", "--gross--",
+ "--net--", "--each--", "--mem--", "--each--", "--name--" );
+ hashenumerate( profile_hash, dump_profile_entry, 0 );
+ dump_profile_entry( &profile_other, 0 );
+ dump_profile_entry( &profile_total, (void *)1 );
+ }
+}
diff --git a/jam-files/engine/debug.h b/jam-files/engine/debug.h
new file mode 100644
index 00000000..115a8873
--- /dev/null
+++ b/jam-files/engine/debug.h
@@ -0,0 +1,54 @@
+/*
+ Copyright Rene Rivera 2005.
+ Distributed under the Boost Software License, Version 1.0.
+ (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+*/
+#ifndef BJAM_DEBUG_H
+#define BJAM_DEBUG_H
+
+#include "jam.h"
+#include <time.h>
+
+
+struct profile_info
+{
+ /* name of rule being called */
+ char* name;
+ /* cumulative time spent in rule */
+ clock_t cumulative;
+ /* time spent in rule proper */
+ clock_t net;
+ /* number of time rule was entered */
+ unsigned long num_entries;
+ /* number of the times this function is present in stack */
+ unsigned long stack_count;
+ /* bytes of memory allocated by the call */
+ unsigned long memory;
+};
+typedef struct profile_info profile_info;
+
+struct profile_frame
+{
+ /* permanent storage where data accumulates */
+ profile_info* info;
+ /* overhead for profiling in this call */
+ clock_t overhead;
+ /* time of last entry to rule */
+ clock_t entry_time;
+ /* stack frame of caller */
+ struct profile_frame* caller;
+ /* time spent in subrules */
+ clock_t subrules;
+};
+typedef struct profile_frame profile_frame;
+
+profile_frame * profile_init( char * rulename, profile_frame * frame );
+void profile_enter( char* rulename, profile_frame * frame );
+void profile_memory( long mem );
+void profile_exit( profile_frame * frame );
+void profile_dump();
+
+#define PROFILE_ENTER( scope ) profile_frame PROF_ ## scope, *PROF_ ## scope ## _p = profile_init( #scope, &PROF_ ## scope )
+#define PROFILE_EXIT( scope ) profile_exit( PROF_ ## scope ## _p )
+
+#endif
diff --git a/jam-files/engine/execcmd.h b/jam-files/engine/execcmd.h
new file mode 100644
index 00000000..67f2b839
--- /dev/null
+++ b/jam-files/engine/execcmd.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * execcmd.h - execute a shell script.
+ *
+ * Defines the interface to be implemented in platform specific implementation
+ * modules.
+ *
+ * 05/04/94 (seiwald) - async multiprocess interface
+ */
+
+#ifndef EXECCMD_H
+#define EXECCMD_H
+
+#include <time.h>
+
+typedef struct timing_info
+{
+ double system;
+ double user;
+ time_t start;
+ time_t end;
+} timing_info;
+
+void exec_cmd
+(
+ char * string,
+ void (* func)( void * closure, int status, timing_info *, char *, char * ),
+ void * closure,
+ LIST * shell,
+ char * action,
+ char * target
+);
+
+int exec_wait();
+
+#define EXEC_CMD_OK 0
+#define EXEC_CMD_FAIL 1
+#define EXEC_CMD_INTR 2
+
+#endif
diff --git a/jam-files/engine/execmac.c b/jam-files/engine/execmac.c
new file mode 100644
index 00000000..2ddddedd
--- /dev/null
+++ b/jam-files/engine/execmac.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+#include "jam.h"
+#include "lists.h"
+#include "execcmd.h"
+#include <errno.h>
+
+#ifdef OS_MAC
+
+/*
+ * execunix.c - execute a shell script on UNIX
+ *
+ * If $(JAMSHELL) is defined, uses that to formulate execvp().
+ * The default is:
+ *
+ * /bin/sh -c %
+ *
+ * Each word must be an individual element in a jam variable value.
+ *
+ * In $(JAMSHELL), % expands to the command string and ! expands to
+ * the slot number (starting at 1) for multiprocess (-j) invocations.
+ * If $(JAMSHELL) doesn't include a %, it is tacked on as the last
+ * argument.
+ *
+ * Don't just set JAMSHELL to /bin/sh - it won't work!
+ *
+ * External routines:
+ * exec_cmd() - launch an async command execution.
+ * exec_wait() - wait and drive at most one execution completion.
+ *
+ * Internal routines:
+ * onintr() - bump intr to note command interruption.
+ *
+ * 04/08/94 (seiwald) - Coherent/386 support added.
+ * 05/04/94 (seiwald) - async multiprocess interface
+ * 01/22/95 (seiwald) - $(JAMSHELL) support
+ */
+
+
+/*
+ * exec_cmd() - launch an async command execution.
+ */
+
+void exec_cmd
+(
+ char * string,
+ void (* func)( void * closure, int status, timing_info *, char *, char * ),
+ void * closure,
+ LIST * shell
+)
+{
+ printf( "%s", string );
+ (*func)( closure, EXEC_CMD_OK );
+}
+
+/*
+ * exec_wait() - wait and drive at most one execution completion.
+ */
+
+int exec_wait()
+{
+ return 0;
+}
+
+#endif /* OS_MAC */
diff --git a/jam-files/engine/execnt.c b/jam-files/engine/execnt.c
new file mode 100644
index 00000000..76420451
--- /dev/null
+++ b/jam-files/engine/execnt.c
@@ -0,0 +1,1296 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/* This file is ALSO:
+ * Copyright 2001-2004 David Abrahams.
+ * Copyright 2007 Rene Rivera.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+#include "jam.h"
+#include "lists.h"
+#include "execcmd.h"
+#include "pathsys.h"
+#include "string.h"
+#include "output.h"
+#include <errno.h>
+#include <assert.h>
+#include <ctype.h>
+#include <time.h>
+#include <math.h>
+
+#ifdef USE_EXECNT
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <process.h>
+#include <tlhelp32.h>
+
+/*
+ * execnt.c - execute a shell command on Windows NT
+ *
+ * If $(JAMSHELL) is defined, uses that to formulate execvp()/spawnvp().
+ * The default is:
+ *
+ * /bin/sh -c % [ on UNIX/AmigaOS ]
+ * cmd.exe /c % [ on Windows NT ]
+ *
+ * Each word must be an individual element in a jam variable value.
+ *
+ * In $(JAMSHELL), % expands to the command string and ! expands to
+ * the slot number (starting at 1) for multiprocess (-j) invocations.
+ * If $(JAMSHELL) doesn't include a %, it is tacked on as the last
+ * argument.
+ *
+ * Don't just set JAMSHELL to /bin/sh or cmd.exe - it won't work!
+ *
+ * External routines:
+ * exec_cmd() - launch an async command execution.
+ * exec_wait() - wait and drive at most one execution completion.
+ *
+ * Internal routines:
+ * onintr() - bump intr to note command interruption.
+ *
+ * 04/08/94 (seiwald) - Coherent/386 support added.
+ * 05/04/94 (seiwald) - async multiprocess interface
+ * 01/22/95 (seiwald) - $(JAMSHELL) support
+ * 06/02/97 (gsar) - full async multiprocess support for Win32
+ */
+
+/* get the maximum command line length according to the OS */
+int maxline();
+
+/* delete and argv list */
+static void free_argv(char**);
+/* Convert a command string into arguments for spawnvp. */
+static char** string_to_args(const char*);
+/* bump intr to note command interruption */
+static void onintr(int);
+/* If the command is suitable for execution via spawnvp */
+long can_spawn(char*);
+/* Add two 64-bit unsigned numbers, h1l1 and h2l2 */
+static FILETIME add_64(
+ unsigned long h1, unsigned long l1,
+ unsigned long h2, unsigned long l2);
+static FILETIME add_FILETIME(FILETIME t1, FILETIME t2);
+static FILETIME negate_FILETIME(FILETIME t);
+/* Convert a FILETIME to a number of seconds */
+static double filetime_seconds(FILETIME t);
+/* record the timing info for the process */
+static void record_times(HANDLE, timing_info*);
+/* calc the current running time of an *active* process */
+static double running_time(HANDLE);
+/* */
+DWORD get_process_id(HANDLE);
+/* terminate the given process, after terminating all its children */
+static void kill_process_tree(DWORD, HANDLE);
+/* waits for a command to complete or for the given timeout, whichever is first */
+static int try_wait(int timeoutMillis);
+/* reads any pending output for running commands */
+static void read_output();
+/* checks if a command ran out of time, and kills it */
+static int try_kill_one();
+/* */
+static double creation_time(HANDLE);
+/* Recursive check if first process is parent (directly or indirectly) of
+the second one. */
+static int is_parent_child(DWORD, DWORD);
+/* */
+static void close_alert(HANDLE);
+/* close any alerts hanging around */
+static void close_alerts();
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+static int intr = 0;
+static int cmdsrunning = 0;
+static void (* istat)( int );
+
+
+/* The list of commands we run. */
+static struct
+{
+ string action; /* buffer to hold action */
+ string target; /* buffer to hold target */
+ string command; /* buffer to hold command being invoked */
+
+ /* Temporary batch file used to execute the action when needed. */
+ char * tempfile_bat;
+
+ /* Pipes for communicating with the child process. Parent reads from (0),
+ * child writes to (1).
+ */
+ HANDLE pipe_out[ 2 ];
+ HANDLE pipe_err[ 2 ];
+
+ string buffer_out; /* buffer to hold stdout, if any */
+ string buffer_err; /* buffer to hold stderr, if any */
+
+ PROCESS_INFORMATION pi; /* running process information */
+ DWORD exit_code; /* executed command's exit code */
+ int exit_reason; /* reason why a command completed */
+
+ /* Function called when the command completes. */
+ void (* func)( void * closure, int status, timing_info *, char *, char * );
+
+ /* Opaque data passed back to the 'func' callback called when the command
+ * completes.
+ */
+ void * closure;
+}
+cmdtab[ MAXJOBS ] = { { 0 } };
+
+
+/*
+ * Execution unit tests.
+ */
+
+void execnt_unit_test()
+{
+#if !defined( NDEBUG )
+ /* vc6 preprocessor is broken, so assert with these strings gets confused.
+ * Use a table instead.
+ */
+ typedef struct test { char * command; int result; } test;
+ test tests[] = {
+ { "x", 0 },
+ { "x\n ", 0 },
+ { "x\ny", 1 },
+ { "x\n\n y", 1 },
+ { "echo x > foo.bar", 1 },
+ { "echo x < foo.bar", 1 },
+ { "echo x \">\" foo.bar", 0 },
+ { "echo x \"<\" foo.bar", 0 },
+ { "echo x \\\">\\\" foo.bar", 1 },
+ { "echo x \\\"<\\\" foo.bar", 1 } };
+ int i;
+ for ( i = 0; i < sizeof( tests ) / sizeof( *tests ); ++i )
+ assert( !can_spawn( tests[ i ].command ) == tests[ i ].result );
+
+ {
+ char * long_command = BJAM_MALLOC_ATOMIC( MAXLINE + 10 );
+ assert( long_command != 0 );
+ memset( long_command, 'x', MAXLINE + 9 );
+ long_command[ MAXLINE + 9 ] = 0;
+ assert( can_spawn( long_command ) == MAXLINE + 9 );
+ BJAM_FREE( long_command );
+ }
+
+ {
+ /* Work around vc6 bug; it doesn't like escaped string
+ * literals inside assert
+ */
+ char * * argv = string_to_args(" \"g++\" -c -I\"Foobar\"" );
+ char const expected[] = "-c -I\"Foobar\"";
+
+ assert( !strcmp( argv[ 0 ], "g++" ) );
+ assert( !strcmp( argv[ 1 ], expected ) );
+ free_argv( argv );
+ }
+#endif
+}
+
+
+/*
+ * exec_cmd() - launch an async command execution.
+ */
+
+void exec_cmd
+(
+ char * command,
+ void (* func)( void * closure, int status, timing_info *, char * invoked_command, char * command_output ),
+ void * closure,
+ LIST * shell,
+ char * action,
+ char * target
+)
+{
+ int slot;
+ int raw_cmd = 0 ;
+ char * argv_static[ MAXARGC + 1 ]; /* +1 for NULL */
+ char * * argv = argv_static;
+ char * p;
+ char * command_orig = command;
+
+ /* Check to see if we need to hack around the line-length limitation. Look
+ * for a JAMSHELL setting of "%", indicating that the command should be
+ * invoked directly.
+ */
+ if ( shell && !strcmp( shell->string, "%" ) && !list_next( shell ) )
+ {
+ raw_cmd = 1;
+ shell = 0;
+ }
+
+ /* Find a slot in the running commands table for this one. */
+ for ( slot = 0; slot < MAXJOBS; ++slot )
+ if ( !cmdtab[ slot ].pi.hProcess )
+ break;
+ if ( slot == MAXJOBS )
+ {
+ printf( "no slots for child!\n" );
+ exit( EXITBAD );
+ }
+
+ /* Compute the name of a temp batch file, for possible use. */
+ if ( !cmdtab[ slot ].tempfile_bat )
+ {
+ char const * tempdir = path_tmpdir();
+ DWORD procID = GetCurrentProcessId();
+
+ /* SVA - allocate 64 bytes extra just to be safe. */
+ cmdtab[ slot ].tempfile_bat = BJAM_MALLOC_ATOMIC( strlen( tempdir ) + 64 );
+
+ sprintf( cmdtab[ slot ].tempfile_bat, "%s\\jam%d-%02d.bat",
+ tempdir, procID, slot );
+ }
+
+ /* Trim leading, -ending- white space */
+ while ( *( command + 1 ) && isspace( *command ) )
+ ++command;
+
+ /* Write to .BAT file unless the line would be too long and it meets the
+ * other spawnability criteria.
+ */
+ if ( raw_cmd && ( can_spawn( command ) >= MAXLINE ) )
+ {
+ if ( DEBUG_EXECCMD )
+ printf("Executing raw command directly\n");
+ }
+ else
+ {
+ FILE * f = 0;
+ int tries = 0;
+ raw_cmd = 0;
+
+ /* Write command to bat file. For some reason this open can fail
+ * intermitently. But doing some retries works. Most likely this is due
+ * to a previously existing file of the same name that happens to be
+ * opened by an active virus scanner. Pointed out and fixed by Bronek
+ * Kozicki.
+ */
+ for ( ; !f && ( tries < 4 ); ++tries )
+ {
+ f = fopen( cmdtab[ slot ].tempfile_bat, "w" );
+ if ( !f && ( tries < 4 ) ) Sleep( 250 );
+ }
+ if ( !f )
+ {
+ printf( "failed to write command file!\n" );
+ exit( EXITBAD );
+ }
+ fputs( command, f );
+ fclose( f );
+
+ command = cmdtab[ slot ].tempfile_bat;
+
+ if ( DEBUG_EXECCMD )
+ {
+ if ( shell )
+ printf( "using user-specified shell: %s", shell->string );
+ else
+ printf( "Executing through .bat file\n" );
+ }
+ }
+
+ /* Formulate argv; If shell was defined, be prepared for % and ! subs.
+ * Otherwise, use stock cmd.exe.
+ */
+ if ( shell )
+ {
+ int i;
+ char jobno[ 4 ];
+ int gotpercent = 0;
+
+ sprintf( jobno, "%d", slot + 1 );
+
+ for ( i = 0; shell && ( i < MAXARGC ); ++i, shell = list_next( shell ) )
+ {
+ switch ( shell->string[ 0 ] )
+ {
+ case '%': argv[ i ] = command; ++gotpercent; break;
+ case '!': argv[ i ] = jobno; break;
+ default : argv[ i ] = shell->string;
+ }
+ if ( DEBUG_EXECCMD )
+ printf( "argv[%d] = '%s'\n", i, argv[ i ] );
+ }
+
+ if ( !gotpercent )
+ argv[ i++ ] = command;
+
+ argv[ i ] = 0;
+ }
+ else if ( raw_cmd )
+ {
+ argv = string_to_args( command );
+ }
+ else
+ {
+ argv[ 0 ] = "cmd.exe";
+ argv[ 1 ] = "/Q/C"; /* anything more is non-portable */
+ argv[ 2 ] = command;
+ argv[ 3 ] = 0;
+ }
+
+ /* Catch interrupts whenever commands are running. */
+ if ( !cmdsrunning++ )
+ istat = signal( SIGINT, onintr );
+
+ /* Start the command. */
+ {
+ SECURITY_ATTRIBUTES sa
+ = { sizeof( SECURITY_ATTRIBUTES ), 0, 0 };
+ SECURITY_DESCRIPTOR sd;
+ STARTUPINFO si
+ = { sizeof( STARTUPINFO ), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ string cmd;
+
+ /* Init the security data. */
+ InitializeSecurityDescriptor( &sd, SECURITY_DESCRIPTOR_REVISION );
+ SetSecurityDescriptorDacl( &sd, TRUE, NULL, FALSE );
+ sa.lpSecurityDescriptor = &sd;
+ sa.bInheritHandle = TRUE;
+
+ /* Create the stdout, which is also the merged out + err, pipe. */
+ if ( !CreatePipe( &cmdtab[ slot ].pipe_out[ 0 ],
+ &cmdtab[ slot ].pipe_out[ 1 ], &sa, 0 ) )
+ {
+ perror( "CreatePipe" );
+ exit( EXITBAD );
+ }
+
+ /* Create the stdout, which is also the merged out+err, pipe. */
+ if ( globs.pipe_action == 2 )
+ {
+ if ( !CreatePipe( &cmdtab[ slot ].pipe_err[ 0 ],
+ &cmdtab[ slot ].pipe_err[ 1 ], &sa, 0 ) )
+ {
+ perror( "CreatePipe" );
+ exit( EXITBAD );
+ }
+ }
+
+ /* Set handle inheritance off for the pipe ends the parent reads from. */
+ SetHandleInformation( cmdtab[ slot ].pipe_out[ 0 ], HANDLE_FLAG_INHERIT, 0 );
+ if ( globs.pipe_action == 2 )
+ SetHandleInformation( cmdtab[ slot ].pipe_err[ 0 ], HANDLE_FLAG_INHERIT, 0 );
+
+ /* Hide the child window, if any. */
+ si.dwFlags |= STARTF_USESHOWWINDOW;
+ si.wShowWindow = SW_HIDE;
+
+ /* Set the child outputs to the pipes. */
+ si.dwFlags |= STARTF_USESTDHANDLES;
+ si.hStdOutput = cmdtab[ slot ].pipe_out[ 1 ];
+ if ( globs.pipe_action == 2 )
+ {
+ /* Pipe stderr to the action error output. */
+ si.hStdError = cmdtab[ slot ].pipe_err[ 1 ];
+ }
+ else if ( globs.pipe_action == 1 )
+ {
+ /* Pipe stderr to the console error output. */
+ si.hStdError = GetStdHandle( STD_ERROR_HANDLE );
+ }
+ else
+ {
+ /* Pipe stderr to the action merged output. */
+ si.hStdError = cmdtab[ slot ].pipe_out[ 1 ];
+ }
+
+ /* Let the child inherit stdin, as some commands assume it's available. */
+ si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+
+ /* Save the operation for exec_wait() to find. */
+ cmdtab[ slot ].func = func;
+ cmdtab[ slot ].closure = closure;
+ if ( action && target )
+ {
+ string_copy( &cmdtab[ slot ].action, action );
+ string_copy( &cmdtab[ slot ].target, target );
+ }
+ else
+ {
+ string_free( &cmdtab[ slot ].action );
+ string_new ( &cmdtab[ slot ].action );
+ string_free( &cmdtab[ slot ].target );
+ string_new ( &cmdtab[ slot ].target );
+ }
+ string_copy( &cmdtab[ slot ].command, command_orig );
+
+ /* Put together the command we run. */
+ {
+ char * * argp = argv;
+ string_new( &cmd );
+ string_copy( &cmd, *(argp++) );
+ while ( *argp )
+ {
+ string_push_back( &cmd, ' ' );
+ string_append( &cmd, *(argp++) );
+ }
+ }
+
+ /* Create output buffers. */
+ string_new( &cmdtab[ slot ].buffer_out );
+ string_new( &cmdtab[ slot ].buffer_err );
+
+ /* Run the command by creating a sub-process for it. */
+ if (
+ ! CreateProcess(
+ NULL , /* application name */
+ cmd.value , /* command line */
+ NULL , /* process attributes */
+ NULL , /* thread attributes */
+ TRUE , /* inherit handles */
+ CREATE_NEW_PROCESS_GROUP, /* create flags */
+ NULL , /* env vars, null inherits env */
+ NULL , /* current dir, null is our */
+ /* current dir */
+ &si , /* startup info */
+ &cmdtab[ slot ].pi /* child process info, if created */
+ )
+ )
+ {
+ perror( "CreateProcess" );
+ exit( EXITBAD );
+ }
+
+ /* Clean up temporary stuff. */
+ string_free( &cmd );
+ }
+
+ /* Wait until we are under the limit of concurrent commands. Do not trust
+ * globs.jobs alone.
+ */
+ while ( ( cmdsrunning >= MAXJOBS ) || ( cmdsrunning >= globs.jobs ) )
+ if ( !exec_wait() )
+ break;
+
+ if ( argv != argv_static )
+ free_argv( argv );
+}
+
+
+/*
+ * exec_wait()
+ * * wait and drive at most one execution completion.
+ * * waits for one command to complete, while processing the i/o for all
+ * ongoing commands.
+ *
+ * Returns 0 if called when there were no more commands being executed or 1
+ * otherwise.
+ */
+
+int exec_wait()
+{
+ int i = -1;
+
+ /* Handle naive make1() which does not know if cmds are running. */
+ if ( !cmdsrunning )
+ return 0;
+
+ /* Wait for a command to complete, while snarfing up any output. */
+ do
+ {
+ /* Check for a complete command, briefly. */
+ i = try_wait(500);
+ /* Read in the output of all running commands. */
+ read_output();
+ /* Close out pending debug style dialogs. */
+ close_alerts();
+ /* Check if a command ran out of time. */
+ if ( i < 0 ) i = try_kill_one();
+ }
+ while ( i < 0 );
+
+ /* We have a command... process it. */
+ --cmdsrunning;
+ {
+ timing_info time;
+ int rstat;
+
+ /* The time data for the command. */
+ record_times( cmdtab[ i ].pi.hProcess, &time );
+
+ /* Clear the temp file. */
+ if ( cmdtab[ i ].tempfile_bat )
+ {
+ unlink( cmdtab[ i ].tempfile_bat );
+ BJAM_FREE( cmdtab[ i ].tempfile_bat );
+ cmdtab[ i ].tempfile_bat = NULL;
+ }
+
+ /* Find out the process exit code. */
+ GetExitCodeProcess( cmdtab[ i ].pi.hProcess, &cmdtab[ i ].exit_code );
+
+ /* The dispossition of the command. */
+ if ( intr )
+ rstat = EXEC_CMD_INTR;
+ else if ( cmdtab[ i ].exit_code != 0 )
+ rstat = EXEC_CMD_FAIL;
+ else
+ rstat = EXEC_CMD_OK;
+
+ /* Output the action block. */
+ out_action(
+ cmdtab[ i ].action.size > 0 ? cmdtab[ i ].action.value : 0,
+ cmdtab[ i ].target.size > 0 ? cmdtab[ i ].target.value : 0,
+ cmdtab[ i ].command.size > 0 ? cmdtab[ i ].command.value : 0,
+ cmdtab[ i ].buffer_out.size > 0 ? cmdtab[ i ].buffer_out.value : 0,
+ cmdtab[ i ].buffer_err.size > 0 ? cmdtab[ i ].buffer_err.value : 0,
+ cmdtab[ i ].exit_reason );
+
+ /* Call the callback, may call back to jam rule land. Assume -p0 in
+ * effect so only pass buffer containing merged output.
+ */
+ (*cmdtab[ i ].func)(
+ cmdtab[ i ].closure,
+ rstat,
+ &time,
+ cmdtab[ i ].command.value,
+ cmdtab[ i ].buffer_out.value );
+
+ /* Clean up the command data, process, etc. */
+ string_free( &cmdtab[ i ].action ); string_new( &cmdtab[ i ].action );
+ string_free( &cmdtab[ i ].target ); string_new( &cmdtab[ i ].target );
+ string_free( &cmdtab[ i ].command ); string_new( &cmdtab[ i ].command );
+ if ( cmdtab[ i ].pi.hProcess ) { CloseHandle( cmdtab[ i ].pi.hProcess ); cmdtab[ i ].pi.hProcess = 0; }
+ if ( cmdtab[ i ].pi.hThread ) { CloseHandle( cmdtab[ i ].pi.hThread ); cmdtab[ i ].pi.hThread = 0; }
+ if ( cmdtab[ i ].pipe_out[ 0 ] ) { CloseHandle( cmdtab[ i ].pipe_out[ 0 ] ); cmdtab[ i ].pipe_out[ 0 ] = 0; }
+ if ( cmdtab[ i ].pipe_out[ 1 ] ) { CloseHandle( cmdtab[ i ].pipe_out[ 1 ] ); cmdtab[ i ].pipe_out[ 1 ] = 0; }
+ if ( cmdtab[ i ].pipe_err[ 0 ] ) { CloseHandle( cmdtab[ i ].pipe_err[ 0 ] ); cmdtab[ i ].pipe_err[ 0 ] = 0; }
+ if ( cmdtab[ i ].pipe_err[ 1 ] ) { CloseHandle( cmdtab[ i ].pipe_err[ 1 ] ); cmdtab[ i ].pipe_err[ 1 ] = 0; }
+ string_free( &cmdtab[ i ].buffer_out ); string_new( &cmdtab[ i ].buffer_out );
+ string_free( &cmdtab[ i ].buffer_err ); string_new( &cmdtab[ i ].buffer_err );
+ cmdtab[ i ].exit_code = 0;
+ cmdtab[ i ].exit_reason = EXIT_OK;
+ }
+
+ return 1;
+}
+
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+static void free_argv( char * * args )
+{
+ BJAM_FREE( args[ 0 ] );
+ BJAM_FREE( args );
+}
+
+
+/*
+ * For more details on Windows cmd.exe shell command-line length limitations see
+ * the following MSDN article:
+ * http://support.microsoft.com/default.aspx?scid=kb;en-us;830473
+ */
+
+int maxline()
+{
+ OSVERSIONINFO os_info;
+ os_info.dwOSVersionInfoSize = sizeof( os_info );
+ GetVersionEx( &os_info );
+
+ if ( os_info.dwMajorVersion >= 5 ) return 8191; /* XP > */
+ if ( os_info.dwMajorVersion == 4 ) return 2047; /* NT 4.x */
+ return 996; /* NT 3.5.1 */
+}
+
+
+/*
+ * Convert a command string into arguments for spawnvp(). The original code,
+ * inherited from ftjam, tried to break up every argument on the command-line,
+ * dealing with quotes, but that is really a waste of time on Win32, at least.
+ * It turns out that all you need to do is get the raw path to the executable in
+ * the first argument to spawnvp(), and you can pass all the rest of the
+ * command-line arguments to spawnvp() in one, un-processed string.
+ *
+ * New strategy: break the string in at most one place.
+ */
+
+static char * * string_to_args( char const * string )
+{
+ int src_len;
+ int in_quote;
+ char * line;
+ char const * src;
+ char * dst;
+ char * * argv;
+
+ /* Drop leading and trailing whitespace if any. */
+ while ( isspace( *string ) )
+ ++string;
+
+ src_len = strlen( string );
+ while ( ( src_len > 0 ) && isspace( string[ src_len - 1 ] ) )
+ --src_len;
+
+ /* Copy the input string into a buffer we can modify. */
+ line = (char *)BJAM_MALLOC_ATOMIC( src_len + 1 );
+ if ( !line )
+ return 0;
+
+ /* Allocate the argv array.
+ * element 0: stores the path to the executable
+ * element 1: stores the command-line arguments to the executable
+ * element 2: NULL terminator
+ */
+ argv = (char * *)BJAM_MALLOC( 3 * sizeof( char * ) );
+ if ( !argv )
+ {
+ BJAM_FREE( line );
+ return 0;
+ }
+
+ /* Strip quotes from the first command-line argument and find where it ends.
+ * Quotes are illegal in Win32 pathnames, so we do not need to worry about
+ * preserving escaped quotes here. Spaces can not be escaped in Win32, only
+ * enclosed in quotes, so removing backslash escapes is also a non-issue.
+ */
+ in_quote = 0;
+ for ( src = string, dst = line ; *src; ++src )
+ {
+ if ( *src == '"' )
+ in_quote = !in_quote;
+ else if ( !in_quote && isspace( *src ) )
+ break;
+ else
+ *dst++ = *src;
+ }
+ *dst++ = 0;
+ argv[ 0 ] = line;
+
+ /* Skip whitespace in src. */
+ while ( isspace( *src ) )
+ ++src;
+
+ argv[ 1 ] = dst;
+
+ /* Copy the rest of the arguments verbatim. */
+ src_len -= src - string;
+
+ /* Use strncat() because it appends a trailing nul. */
+ *dst = 0;
+ strncat( dst, src, src_len );
+
+ argv[ 2 ] = 0;
+
+ return argv;
+}
+
+
+static void onintr( int disp )
+{
+ ++intr;
+ printf( "...interrupted\n" );
+}
+
+
+/*
+ * can_spawn() - If the command is suitable for execution via spawnvp(), return
+ * a number >= the number of characters it would occupy on the command-line.
+ * Otherwise, return zero.
+ */
+
+long can_spawn( char * command )
+{
+ char * p;
+ char inquote = 0;
+
+ /* Move to the first non-whitespace. */
+ command += strspn( command, " \t" );
+
+ p = command;
+
+ /* Look for newlines and unquoted i/o redirection. */
+ do
+ {
+ p += strcspn( p, "'\n\"<>|" );
+
+ switch ( *p )
+ {
+ case '\n':
+ /* Skip over any following spaces. */
+ while ( isspace( *p ) )
+ ++p;
+ /* Must use a .bat file if there is anything significant following
+ * the newline.
+ */
+ if ( *p )
+ return 0;
+ break;
+
+ case '"':
+ case '\'':
+ if ( ( p > command ) && ( p[ -1 ] != '\\' ) )
+ {
+ if ( inquote == *p )
+ inquote = 0;
+ else if ( inquote == 0 )
+ inquote = *p;
+ }
+ ++p;
+ break;
+
+ case '<':
+ case '>':
+ case '|':
+ if ( !inquote )
+ return 0;
+ ++p;
+ break;
+ }
+ }
+ while ( *p );
+
+ /* Return the number of characters the command will occupy. */
+ return p - command;
+}
+
+
+/* 64-bit arithmetic helpers. */
+
+/* Compute the carry bit from the addition of two 32-bit unsigned numbers. */
+#define add_carry_bit( a, b ) ( (((a) | (b)) >> 31) & (~((a) + (b)) >> 31) & 0x1 )
+
+/* Compute the high 32 bits of the addition of two 64-bit unsigned numbers, h1l1 and h2l2. */
+#define add_64_hi( h1, l1, h2, l2 ) ((h1) + (h2) + add_carry_bit(l1, l2))
+
+
+/*
+ * Add two 64-bit unsigned numbers, h1l1 and h2l2.
+ */
+
+static FILETIME add_64
+(
+ unsigned long h1, unsigned long l1,
+ unsigned long h2, unsigned long l2
+)
+{
+ FILETIME result;
+ result.dwLowDateTime = l1 + l2;
+ result.dwHighDateTime = add_64_hi( h1, l1, h2, l2 );
+ return result;
+}
+
+
+static FILETIME add_FILETIME( FILETIME t1, FILETIME t2 )
+{
+ return add_64( t1.dwHighDateTime, t1.dwLowDateTime, t2.dwHighDateTime,
+ t2.dwLowDateTime );
+}
+
+
+static FILETIME negate_FILETIME( FILETIME t )
+{
+ /* 2s complement negation */
+ return add_64( ~t.dwHighDateTime, ~t.dwLowDateTime, 0, 1 );
+}
+
+
+/*
+ * Convert a FILETIME to a number of seconds.
+ */
+
+static double filetime_seconds( FILETIME t )
+{
+ return t.dwHighDateTime * ( (double)( 1UL << 31 ) * 2.0 * 1.0e-7 ) + t.dwLowDateTime * 1.0e-7;
+}
+
+
+/*
+ * What should be a simple conversion, turns out to be horribly complicated by
+ * the defficiencies of MSVC and the Win32 API.
+ */
+
+static time_t filetime_dt( FILETIME t_utc )
+{
+ static int calc_time_diff = 1;
+ static double time_diff;
+ if ( calc_time_diff )
+ {
+ struct tm t0_;
+ FILETIME f0_local;
+ FILETIME f0_;
+ SYSTEMTIME s0_;
+ GetSystemTime( &s0_ );
+ t0_.tm_year = s0_.wYear-1900;
+ t0_.tm_mon = s0_.wMonth-1;
+ t0_.tm_wday = s0_.wDayOfWeek;
+ t0_.tm_mday = s0_.wDay;
+ t0_.tm_hour = s0_.wHour;
+ t0_.tm_min = s0_.wMinute;
+ t0_.tm_sec = s0_.wSecond;
+ t0_.tm_isdst = 0;
+ SystemTimeToFileTime( &s0_, &f0_local );
+ LocalFileTimeToFileTime( &f0_local, &f0_ );
+ time_diff = filetime_seconds( f0_ ) - (double)mktime( &t0_ );
+ calc_time_diff = 0;
+ }
+ return ceil( filetime_seconds( t_utc ) - time_diff );
+}
+
+
+static void record_times( HANDLE process, timing_info * time )
+{
+ FILETIME creation;
+ FILETIME exit;
+ FILETIME kernel;
+ FILETIME user;
+ if ( GetProcessTimes( process, &creation, &exit, &kernel, &user ) )
+ {
+ time->system = filetime_seconds( kernel );
+ time->user = filetime_seconds( user );
+ time->start = filetime_dt ( creation );
+ time->end = filetime_dt ( exit );
+ }
+}
+
+
+#define IO_BUFFER_SIZE ( 16 * 1024 )
+
+static char ioBuffer[ IO_BUFFER_SIZE + 1 ];
+
+
+static void read_pipe
+(
+ HANDLE in, /* the pipe to read from */
+ string * out
+)
+{
+ DWORD bytesInBuffer = 0;
+ DWORD bytesAvailable = 0;
+
+ do
+ {
+ /* check if we have any data to read */
+ if ( !PeekNamedPipe( in, ioBuffer, IO_BUFFER_SIZE, &bytesInBuffer, &bytesAvailable, NULL ) )
+ bytesAvailable = 0;
+
+ /* read in the available data */
+ if ( bytesAvailable > 0 )
+ {
+ /* we only read in the available bytes, to avoid blocking */
+ if ( ReadFile( in, ioBuffer,
+ bytesAvailable <= IO_BUFFER_SIZE ? bytesAvailable : IO_BUFFER_SIZE,
+ &bytesInBuffer, NULL ) )
+ {
+ if ( bytesInBuffer > 0 )
+ {
+ /* Clean up some illegal chars. */
+ int i;
+ for ( i = 0; i < bytesInBuffer; ++i )
+ {
+ if ( ( (unsigned char)ioBuffer[ i ] < 1 ) )
+ ioBuffer[ i ] = '?';
+ }
+ /* Null, terminate. */
+ ioBuffer[ bytesInBuffer ] = '\0';
+ /* Append to the output. */
+ string_append( out, ioBuffer );
+ /* Subtract what we read in. */
+ bytesAvailable -= bytesInBuffer;
+ }
+ else
+ {
+ /* Likely read a error, bail out. */
+ bytesAvailable = 0;
+ }
+ }
+ else
+ {
+ /* Definitely read a error, bail out. */
+ bytesAvailable = 0;
+ }
+ }
+ }
+ while ( bytesAvailable > 0 );
+}
+
+
+static void read_output()
+{
+ int i;
+ for ( i = 0; i < globs.jobs && i < MAXJOBS; ++i )
+ {
+ /* Read stdout data. */
+ if ( cmdtab[ i ].pipe_out[ 0 ] )
+ read_pipe( cmdtab[ i ].pipe_out[ 0 ], & cmdtab[ i ].buffer_out );
+ /* Read stderr data. */
+ if ( cmdtab[ i ].pipe_err[ 0 ] )
+ read_pipe( cmdtab[ i ].pipe_err[ 0 ], & cmdtab[ i ].buffer_err );
+ }
+}
+
+
+/*
+ * Waits for a single child process command to complete, or the timeout,
+ * whichever comes first. Returns the index of the completed command in the
+ * cmdtab array, or -1.
+ */
+
+static int try_wait( int timeoutMillis )
+{
+ int i;
+ int num_active;
+ int wait_api_result;
+ HANDLE active_handles[ MAXJOBS ];
+ int active_procs[ MAXJOBS ];
+
+ /* Prepare a list of all active processes to wait for. */
+ for ( num_active = 0, i = 0; i < globs.jobs; ++i )
+ {
+ if ( cmdtab[ i ].pi.hProcess )
+ {
+ active_handles[ num_active ] = cmdtab[ i ].pi.hProcess;
+ active_procs[ num_active ] = i;
+ ++num_active;
+ }
+ }
+
+ /* Wait for a child to complete, or for our timeout window to expire. */
+ wait_api_result = WaitForMultipleObjects( num_active, active_handles,
+ FALSE, timeoutMillis );
+ if ( ( WAIT_OBJECT_0 <= wait_api_result ) &&
+ ( wait_api_result < WAIT_OBJECT_0 + num_active ) )
+ {
+ /* Rerminated process detected - return its index. */
+ return active_procs[ wait_api_result - WAIT_OBJECT_0 ];
+ }
+
+ /* Timeout. */
+ return -1;
+}
+
+
+static int try_kill_one()
+{
+ /* Only need to check if a timeout was specified with the -l option. */
+ if ( globs.timeout > 0 )
+ {
+ int i;
+ for ( i = 0; i < globs.jobs; ++i )
+ {
+ double t = running_time( cmdtab[ i ].pi.hProcess );
+ if ( t > (double)globs.timeout )
+ {
+ /* The job may have left an alert dialog around, try and get rid
+ * of it before killing
+ */
+ close_alert( cmdtab[ i ].pi.hProcess );
+ /* We have a "runaway" job, kill it. */
+ kill_process_tree( 0, cmdtab[ i ].pi.hProcess );
+ /* And return it marked as a timeout. */
+ cmdtab[ i ].exit_reason = EXIT_TIMEOUT;
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
+
+static void close_alerts()
+{
+ /* We only attempt this every 5 seconds, or so, because it is not a cheap
+ * operation, and we will catch the alerts eventually. This check uses
+ * floats as some compilers define CLOCKS_PER_SEC as a float or double.
+ */
+ if ( ( (float)clock() / (float)( CLOCKS_PER_SEC * 5 ) ) < ( 1.0 / 5.0 ) )
+ {
+ int i;
+ for ( i = 0; i < globs.jobs; ++i )
+ close_alert( cmdtab[ i ].pi.hProcess );
+ }
+}
+
+
+/*
+ * Calc the current running time of an *active* process.
+ */
+
+static double running_time( HANDLE process )
+{
+ FILETIME creation;
+ FILETIME exit;
+ FILETIME kernel;
+ FILETIME user;
+ FILETIME current;
+ if ( GetProcessTimes( process, &creation, &exit, &kernel, &user ) )
+ {
+ /* Compute the elapsed time. */
+ GetSystemTimeAsFileTime( &current );
+ return filetime_seconds( add_FILETIME( current,
+ negate_FILETIME( creation ) ) );
+ }
+ return 0.0;
+}
+
+
+/* It is just stupidly silly that one has to do this. */
+typedef struct PROCESS_BASIC_INFORMATION__
+{
+ LONG ExitStatus;
+ PVOID PebBaseAddress;
+ ULONG AffinityMask;
+ LONG BasePriority;
+ ULONG UniqueProcessId;
+ ULONG InheritedFromUniqueProcessId;
+} PROCESS_BASIC_INFORMATION_;
+typedef LONG (__stdcall * NtQueryInformationProcess__)(
+ HANDLE ProcessHandle,
+ LONG ProcessInformationClass,
+ PVOID ProcessInformation,
+ ULONG ProcessInformationLength,
+ PULONG ReturnLength);
+static NtQueryInformationProcess__ NtQueryInformationProcess_ = NULL;
+static HMODULE NTDLL_ = NULL;
+DWORD get_process_id( HANDLE process )
+{
+ PROCESS_BASIC_INFORMATION_ pinfo;
+ if ( !NtQueryInformationProcess_ )
+ {
+ if ( ! NTDLL_ )
+ NTDLL_ = GetModuleHandleA( "ntdll" );
+ if ( NTDLL_ )
+ NtQueryInformationProcess_
+ = (NtQueryInformationProcess__)GetProcAddress( NTDLL_, "NtQueryInformationProcess" );
+ }
+ if ( NtQueryInformationProcess_ )
+ {
+ LONG r = (*NtQueryInformationProcess_)( process,
+ /* ProcessBasicInformation == */ 0, &pinfo,
+ sizeof( PROCESS_BASIC_INFORMATION_ ), NULL );
+ return pinfo.UniqueProcessId;
+ }
+ return 0;
+}
+
+
+/*
+ * Not really optimal, or efficient, but it is easier this way, and it is not
+ * like we are going to be killing thousands, or even tens of processes.
+ */
+
+static void kill_process_tree( DWORD pid, HANDLE process )
+{
+ HANDLE process_snapshot_h = INVALID_HANDLE_VALUE;
+ if ( !pid )
+ pid = get_process_id( process );
+ process_snapshot_h = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
+
+ if ( INVALID_HANDLE_VALUE != process_snapshot_h )
+ {
+ BOOL ok = TRUE;
+ PROCESSENTRY32 pinfo;
+ pinfo.dwSize = sizeof( PROCESSENTRY32 );
+ for (
+ ok = Process32First( process_snapshot_h, &pinfo );
+ ok == TRUE;
+ ok = Process32Next( process_snapshot_h, &pinfo ) )
+ {
+ if ( pinfo.th32ParentProcessID == pid )
+ {
+ /* Found a child, recurse to kill it and anything else below it.
+ */
+ HANDLE ph = OpenProcess( PROCESS_ALL_ACCESS, FALSE,
+ pinfo.th32ProcessID );
+ if ( NULL != ph )
+ {
+ kill_process_tree( pinfo.th32ProcessID, ph );
+ CloseHandle( ph );
+ }
+ }
+ }
+ CloseHandle( process_snapshot_h );
+ }
+ /* Now that the children are all dead, kill the root. */
+ TerminateProcess( process, -2 );
+}
+
+
+static double creation_time( HANDLE process )
+{
+ FILETIME creation;
+ FILETIME exit;
+ FILETIME kernel;
+ FILETIME user;
+ FILETIME current;
+ return GetProcessTimes( process, &creation, &exit, &kernel, &user )
+ ? filetime_seconds( creation )
+ : 0.0;
+}
+
+
+/*
+ * Recursive check if first process is parent (directly or indirectly) of the
+ * second one. Both processes are passed as process ids, not handles. Special
+ * return value 2 means that the second process is smss.exe and its parent
+ * process is System (first argument is ignored).
+ */
+
+static int is_parent_child( DWORD parent, DWORD child )
+{
+ HANDLE process_snapshot_h = INVALID_HANDLE_VALUE;
+
+ if ( !child )
+ return 0;
+ if ( parent == child )
+ return 1;
+
+ process_snapshot_h = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
+ if ( INVALID_HANDLE_VALUE != process_snapshot_h )
+ {
+ BOOL ok = TRUE;
+ PROCESSENTRY32 pinfo;
+ pinfo.dwSize = sizeof( PROCESSENTRY32 );
+ for (
+ ok = Process32First( process_snapshot_h, &pinfo );
+ ok == TRUE;
+ ok = Process32Next( process_snapshot_h, &pinfo ) )
+ {
+ if ( pinfo.th32ProcessID == child )
+ {
+ /* Unfortunately, process ids are not really unique. There might
+ * be spurious "parent and child" relationship match between two
+ * non-related processes if real parent process of a given
+ * process has exited (while child process kept running as an
+ * "orphan") and the process id of such parent process has been
+ * reused by internals of the operating system when creating
+ * another process.
+ *
+ * Thus additional check is needed - process creation time. This
+ * check may fail (i.e. return 0) for system processes due to
+ * insufficient privileges, and that is OK.
+ */
+ double tchild = 0.0;
+ double tparent = 0.0;
+ HANDLE hchild = OpenProcess( PROCESS_QUERY_INFORMATION, FALSE, pinfo.th32ProcessID );
+ CloseHandle( process_snapshot_h );
+
+ /* csrss.exe may display message box like following:
+ * xyz.exe - Unable To Locate Component
+ * This application has failed to start because
+ * boost_foo-bar.dll was not found. Re-installing the
+ * application may fix the problem
+ * This actually happens when starting test process that depends
+ * on a dynamic library which failed to build. We want to
+ * automatically close these message boxes even though csrss.exe
+ * is not our child process. We may depend on the fact that (in
+ * all current versions of Windows) csrss.exe is directly child
+ * of the smss.exe process, which in turn is directly child of
+ * the System process, which always has process id == 4. This
+ * check must be performed before comparison of process creation
+ * times.
+ */
+ if ( !stricmp( pinfo.szExeFile, "csrss.exe" ) &&
+ ( is_parent_child( parent, pinfo.th32ParentProcessID ) == 2 ) )
+ return 1;
+ if ( !stricmp( pinfo.szExeFile, "smss.exe" ) &&
+ ( pinfo.th32ParentProcessID == 4 ) )
+ return 2;
+
+ if ( hchild )
+ {
+ HANDLE hparent = OpenProcess( PROCESS_QUERY_INFORMATION,
+ FALSE, pinfo.th32ParentProcessID );
+ if ( hparent )
+ {
+ tchild = creation_time( hchild );
+ tparent = creation_time( hparent );
+ CloseHandle( hparent );
+ }
+ CloseHandle( hchild );
+ }
+
+ /* Return 0 if one of the following is true:
+ * 1. we failed to read process creation time
+ * 2. child was created before alleged parent
+ */
+ if ( ( tchild == 0.0 ) || ( tparent == 0.0 ) ||
+ ( tchild < tparent ) )
+ return 0;
+
+ return is_parent_child( parent, pinfo.th32ParentProcessID ) & 1;
+ }
+ }
+
+ CloseHandle( process_snapshot_h );
+ }
+
+ return 0;
+}
+
+typedef struct PROCESS_HANDLE_ID { HANDLE h; DWORD pid; } PROCESS_HANDLE_ID;
+
+
+/*
+ * This function is called by the operating system for each topmost window.
+ */
+
+BOOL CALLBACK close_alert_window_enum( HWND hwnd, LPARAM lParam )
+{
+ char buf[ 7 ] = { 0 };
+ PROCESS_HANDLE_ID p = *( (PROCESS_HANDLE_ID *)lParam );
+ DWORD pid = 0;
+ DWORD tid = 0;
+
+ /* We want to find and close any window that:
+ * 1. is visible and
+ * 2. is a dialog and
+ * 3. is displayed by any of our child processes
+ */
+ if ( !IsWindowVisible( hwnd ) )
+ return TRUE;
+
+ if ( !GetClassNameA( hwnd, buf, sizeof( buf ) ) )
+ return TRUE; /* Failed to read class name; presume it is not a dialog. */
+
+ if ( strcmp( buf, "#32770" ) )
+ return TRUE; /* Not a dialog */
+
+ /* GetWindowThreadProcessId() returns 0 on error, otherwise thread id of
+ * window message pump thread.
+ */
+ tid = GetWindowThreadProcessId( hwnd, &pid );
+
+ if ( tid && is_parent_child( p.pid, pid ) )
+ {
+ /* Ask really nice. */
+ PostMessageA( hwnd, WM_CLOSE, 0, 0 );
+ /* Now wait and see if it worked. If not, insist. */
+ if ( WaitForSingleObject( p.h, 200 ) == WAIT_TIMEOUT )
+ {
+ PostThreadMessageA( tid, WM_QUIT, 0, 0 );
+ WaitForSingleObject( p.h, 300 );
+ }
+
+ /* Done, we do not want to check any other window now. */
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static void close_alert( HANDLE process )
+{
+ DWORD pid = get_process_id( process );
+ /* If process already exited or we just can not get its process id, do not
+ * go any further.
+ */
+ if ( pid )
+ {
+ PROCESS_HANDLE_ID p;
+ p.h = process;
+ p.pid = pid;
+ EnumWindows( &close_alert_window_enum, (LPARAM)&p );
+ }
+}
+
+#endif /* USE_EXECNT */
diff --git a/jam-files/engine/execunix.c b/jam-files/engine/execunix.c
new file mode 100644
index 00000000..ef9dba00
--- /dev/null
+++ b/jam-files/engine/execunix.c
@@ -0,0 +1,569 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ * Copyright 2007 Noel Belcourt.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+#include "jam.h"
+#include "lists.h"
+#include "execcmd.h"
+#include "output.h"
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h> /* needed for vfork(), _exit() prototypes */
+#include <sys/resource.h>
+#include <sys/times.h>
+#include <sys/wait.h>
+
+#if defined(sun) || defined(__sun) || defined(linux)
+ #include <wait.h>
+#endif
+
+#ifdef USE_EXECUNIX
+
+#include <sys/times.h>
+
+#if defined(__APPLE__)
+ #define NO_VFORK
+#endif
+
+#ifdef NO_VFORK
+ #define vfork() fork()
+#endif
+
+
+/*
+ * execunix.c - execute a shell script on UNIX/WinNT/OS2/AmigaOS
+ *
+ * If $(JAMSHELL) is defined, uses that to formulate execvp()/spawnvp().
+ * The default is:
+ *
+ * /bin/sh -c % [ on UNIX/AmigaOS ]
+ * cmd.exe /c % [ on OS2/WinNT ]
+ *
+ * Each word must be an individual element in a jam variable value.
+ *
+ * In $(JAMSHELL), % expands to the command string and ! expands to the slot
+ * number (starting at 1) for multiprocess (-j) invocations. If $(JAMSHELL) does
+ * not include a %, it is tacked on as the last argument.
+ *
+ * Do not just set JAMSHELL to /bin/sh or cmd.exe - it will not work!
+ *
+ * External routines:
+ * exec_cmd() - launch an async command execution.
+ * exec_wait() - wait and drive at most one execution completion.
+ *
+ * Internal routines:
+ * onintr() - bump intr to note command interruption.
+ *
+ * 04/08/94 (seiwald) - Coherent/386 support added.
+ * 05/04/94 (seiwald) - async multiprocess interface
+ * 01/22/95 (seiwald) - $(JAMSHELL) support
+ * 06/02/97 (gsar) - full async multiprocess support for Win32
+ */
+
+static clock_t tps = 0;
+static struct timeval tv;
+static int select_timeout = 0;
+static int intr = 0;
+static int cmdsrunning = 0;
+static struct tms old_time;
+
+#define OUT 0
+#define ERR 1
+
+static struct
+{
+ int pid; /* on win32, a real process handle */
+ int fd[2]; /* file descriptors for stdout and stderr */
+ FILE *stream[2]; /* child's stdout (0) and stderr (1) file stream */
+ clock_t start_time; /* start time of child process */
+ int exit_reason; /* termination status */
+ int action_length; /* length of action string */
+ int target_length; /* length of target string */
+ char *action; /* buffer to hold action and target invoked */
+ char *target; /* buffer to hold action and target invoked */
+ char *command; /* buffer to hold command being invoked */
+ char *buffer[2]; /* buffer to hold stdout and stderr, if any */
+ void (*func)( void *closure, int status, timing_info*, char *, char * );
+ void *closure;
+ time_t start_dt; /* start of command timestamp */
+} cmdtab[ MAXJOBS ] = {{0}};
+
+/*
+ * onintr() - bump intr to note command interruption
+ */
+
+void onintr( int disp )
+{
+ ++intr;
+ printf( "...interrupted\n" );
+}
+
+
+/*
+ * exec_cmd() - launch an async command execution.
+ */
+
+void exec_cmd
+(
+ char * string,
+ void (*func)( void *closure, int status, timing_info*, char *, char * ),
+ void * closure,
+ LIST * shell,
+ char * action,
+ char * target
+)
+{
+ static int initialized = 0;
+ int out[2];
+ int err[2];
+ int slot;
+ int len;
+ char * argv[ MAXARGC + 1 ]; /* +1 for NULL */
+
+ /* Find a slot in the running commands table for this one. */
+ for ( slot = 0; slot < MAXJOBS; ++slot )
+ if ( !cmdtab[ slot ].pid )
+ break;
+
+ if ( slot == MAXJOBS )
+ {
+ printf( "no slots for child!\n" );
+ exit( EXITBAD );
+ }
+
+ /* Forumulate argv. If shell was defined, be prepared for % and ! subs.
+ * Otherwise, use stock /bin/sh on unix or cmd.exe on NT.
+ */
+ if ( shell )
+ {
+ int i;
+ char jobno[4];
+ int gotpercent = 0;
+
+ sprintf( jobno, "%d", slot + 1 );
+
+ for ( i = 0; shell && i < MAXARGC; ++i, shell = list_next( shell ) )
+ {
+ switch ( shell->string[0] )
+ {
+ case '%': argv[ i ] = string; ++gotpercent; break;
+ case '!': argv[ i ] = jobno; break;
+ default : argv[ i ] = shell->string;
+ }
+ if ( DEBUG_EXECCMD )
+ printf( "argv[%d] = '%s'\n", i, argv[ i ] );
+ }
+
+ if ( !gotpercent )
+ argv[ i++ ] = string;
+
+ argv[ i ] = 0;
+ }
+ else
+ {
+ argv[ 0 ] = "/bin/sh";
+ argv[ 1 ] = "-c";
+ argv[ 2 ] = string;
+ argv[ 3 ] = 0;
+ }
+
+ /* Increment jobs running. */
+ ++cmdsrunning;
+
+ /* Save off actual command string. */
+ cmdtab[ slot ].command = BJAM_MALLOC_ATOMIC( strlen( string ) + 1 );
+ strcpy( cmdtab[ slot ].command, string );
+
+ /* Initialize only once. */
+ if ( !initialized )
+ {
+ times( &old_time );
+ initialized = 1;
+ }
+
+ /* Create pipes from child to parent. */
+ {
+ if ( pipe( out ) < 0 )
+ exit( EXITBAD );
+
+ if ( pipe( err ) < 0 )
+ exit( EXITBAD );
+ }
+
+ /* Start the command */
+
+ cmdtab[ slot ].start_dt = time(0);
+
+ if ( 0 < globs.timeout )
+ {
+ /*
+ * Handle hung processes by manually tracking elapsed time and signal
+ * process when time limit expires.
+ */
+ struct tms buf;
+ cmdtab[ slot ].start_time = times( &buf );
+
+ /* Make a global, only do this once. */
+ if ( tps == 0 ) tps = sysconf( _SC_CLK_TCK );
+ }
+
+ if ( ( cmdtab[ slot ].pid = vfork() ) == 0 )
+ {
+ int pid = getpid();
+
+ close( out[0] );
+ close( err[0] );
+
+ dup2( out[1], STDOUT_FILENO );
+
+ if ( globs.pipe_action == 0 )
+ dup2( out[1], STDERR_FILENO );
+ else
+ dup2( err[1], STDERR_FILENO );
+
+ close( out[1] );
+ close( err[1] );
+
+ /* Make this process a process group leader so that when we kill it, all
+ * child processes of this process are terminated as well. We use
+ * killpg(pid, SIGKILL) to kill the process group leader and all its
+ * children.
+ */
+ if ( 0 < globs.timeout )
+ {
+ struct rlimit r_limit;
+ r_limit.rlim_cur = globs.timeout;
+ r_limit.rlim_max = globs.timeout;
+ setrlimit( RLIMIT_CPU, &r_limit );
+ }
+ setpgid( pid,pid );
+ execvp( argv[0], argv );
+ perror( "execvp" );
+ _exit( 127 );
+ }
+ else if ( cmdtab[ slot ].pid == -1 )
+ {
+ perror( "vfork" );
+ exit( EXITBAD );
+ }
+
+ setpgid( cmdtab[ slot ].pid, cmdtab[ slot ].pid );
+
+ /* close write end of pipes */
+ close( out[1] );
+ close( err[1] );
+
+ /* set both file descriptors to non-blocking */
+ fcntl(out[0], F_SETFL, O_NONBLOCK);
+ fcntl(err[0], F_SETFL, O_NONBLOCK);
+
+ /* child writes stdout to out[1], parent reads from out[0] */
+ cmdtab[ slot ].fd[ OUT ] = out[0];
+ cmdtab[ slot ].stream[ OUT ] = fdopen( cmdtab[ slot ].fd[ OUT ], "rb" );
+ if ( cmdtab[ slot ].stream[ OUT ] == NULL )
+ {
+ perror( "fdopen" );
+ exit( EXITBAD );
+ }
+
+ /* child writes stderr to err[1], parent reads from err[0] */
+ if (globs.pipe_action == 0)
+ {
+ close(err[0]);
+ }
+ else
+ {
+ cmdtab[ slot ].fd[ ERR ] = err[0];
+ cmdtab[ slot ].stream[ ERR ] = fdopen( cmdtab[ slot ].fd[ ERR ], "rb" );
+ if ( cmdtab[ slot ].stream[ ERR ] == NULL )
+ {
+ perror( "fdopen" );
+ exit( EXITBAD );
+ }
+ }
+
+ /* Ensure enough room for rule and target name. */
+ if ( action && target )
+ {
+ len = strlen( action ) + 1;
+ if ( cmdtab[ slot ].action_length < len )
+ {
+ BJAM_FREE( cmdtab[ slot ].action );
+ cmdtab[ slot ].action = BJAM_MALLOC_ATOMIC( len );
+ cmdtab[ slot ].action_length = len;
+ }
+ strcpy( cmdtab[ slot ].action, action );
+ len = strlen( target ) + 1;
+ if ( cmdtab[ slot ].target_length < len )
+ {
+ BJAM_FREE( cmdtab[ slot ].target );
+ cmdtab[ slot ].target = BJAM_MALLOC_ATOMIC( len );
+ cmdtab[ slot ].target_length = len;
+ }
+ strcpy( cmdtab[ slot ].target, target );
+ }
+ else
+ {
+ BJAM_FREE( cmdtab[ slot ].action );
+ BJAM_FREE( cmdtab[ slot ].target );
+ cmdtab[ slot ].action = 0;
+ cmdtab[ slot ].target = 0;
+ cmdtab[ slot ].action_length = 0;
+ cmdtab[ slot ].target_length = 0;
+ }
+
+ /* Save the operation for exec_wait() to find. */
+ cmdtab[ slot ].func = func;
+ cmdtab[ slot ].closure = closure;
+
+ /* Wait until we are under the limit of concurrent commands. Do not trust
+ * globs.jobs alone.
+ */
+ while ( ( cmdsrunning >= MAXJOBS ) || ( cmdsrunning >= globs.jobs ) )
+ if ( !exec_wait() )
+ break;
+}
+
+
+/* Returns 1 if file is closed, 0 if descriptor is still live.
+ *
+ * i is index into cmdtab
+ *
+ * s (stream) indexes:
+ * - cmdtab[ i ].stream[ s ]
+ * - cmdtab[ i ].buffer[ s ]
+ * - cmdtab[ i ].fd [ s ]
+ */
+
+int read_descriptor( int i, int s )
+{
+ int ret;
+ int len;
+ char buffer[BUFSIZ];
+
+ while ( 0 < ( ret = fread( buffer, sizeof(char), BUFSIZ-1, cmdtab[ i ].stream[ s ] ) ) )
+ {
+ buffer[ret] = 0;
+ if ( !cmdtab[ i ].buffer[ s ] )
+ {
+ /* Never been allocated. */
+ cmdtab[ i ].buffer[ s ] = (char*)BJAM_MALLOC_ATOMIC( ret + 1 );
+ memcpy( cmdtab[ i ].buffer[ s ], buffer, ret + 1 );
+ }
+ else
+ {
+ /* Previously allocated. */
+ char * tmp = cmdtab[ i ].buffer[ s ];
+ len = strlen( tmp );
+ cmdtab[ i ].buffer[ s ] = (char*)BJAM_MALLOC_ATOMIC( len + ret + 1 );
+ memcpy( cmdtab[ i ].buffer[ s ], tmp, len );
+ memcpy( cmdtab[ i ].buffer[ s ] + len, buffer, ret + 1 );
+ BJAM_FREE( tmp );
+ }
+ }
+
+ return feof(cmdtab[ i ].stream[ s ]);
+}
+
+
+void close_streams( int i, int s )
+{
+ /* Close the stream and pipe descriptor. */
+ fclose(cmdtab[ i ].stream[ s ]);
+ cmdtab[ i ].stream[ s ] = 0;
+
+ close(cmdtab[ i ].fd[ s ]);
+ cmdtab[ i ].fd[ s ] = 0;
+}
+
+
+void populate_file_descriptors( int * fmax, fd_set * fds)
+{
+ int i, fd_max = 0;
+ struct tms buf;
+ clock_t current = times( &buf );
+ select_timeout = globs.timeout;
+
+ /* Compute max read file descriptor for use in select. */
+ FD_ZERO(fds);
+ for ( i = 0; i < globs.jobs; ++i )
+ {
+ if ( 0 < cmdtab[ i ].fd[ OUT ] )
+ {
+ fd_max = fd_max < cmdtab[ i ].fd[ OUT ] ? cmdtab[ i ].fd[ OUT ] : fd_max;
+ FD_SET(cmdtab[ i ].fd[ OUT ], fds);
+ }
+ if ( globs.pipe_action != 0 )
+ {
+ if (0 < cmdtab[ i ].fd[ ERR ])
+ {
+ fd_max = fd_max < cmdtab[ i ].fd[ ERR ] ? cmdtab[ i ].fd[ ERR ] : fd_max;
+ FD_SET(cmdtab[ i ].fd[ ERR ], fds);
+ }
+ }
+
+ if (globs.timeout && cmdtab[ i ].pid) {
+ clock_t consumed = (current - cmdtab[ i ].start_time) / tps;
+ clock_t process_timesout = globs.timeout - consumed;
+ if (0 < process_timesout && process_timesout < select_timeout) {
+ select_timeout = process_timesout;
+ }
+ if ( globs.timeout <= consumed )
+ {
+ killpg( cmdtab[ i ].pid, SIGKILL );
+ cmdtab[ i ].exit_reason = EXIT_TIMEOUT;
+ }
+ }
+ }
+ *fmax = fd_max;
+}
+
+
+/*
+ * exec_wait() - wait and drive at most one execution completion.
+ */
+
+int exec_wait()
+{
+ int i;
+ int ret;
+ int fd_max;
+ int pid;
+ int status;
+ int finished;
+ int rstat;
+ timing_info time_info;
+ fd_set fds;
+ struct tms new_time;
+
+ /* Handle naive make1() which does not know if commands are running. */
+ if ( !cmdsrunning )
+ return 0;
+
+ /* Process children that signaled. */
+ finished = 0;
+ while ( !finished && cmdsrunning )
+ {
+ /* Compute max read file descriptor for use in select(). */
+ populate_file_descriptors( &fd_max, &fds );
+
+ if ( 0 < globs.timeout )
+ {
+ /* Force select() to timeout so we can terminate expired processes.
+ */
+ tv.tv_sec = select_timeout;
+ tv.tv_usec = 0;
+
+ /* select() will wait until: i/o on a descriptor, a signal, or we
+ * time out.
+ */
+ ret = select( fd_max + 1, &fds, 0, 0, &tv );
+ }
+ else
+ {
+ /* select() will wait until i/o on a descriptor or a signal. */
+ ret = select( fd_max + 1, &fds, 0, 0, 0 );
+ }
+
+ if ( 0 < ret )
+ {
+ for ( i = 0; i < globs.jobs; ++i )
+ {
+ int out = 0;
+ int err = 0;
+ if ( FD_ISSET( cmdtab[ i ].fd[ OUT ], &fds ) )
+ out = read_descriptor( i, OUT );
+
+ if ( ( globs.pipe_action != 0 ) &&
+ ( FD_ISSET( cmdtab[ i ].fd[ ERR ], &fds ) ) )
+ err = read_descriptor( i, ERR );
+
+ /* If feof on either descriptor, then we are done. */
+ if ( out || err )
+ {
+ /* Close the stream and pipe descriptors. */
+ close_streams( i, OUT );
+ if ( globs.pipe_action != 0 )
+ close_streams( i, ERR );
+
+ /* Reap the child and release resources. */
+ pid = waitpid( cmdtab[ i ].pid, &status, 0 );
+
+ if ( pid == cmdtab[ i ].pid )
+ {
+ finished = 1;
+ pid = 0;
+ cmdtab[ i ].pid = 0;
+
+ /* Set reason for exit if not timed out. */
+ if ( WIFEXITED( status ) )
+ {
+ cmdtab[ i ].exit_reason = 0 == WEXITSTATUS( status )
+ ? EXIT_OK
+ : EXIT_FAIL;
+ }
+
+ /* Print out the rule and target name. */
+ out_action( cmdtab[ i ].action, cmdtab[ i ].target,
+ cmdtab[ i ].command, cmdtab[ i ].buffer[ OUT ],
+ cmdtab[ i ].buffer[ ERR ], cmdtab[ i ].exit_reason
+ );
+
+ times( &new_time );
+
+ time_info.system = (double)( new_time.tms_cstime - old_time.tms_cstime ) / CLOCKS_PER_SEC;
+ time_info.user = (double)( new_time.tms_cutime - old_time.tms_cutime ) / CLOCKS_PER_SEC;
+ time_info.start = cmdtab[ i ].start_dt;
+ time_info.end = time( 0 );
+
+ old_time = new_time;
+
+ /* Drive the completion. */
+ --cmdsrunning;
+
+ if ( intr )
+ rstat = EXEC_CMD_INTR;
+ else if ( status != 0 )
+ rstat = EXEC_CMD_FAIL;
+ else
+ rstat = EXEC_CMD_OK;
+
+ /* Assume -p0 in effect so only pass buffer[ 0 ]
+ * containing merged output.
+ */
+ (*cmdtab[ i ].func)( cmdtab[ i ].closure, rstat,
+ &time_info, cmdtab[ i ].command,
+ cmdtab[ i ].buffer[ 0 ] );
+
+ BJAM_FREE( cmdtab[ i ].buffer[ OUT ] );
+ cmdtab[ i ].buffer[ OUT ] = 0;
+
+ BJAM_FREE( cmdtab[ i ].buffer[ ERR ] );
+ cmdtab[ i ].buffer[ ERR ] = 0;
+
+ BJAM_FREE( cmdtab[ i ].command );
+ cmdtab[ i ].command = 0;
+
+ cmdtab[ i ].func = 0;
+ cmdtab[ i ].closure = 0;
+ cmdtab[ i ].start_time = 0;
+ }
+ else
+ {
+ printf( "unknown pid %d with errno = %d\n", pid, errno );
+ exit( EXITBAD );
+ }
+ }
+ }
+ }
+ }
+
+ return 1;
+}
+
+# endif /* USE_EXECUNIX */
diff --git a/jam-files/engine/execvms.c b/jam-files/engine/execvms.c
new file mode 100644
index 00000000..729917d3
--- /dev/null
+++ b/jam-files/engine/execvms.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+#include "jam.h"
+#include "lists.h"
+#include "execcmd.h"
+
+#ifdef OS_VMS
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <iodef.h>
+#include <ssdef.h>
+#include <descrip.h>
+#include <dvidef.h>
+#include <clidef.h>
+
+/*
+ * execvms.c - execute a shell script, ala VMS.
+ *
+ * The approach is this:
+ *
+ * If the command is a single line, and shorter than WRTLEN (what we believe to
+ * be the maximum line length), we just system() it.
+ *
+ * If the command is multi-line, or longer than WRTLEN, we write the command
+ * block to a temp file, splitting long lines (using "-" at the end of the line
+ * to indicate contiuation), and then source that temp file. We use special
+ * logic to make sure we do not continue in the middle of a quoted string.
+ *
+ * 05/04/94 (seiwald) - async multiprocess interface; noop on VMS
+ * 12/20/96 (seiwald) - rewritten to handle multi-line commands well
+ * 01/14/96 (seiwald) - do not put -'s between "'s
+ */
+
+#define WRTLEN 240
+
+#define MIN( a, b ) ((a) < (b) ? (a) : (b))
+
+/* 1 for the @ and 4 for the .com */
+
+char tempnambuf[ L_tmpnam + 1 + 4 ] = { 0 };
+
+
+void exec_cmd
+(
+ char * string,
+ void (* func)( void * closure, int status, timing_info *, char *, char * ),
+ void * closure,
+ LIST * shell,
+ char * rule_name,
+ char * target
+)
+{
+ char * s;
+ char * e;
+ cahr * p;
+ int rstat = EXEC_CMD_OK;
+ int status;
+
+ /* See if string is more than one line discounting leading/trailing white
+ * space.
+ */
+ for ( s = string; *s && isspace( *s ); ++s );
+
+ e = p = strchr( s, '\n' );
+
+ while ( p && isspace( *p ) )
+ ++p;
+
+ /* If multi line or long, write to com file. Otherwise, exec directly. */
+ if ( ( p && *p ) || ( e - s > WRTLEN ) )
+ {
+ FILE * f;
+
+ /* Create temp file invocation "@sys$scratch:tempfile.com". */
+ if ( !*tempnambuf )
+ {
+ tempnambuf[0] = '@';
+ (void)tmpnam( tempnambuf + 1 );
+ strcat( tempnambuf, ".com" );
+ }
+
+ /* Open tempfile. */
+ if ( !( f = fopen( tempnambuf + 1, "w" ) ) )
+ {
+ printf( "can't open command file\n" );
+ (*func)( closure, EXEC_CMD_FAIL );
+ return;
+ }
+
+ /* For each line of the string. */
+ while ( *string )
+ {
+ char * s = strchr( string, '\n' );
+ int len = s ? s + 1 - string : strlen( string );
+
+ fputc( '$', f );
+
+ /* For each chunk of a line that needs to be split. */
+ while ( len > 0 )
+ {
+ char * q = string;
+ char * qe = string + MIN( len, WRTLEN );
+ char * qq = q;
+ int quote = 0;
+
+ /* Look for matching "s. */
+ for ( ; q < qe; ++q )
+ if ( ( *q == '"' ) && ( quote = !quote ) )
+ qq = q;
+
+ /* Back up to opening quote, if in one. */
+ if ( quote )
+ q = qq;
+
+ fwrite( string, ( q - string ), 1, f );
+
+ len -= ( q - string );
+ string = q;
+
+ if ( len )
+ {
+ fputc( '-', f );
+ fputc( '\n', f );
+ }
+ }
+ }
+
+ fclose( f );
+
+ status = system( tempnambuf ) & 0x07;
+
+ unlink( tempnambuf + 1 );
+ }
+ else
+ {
+ /* Execute single line command. Strip trailing newline before execing.
+ */
+ if ( e ) *e = 0;
+ status = system( s ) & 0x07;
+ }
+
+ /* Fail for error or fatal error. OK on OK, warning or info exit. */
+ if ( ( status == 2 ) || ( status == 4 ) )
+ rstat = EXEC_CMD_FAIL;
+
+ (*func)( closure, rstat );
+}
+
+
+int exec_wait()
+{
+ return 0;
+}
+
+# endif /* VMS */
diff --git a/jam-files/engine/expand.c b/jam-files/engine/expand.c
new file mode 100644
index 00000000..d8e58827
--- /dev/null
+++ b/jam-files/engine/expand.c
@@ -0,0 +1,733 @@
+/*
+ * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+# include "jam.h"
+# include "lists.h"
+# include "variable.h"
+# include "expand.h"
+# include "pathsys.h"
+# include "newstr.h"
+# include <assert.h>
+# include <stdlib.h>
+# include <limits.h>
+
+# ifdef OS_CYGWIN
+# include <sys/cygwin.h>
+# include <windows.h>
+# endif
+
+/*
+ * expand.c - expand a buffer, given variable values
+ *
+ * External routines:
+ *
+ * var_expand() - variable-expand input string into list of strings
+ *
+ * Internal routines:
+ *
+ * var_edit_parse() - parse : modifiers into PATHNAME structure.
+ * var_edit_file() - copy input target name to output, modifying filename.
+ * var_edit_shift() - do upshift/downshift mods.
+ *
+ * 01/25/94 (seiwald) - $(X)$(UNDEF) was expanding like plain $(X)
+ * 04/13/94 (seiwald) - added shorthand L0 for null list pointer
+ * 01/11/01 (seiwald) - added support for :E=emptyvalue, :J=joinval
+ */
+
+typedef struct
+{
+ PATHNAME f; /* :GDBSMR -- pieces */
+ char parent; /* :P -- go to parent directory */
+ char filemods; /* one of the above applied */
+ char downshift; /* :L -- downshift result */
+ char upshift; /* :U -- upshift result */
+ char to_slashes; /* :T -- convert "\" to "/" */
+ char to_windows; /* :W -- convert cygwin to native paths */
+ PATHPART empty; /* :E -- default for empties */
+ PATHPART join; /* :J -- join list with char */
+} VAR_EDITS ;
+
+static void var_edit_parse( char * mods, VAR_EDITS * edits );
+static void var_edit_file ( char * in, string * out, VAR_EDITS * edits );
+static void var_edit_shift( string * out, VAR_EDITS * edits );
+
+#define MAGIC_COLON '\001'
+#define MAGIC_LEFT '\002'
+#define MAGIC_RIGHT '\003'
+
+
+/*
+ * var_expand() - variable-expand input string into list of strings.
+ *
+ * Would just copy input to output, performing variable expansion, except that
+ * since variables can contain multiple values the result of variable expansion
+ * may contain multiple values (a list). Properly performs "product" operations
+ * that occur in "$(var1)xxx$(var2)" or even "$($(var2))".
+ *
+ * Returns a newly created list.
+ */
+
+LIST * var_expand( LIST * l, char * in, char * end, LOL * lol, int cancopyin )
+{
+ char out_buf[ MAXSYM ];
+ string buf[ 1 ];
+ string out1[ 1 ]; /* temporary buffer */
+ size_t prefix_length;
+ char * out;
+ char * inp = in;
+ char * ov; /* for temp copy of variable in outbuf */
+ int depth;
+
+ if ( DEBUG_VAREXP )
+ printf( "expand '%.*s'\n", end - in, in );
+
+ /* This gets a lot of cases: $(<) and $(>). */
+ if
+ (
+ ( in[ 0 ] == '$' ) &&
+ ( in[ 1 ] == '(' ) &&
+ ( in[ 3 ] == ')' ) &&
+ ( in[ 4 ] == '\0' )
+ )
+ {
+ switch ( in[ 2 ] )
+ {
+ case '<': return list_copy( l, lol_get( lol, 0 ) );
+ case '>': return list_copy( l, lol_get( lol, 1 ) );
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return list_copy( l, lol_get( lol, in[ 2 ] - '1' ) );
+ }
+ }
+ else if ( in[0] == '$' && in[1] == '(' && in[2] == '1' && in[4] == ')' &&
+ in[5] == '\0') {
+
+ switch( in[3] )
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return list_copy( l, lol_get( lol, in[3]-'0'+10-1 ) );
+ }
+ }
+
+ /* Expand @() files, to single item plus accompanying file. */
+ if ( ( in[ 0 ] == '@' ) && ( in[ 1 ] == '(' ) && ( *( end - 1 ) == ')' ) )
+ {
+ /* We try the expansion until it fits within the propective output
+ * buffer.
+ */
+ char * at_buf = 0;
+ int at_size = MAXJPATH;
+ int at_len = 0;
+ do
+ {
+ BJAM_FREE( at_buf );
+ at_buf = (char *)BJAM_MALLOC_ATOMIC( at_size + 1 );
+ at_len = var_string( in, at_buf, at_size, lol );
+ at_size *= 2;
+ }
+ while ( ( at_len < 0 ) && ( at_size < INT_MAX / 2 ) );
+ /* Return the result as a single item list. */
+ if ( at_len > 0 )
+ {
+ LIST * r;
+ string_copy( buf, at_buf );
+ r = list_new( l, newstr( buf->value ) );
+ string_free( buf );
+ BJAM_FREE( at_buf );
+ return r;
+ }
+ BJAM_FREE( at_buf );
+ }
+
+ /* Just try simple copy of in to out. */
+ while ( in < end )
+ if ( ( *in++ == '$' ) && ( *in == '(' ) )
+ goto expand;
+
+ /* No variables expanded - just add copy of input string to list. */
+
+ /* 'cancopyin' is an optimization: if the input was already a list item, we
+ * can use copystr() to put it on the new list. Otherwise, we use the slower
+ * newstr().
+ */
+ if ( cancopyin )
+ return list_new( l, copystr( inp ) );
+
+ {
+ LIST * r;
+ string_new( buf );
+ string_append_range( buf, inp, end );
+ r = list_new( l, newstr( buf->value ) );
+ string_free( buf );
+ return r;
+ }
+
+expand:
+ string_new( buf );
+ string_append_range( buf, inp, in - 1 ); /* Copy the part before '$'. */
+ /*
+ * Input so far (ignore blanks):
+ *
+ * stuff-in-outbuf $(variable) remainder
+ * ^ ^
+ * in end
+ * Output so far:
+ *
+ * stuff-in-outbuf $
+ * ^ ^
+ * out_buf out
+ *
+ *
+ * We just copied the $ of $(...), so back up one on the output. We now find
+ * the matching close paren, copying the variable and modifiers between the
+ * $( and ) temporarily into out_buf, so that we can replace :'s with
+ * MAGIC_COLON. This is necessary to avoid being confused by modifier values
+ * that are variables containing :'s. Ugly.
+ */
+
+ depth = 1;
+ inp = ++in; /* Skip over the '('. */
+
+ while ( ( in < end ) && depth )
+ {
+ switch ( *in++ )
+ {
+ case '(': ++depth; break;
+ case ')': --depth; break;
+ }
+ }
+
+ /*
+ * Input so far (ignore blanks):
+ *
+ * stuff-in-outbuf $(variable) remainder
+ * ^ ^ ^
+ * inp in end
+ */
+ prefix_length = buf->size;
+ string_append_range( buf, inp, in - 1 );
+
+ out = buf->value + prefix_length;
+ for ( ov = out; ov < buf->value + buf->size; ++ov )
+ {
+ switch ( *ov )
+ {
+ case ':': *ov = MAGIC_COLON; break;
+ case '[': *ov = MAGIC_LEFT ; break;
+ case ']': *ov = MAGIC_RIGHT; break;
+ }
+ }
+
+ /*
+ * Input so far (ignore blanks):
+ *
+ * stuff-in-outbuf $(variable) remainder
+ * ^ ^
+ * in end
+ * Output so far:
+ *
+ * stuff-in-outbuf variable
+ * ^ ^ ^
+ * out_buf out ov
+ *
+ * Later we will overwrite 'variable' in out_buf, but we will be done with
+ * it by then. 'variable' may be a multi-element list, so may each value for
+ * '$(variable element)', and so may 'remainder'. Thus we produce a product
+ * of three lists.
+ */
+ {
+ LIST * variables = 0;
+ LIST * remainder = 0;
+ LIST * vars;
+
+ /* Recursively expand variable name & rest of input. */
+ if ( out < ov ) variables = var_expand( L0, out, ov, lol, 0 );
+ if ( in < end ) remainder = var_expand( L0, in, end, lol, 0 );
+
+ /* Now produce the result chain. */
+
+ /* For each variable name. */
+ for ( vars = variables; vars; vars = list_next( vars ) )
+ {
+ LIST * value = 0;
+ LIST * evalue = 0;
+ char * colon;
+ char * bracket;
+ string variable[1];
+ char * varname;
+ int sub1 = 0;
+ int sub2 = -1;
+ VAR_EDITS edits;
+
+ /* Look for a : modifier in the variable name. Must copy into
+ * varname so we can modify it.
+ */
+ string_copy( variable, vars->string );
+ varname = variable->value;
+
+ if ( ( colon = strchr( varname, MAGIC_COLON ) ) )
+ {
+ string_truncate( variable, colon - varname );
+ var_edit_parse( colon + 1, &edits );
+ }
+
+ /* Look for [x-y] subscripting. sub1 and sub2 are x and y. */
+ if ( ( bracket = strchr( varname, MAGIC_LEFT ) ) )
+ {
+ /* Make all syntax errors in [] subscripting result in the same
+ * behavior: silenty return an empty expansion (by setting sub2
+ * = 0). Brute force parsing; May get moved into yacc someday.
+ */
+
+ char * s = bracket + 1;
+
+ string_truncate( variable, bracket - varname );
+
+ do /* so we can use "break" */
+ {
+ /* Allow negative indexes. */
+ if ( !isdigit( *s ) && ( *s != '-' ) )
+ {
+ sub2 = 0;
+ break;
+ }
+ sub1 = atoi( s );
+
+ /* Skip over the first symbol, which is either a digit or dash. */
+ ++s;
+ while ( isdigit( *s ) ) ++s;
+
+ if ( *s == MAGIC_RIGHT )
+ {
+ sub2 = sub1;
+ break;
+ }
+
+ if ( *s != '-' )
+ {
+ sub2 = 0;
+ break;
+ }
+
+ ++s;
+
+ if ( *s == MAGIC_RIGHT )
+ {
+ sub2 = -1;
+ break;
+ }
+
+ if ( !isdigit( *s ) && ( *s != '-' ) )
+ {
+ sub2 = 0;
+ break;
+ }
+
+ /* First, compute the index of the last element. */
+ sub2 = atoi( s );
+ while ( isdigit( *++s ) );
+
+ if ( *s != MAGIC_RIGHT )
+ sub2 = 0;
+
+ } while ( 0 );
+
+ /* Anything but the end of the string, or the colon introducing
+ * a modifier is a syntax error.
+ */
+ ++s;
+ if ( *s && ( *s != MAGIC_COLON ) )
+ sub2 = 0;
+
+ *bracket = '\0';
+ }
+
+ /* Get variable value, with special handling for $(<), $(>), $(n).
+ */
+ if ( !varname[1] )
+ {
+ if ( varname[0] == '<' )
+ value = lol_get( lol, 0 );
+ else if ( varname[0] == '>' )
+ value = lol_get( lol, 1 );
+ else if ( ( varname[0] >= '1' ) && ( varname[0] <= '9' ) )
+ value = lol_get( lol, varname[0] - '1' );
+ else if( varname[0] == '1' && varname[1] >= '0' &&
+ varname[1] <= '9' && !varname[2] )
+ value = lol_get( lol, varname[1] - '0' + 10 - 1 );
+ }
+
+ if ( !value )
+ value = var_get( varname );
+
+ /* Handle negitive indexes: part two. */
+ {
+ int length = list_length( value );
+
+ if ( sub1 < 0 )
+ sub1 = length + sub1;
+ else
+ sub1 -= 1;
+
+ if ( sub2 < 0 )
+ sub2 = length + 1 + sub2 - sub1;
+ else
+ sub2 -= sub1;
+ /* The "sub2 < 0" test handles the semantic error of sub2 <
+ * sub1.
+ */
+ if ( sub2 < 0 )
+ sub2 = 0;
+ }
+
+ /* The fast path: $(x) - just copy the variable value. This is only
+ * an optimization.
+ */
+ if ( ( out == out_buf ) && !bracket && !colon && ( in == end ) )
+ {
+ string_free( variable );
+ l = list_copy( l, value );
+ continue;
+ }
+
+ /* Handle start subscript. */
+ while ( ( sub1 > 0 ) && value )
+ --sub1, value = list_next( value );
+
+ /* Empty w/ :E=default?. */
+ if ( !value && colon && edits.empty.ptr )
+ evalue = value = list_new( L0, newstr( edits.empty.ptr ) );
+
+ /* For each variable value. */
+ string_new( out1 );
+ for ( ; value; value = list_next( value ) )
+ {
+ LIST * rem;
+ size_t postfix_start;
+
+ /* Handle end subscript (length actually). */
+
+ if ( sub2 >= 0 && --sub2 < 0 )
+ break;
+
+ string_truncate( buf, prefix_length );
+
+ /* Apply : mods, if present */
+
+ if ( colon && edits.filemods )
+ var_edit_file( value->string, out1, &edits );
+ else
+ string_append( out1, value->string );
+
+ if ( colon && ( edits.upshift || edits.downshift || edits.to_slashes || edits.to_windows ) )
+ var_edit_shift( out1, &edits );
+
+ /* Handle :J=joinval */
+ /* If we have more values for this var, just keep appending them
+ * (using the join value) rather than creating separate LIST
+ * elements.
+ */
+ if ( colon && edits.join.ptr &&
+ ( list_next( value ) || list_next( vars ) ) )
+ {
+ string_append( out1, edits.join.ptr );
+ continue;
+ }
+
+ string_append( buf, out1->value );
+ string_free( out1 );
+ string_new( out1 );
+
+ /* If no remainder, append result to output chain. */
+ if ( in == end )
+ {
+ l = list_new( l, newstr( buf->value ) );
+ continue;
+ }
+
+ /* For each remainder, append the complete string to the output
+ * chain. Remember the end of the variable expansion so we can
+ * just tack on each instance of 'remainder'.
+ */
+ postfix_start = buf->size;
+ for ( rem = remainder; rem; rem = list_next( rem ) )
+ {
+ string_truncate( buf, postfix_start );
+ string_append( buf, rem->string );
+ l = list_new( l, newstr( buf->value ) );
+ }
+ }
+ string_free( out1 );
+
+ /* Toss used empty. */
+ if ( evalue )
+ list_free( evalue );
+
+ string_free( variable );
+ }
+
+ /* variables & remainder were gifts from var_expand and must be freed. */
+ if ( variables ) list_free( variables );
+ if ( remainder ) list_free( remainder );
+
+ if ( DEBUG_VAREXP )
+ {
+ printf( "expanded to " );
+ list_print( l );
+ printf( "\n" );
+ }
+
+ string_free( buf );
+ return l;
+ }
+}
+
+
+/*
+ * var_edit_parse() - parse : modifiers into PATHNAME structure
+ *
+ * The : modifiers in a $(varname:modifier) currently support replacing or
+ * omitting elements of a filename, and so they are parsed into a PATHNAME
+ * structure (which contains pointers into the original string).
+ *
+ * Modifiers of the form "X=value" replace the component X with the given value.
+ * Modifiers without the "=value" cause everything but the component X to be
+ * omitted. X is one of:
+ *
+ * G <grist>
+ * D directory name
+ * B base name
+ * S .suffix
+ * M (member)
+ * R root directory - prepended to whole path
+ *
+ * This routine sets:
+ *
+ * f->f_xxx.ptr = 0
+ * f->f_xxx.len = 0
+ * -> leave the original component xxx
+ *
+ * f->f_xxx.ptr = string
+ * f->f_xxx.len = strlen( string )
+ * -> replace component xxx with string
+ *
+ * f->f_xxx.ptr = ""
+ * f->f_xxx.len = 0
+ * -> omit component xxx
+ *
+ * var_edit_file() below and path_build() obligingly follow this convention.
+ */
+
+static void var_edit_parse( char * mods, VAR_EDITS * edits )
+{
+ int havezeroed = 0;
+ memset( (char *)edits, 0, sizeof( *edits ) );
+
+ while ( *mods )
+ {
+ char * p;
+ PATHPART * fp;
+
+ switch ( *mods++ )
+ {
+ case 'L': edits->downshift = 1; continue;
+ case 'U': edits->upshift = 1; continue;
+ case 'P': edits->parent = edits->filemods = 1; continue;
+ case 'E': fp = &edits->empty; goto strval;
+ case 'J': fp = &edits->join; goto strval;
+ case 'G': fp = &edits->f.f_grist; goto fileval;
+ case 'R': fp = &edits->f.f_root; goto fileval;
+ case 'D': fp = &edits->f.f_dir; goto fileval;
+ case 'B': fp = &edits->f.f_base; goto fileval;
+ case 'S': fp = &edits->f.f_suffix; goto fileval;
+ case 'M': fp = &edits->f.f_member; goto fileval;
+ case 'T': edits->to_slashes = 1; continue;
+ case 'W': edits->to_windows = 1; continue;
+ default:
+ return; /* Should complain, but so what... */
+ }
+
+ fileval:
+ /* Handle :CHARS, where each char (without a following =) selects a
+ * particular file path element. On the first such char, we deselect all
+ * others (by setting ptr = "", len = 0) and for each char we select
+ * that element (by setting ptr = 0).
+ */
+ edits->filemods = 1;
+
+ if ( *mods != '=' )
+ {
+ if ( !havezeroed++ )
+ {
+ int i;
+ for ( i = 0; i < 6; ++i )
+ {
+ edits->f.part[ i ].len = 0;
+ edits->f.part[ i ].ptr = "";
+ }
+ }
+
+ fp->ptr = 0;
+ continue;
+ }
+
+ strval:
+ /* Handle :X=value, or :X */
+ if ( *mods != '=' )
+ {
+ fp->ptr = "";
+ fp->len = 0;
+ }
+ else if ( ( p = strchr( mods, MAGIC_COLON ) ) )
+ {
+ *p = 0;
+ fp->ptr = ++mods;
+ fp->len = p - mods;
+ mods = p + 1;
+ }
+ else
+ {
+ fp->ptr = ++mods;
+ fp->len = strlen( mods );
+ mods += fp->len;
+ }
+ }
+}
+
+
+/*
+ * var_edit_file() - copy input target name to output, modifying filename.
+ */
+
+static void var_edit_file( char * in, string * out, VAR_EDITS * edits )
+{
+ PATHNAME pathname;
+
+ /* Parse apart original filename, putting parts into "pathname". */
+ path_parse( in, &pathname );
+
+ /* Replace any pathname with edits->f */
+ if ( edits->f.f_grist .ptr ) pathname.f_grist = edits->f.f_grist;
+ if ( edits->f.f_root .ptr ) pathname.f_root = edits->f.f_root;
+ if ( edits->f.f_dir .ptr ) pathname.f_dir = edits->f.f_dir;
+ if ( edits->f.f_base .ptr ) pathname.f_base = edits->f.f_base;
+ if ( edits->f.f_suffix.ptr ) pathname.f_suffix = edits->f.f_suffix;
+ if ( edits->f.f_member.ptr ) pathname.f_member = edits->f.f_member;
+
+ /* If requested, modify pathname to point to parent. */
+ if ( edits->parent )
+ path_parent( &pathname );
+
+ /* Put filename back together. */
+ path_build( &pathname, out, 0 );
+}
+
+
+/*
+ * var_edit_shift() - do upshift/downshift mods.
+ */
+
+static void var_edit_shift( string * out, VAR_EDITS * edits )
+{
+ /* Handle upshifting, downshifting and slash translation now. */
+ char * p;
+ for ( p = out->value; *p; ++p)
+ {
+ if ( edits->upshift )
+ *p = toupper( *p );
+ else if ( edits->downshift )
+ *p = tolower( *p );
+ if ( edits->to_slashes && ( *p == '\\' ) )
+ *p = '/';
+# ifdef OS_CYGWIN
+ if ( edits->to_windows )
+ {
+ char result[ MAX_PATH + 1 ];
+ cygwin_conv_to_win32_path( out->value, result );
+ assert( strlen( result ) <= MAX_PATH );
+ string_free( out );
+ string_copy( out, result );
+ }
+# endif
+ }
+ out->size = p - out->value;
+}
+
+
+#ifndef NDEBUG
+void var_expand_unit_test()
+{
+ LOL lol[ 1 ];
+ LIST * l;
+ LIST * l2;
+ LIST * expected = list_new( list_new( L0, newstr( "axb" ) ), newstr( "ayb" ) );
+ LIST * e2;
+ char axyb[] = "a$(xy)b";
+ char azb[] = "a$($(z))b";
+ char path[] = "$(p:W)";
+
+# ifdef OS_CYGWIN
+ char cygpath[ 256 ];
+ cygwin_conv_to_posix_path( "c:\\foo\\bar", cygpath );
+# else
+ char cygpath[] = "/cygdrive/c/foo/bar";
+# endif
+
+ lol_init(lol);
+ var_set( "xy", list_new( list_new( L0, newstr( "x" ) ), newstr( "y" ) ), VAR_SET );
+ var_set( "z", list_new( L0, newstr( "xy" ) ), VAR_SET );
+ var_set( "p", list_new( L0, newstr( cygpath ) ), VAR_SET );
+
+ l = var_expand( 0, axyb, axyb + sizeof( axyb ) - 1, lol, 0 );
+ for ( l2 = l, e2 = expected; l2 && e2; l2 = list_next( l2 ), e2 = list_next( e2 ) )
+ assert( !strcmp( e2->string, l2->string ) );
+ assert( l2 == 0 );
+ assert( e2 == 0 );
+ list_free( l );
+
+ l = var_expand( 0, azb, azb + sizeof( azb ) - 1, lol, 0 );
+ for ( l2 = l, e2 = expected; l2 && e2; l2 = list_next( l2 ), e2 = list_next( e2 ) )
+ assert( !strcmp( e2->string, l2->string ) );
+ assert( l2 == 0 );
+ assert( e2 == 0 );
+ list_free( l );
+
+ l = var_expand( 0, path, path + sizeof( path ) - 1, lol, 0 );
+ assert( l != 0 );
+ assert( list_next( l ) == 0 );
+# ifdef OS_CYGWIN
+ /* On some installations of cygwin the drive letter is expanded to other
+ * case. This has been reported to be the case if cygwin has been installed
+ * to C:\ as opposed to C:\cygwin. Since case of the drive letter will not
+ * matter, we allow for both.
+ */
+ assert( !strcmp( l->string, "c:\\foo\\bar" ) ||
+ !strcmp( l->string, "C:\\foo\\bar" ) );
+# else
+ assert( !strcmp( l->string, cygpath ) );
+# endif
+ list_free( l );
+ list_free( expected );
+ lol_free( lol );
+}
+#endif
diff --git a/jam-files/engine/expand.h b/jam-files/engine/expand.h
new file mode 100644
index 00000000..cc25d190
--- /dev/null
+++ b/jam-files/engine/expand.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * expand.h - expand a buffer, given variable values
+ */
+
+#include "lists.h"
+
+LIST *var_expand( LIST *l, char *in, char *end, LOL *lol, int cancopyin );
+void var_expand_unit_test();
diff --git a/jam-files/engine/filemac.c b/jam-files/engine/filemac.c
new file mode 100644
index 00000000..e69aa648
--- /dev/null
+++ b/jam-files/engine/filemac.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/* This file is ALSO:
+ * Copyright 2001-2004 David Abrahams.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+# include "jam.h"
+# include "filesys.h"
+# include "pathsys.h"
+
+# ifdef OS_MAC
+
+#include <Files.h>
+#include <Folders.h>
+
+# include <:sys:stat.h>
+
+/*
+ * filemac.c - manipulate file names and scan directories on macintosh
+ *
+ * External routines:
+ *
+ * file_dirscan() - scan a directory for files
+ * file_time() - get timestamp of file, if not done by file_dirscan()
+ * file_archscan() - scan an archive for files
+ *
+ * File_dirscan() and file_archscan() call back a caller provided function
+ * for each file found. A flag to this callback function lets file_dirscan()
+ * and file_archscan() indicate that a timestamp is being provided with the
+ * file. If file_dirscan() or file_archscan() do not provide the file's
+ * timestamp, interested parties may later call file_time().
+ *
+ * 04/08/94 (seiwald) - Coherent/386 support added.
+ * 12/19/94 (mikem) - solaris string table insanity support
+ * 02/14/95 (seiwald) - parse and build /xxx properly
+ * 05/03/96 (seiwald) - split into pathunix.c
+ * 11/21/96 (peterk) - BEOS does not have Unix-style archives
+ */
+
+
+void CopyC2PStr( char const * cstr, StringPtr pstr )
+{
+ int len;
+ for ( len = 0; *cstr && ( len < 255 ); pstr[ ++len ] = *cstr++ );
+ pstr[ 0 ] = len;
+}
+
+
+/*
+ * file_dirscan() - scan a directory for files.
+ */
+
+void file_dirscan( char * dir, scanback func, void * closure )
+{
+ PATHNAME f;
+ string filename[ 1 ];
+ unsigned char fullPath[ 512 ];
+
+ FSSpec spec;
+ WDPBRec vol;
+ Str63 volName;
+ CInfoPBRec lastInfo;
+ int index = 1;
+
+ /* First enter directory itself. */
+
+ memset( (char *)&f, '\0', sizeof( f ) );
+
+ f.f_dir.ptr = dir;
+ f.f_dir.len = strlen(dir);
+
+ if ( DEBUG_BINDSCAN )
+ printf( "scan directory %s\n", dir );
+
+ /* Special case ":" - enter it */
+
+ if ( ( f.f_dir.len == 1 ) && ( f.f_dir.ptr[0] == ':' ) )
+ (*func)( closure, dir, 0 /* not stat()'ed */, (time_t)0 );
+
+ /* Now enter contents of directory */
+
+ vol.ioNamePtr = volName;
+
+ if ( PBHGetVolSync( &vol ) )
+ return;
+
+ CopyC2PStr( dir, fullPath );
+
+ if ( FSMakeFSSpec( vol.ioWDVRefNum, vol.ioWDDirID, fullPath, &spec ) )
+ return;
+
+ lastInfo.dirInfo.ioVRefNum = spec.vRefNum;
+ lastInfo.dirInfo.ioDrDirID = spec.parID;
+ lastInfo.dirInfo.ioNamePtr = spec.name;
+ lastInfo.dirInfo.ioFDirIndex = 0;
+ lastInfo.dirInfo.ioACUser = 0;
+
+ if ( PBGetCatInfoSync( &lastInfo ) )
+ return;
+
+ if ( !( lastInfo.dirInfo.ioFlAttrib & 0x10 ) )
+ return;
+
+ /* ioDrDirID must be reset each time. */
+ spec.parID = lastInfo.dirInfo.ioDrDirID;
+
+ string_new( filename );
+ for ( ; ; )
+ {
+ lastInfo.dirInfo.ioVRefNum = spec.vRefNum;
+ lastInfo.dirInfo.ioDrDirID = spec.parID;
+ lastInfo.dirInfo.ioNamePtr = fullPath;
+ lastInfo.dirInfo.ioFDirIndex = index++;
+
+ if ( PBGetCatInfoSync( &lastInfo ) )
+ return;
+
+ f.f_base.ptr = (char *)fullPath + 1;
+ f.f_base.len = *fullPath;
+
+ string_truncate( filename, 0 );
+ path_build( &f, filename, 0 );
+ (*func)( closure, filename->value, 0 /* not stat()'ed */, (time_t)0 );
+ }
+ string_free( filename );
+}
+
+
+/*
+ * file_time() - get timestamp of file, if not done by file_dirscan().
+ */
+
+int file_time( char * filename, time_t * time )
+{
+ struct stat statbuf;
+
+ if ( stat( filename, &statbuf ) < 0 )
+ return -1;
+
+ *time = statbuf.st_mtime;
+
+ return 0;
+}
+
+
+int file_is_file( char * filename )
+{
+ struct stat statbuf;
+ if ( stat( filename, &statbuf ) < 0 )
+ return -1;
+ return S_ISREG( statbuf.st_mode ) ? 1 : 0;
+}
+
+int file_mkdir(char *pathname)
+{
+ return mkdir(pathname, 0766);
+}
+
+
+/*
+ * file_archscan() - scan an archive for files.
+ */
+
+void file_archscan( char * archive, scanback func, void * closure )
+{
+}
+
+
+# endif /* macintosh */
diff --git a/jam-files/engine/filent.c b/jam-files/engine/filent.c
new file mode 100644
index 00000000..ab189576
--- /dev/null
+++ b/jam-files/engine/filent.c
@@ -0,0 +1,387 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/* This file is ALSO:
+ * Copyright 2001-2004 David Abrahams.
+ * Copyright 2005 Rene Rivera.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+# include "jam.h"
+
+# include "filesys.h"
+# include "pathsys.h"
+# include "strings.h"
+# include "newstr.h"
+
+# ifdef OS_NT
+
+# ifdef __BORLANDC__
+# if __BORLANDC__ < 0x550
+# include <dir.h>
+# include <dos.h>
+# endif
+# undef FILENAME /* cpp namespace collision */
+# define _finddata_t ffblk
+# endif
+
+# include <io.h>
+# include <sys/stat.h>
+# include <ctype.h>
+# include <direct.h>
+
+/*
+ * filent.c - scan directories and archives on NT
+ *
+ * External routines:
+ *
+ * file_dirscan() - scan a directory for files
+ * file_time() - get timestamp of file, if not done by file_dirscan()
+ * file_archscan() - scan an archive for files
+ *
+ * File_dirscan() and file_archscan() call back a caller provided function
+ * for each file found. A flag to this callback function lets file_dirscan()
+ * and file_archscan() indicate that a timestamp is being provided with the
+ * file. If file_dirscan() or file_archscan() do not provide the file's
+ * timestamp, interested parties may later call file_time().
+ *
+ * 07/10/95 (taylor) Findfirst() returns the first file on NT.
+ * 05/03/96 (seiwald) split apart into pathnt.c
+ */
+
+/*
+ * file_dirscan() - scan a directory for files
+ */
+
+void file_dirscan( char * dir, scanback func, void * closure )
+{
+ PROFILE_ENTER( FILE_DIRSCAN );
+
+ file_info_t * d = 0;
+
+ dir = short_path_to_long_path( dir );
+
+ /* First enter directory itself */
+
+ d = file_query( dir );
+
+ if ( !d || !d->is_dir )
+ {
+ PROFILE_EXIT( FILE_DIRSCAN );
+ return;
+ }
+
+ if ( !d->files )
+ {
+ PATHNAME f;
+ string filespec[ 1 ];
+ string filename[ 1 ];
+ long handle;
+ int ret;
+ struct _finddata_t finfo[ 1 ];
+ LIST * files = L0;
+ int d_length = strlen( d->name );
+
+ memset( (char *)&f, '\0', sizeof( f ) );
+
+ f.f_dir.ptr = d->name;
+ f.f_dir.len = d_length;
+
+ /* Now enter contents of directory */
+
+ /* Prepare file search specification for the findfirst() API. */
+ if ( d_length == 0 )
+ string_copy( filespec, ".\\*" );
+ else
+ {
+ /*
+ * We can not simply assume the given folder name will never include
+ * its trailing path separator or otherwise we would not support the
+ * Windows root folder specified without its drive letter, i.e. '\'.
+ */
+ char trailingChar = d->name[ d_length - 1 ] ;
+ string_copy( filespec, d->name );
+ if ( ( trailingChar != '\\' ) && ( trailingChar != '/' ) )
+ string_append( filespec, "\\" );
+ string_append( filespec, "*" );
+ }
+
+ if ( DEBUG_BINDSCAN )
+ printf( "scan directory %s\n", dir );
+
+ #if defined(__BORLANDC__) && __BORLANDC__ < 0x550
+ if ( ret = findfirst( filespec->value, finfo, FA_NORMAL | FA_DIREC ) )
+ {
+ string_free( filespec );
+ PROFILE_EXIT( FILE_DIRSCAN );
+ return;
+ }
+
+ string_new ( filename );
+ while ( !ret )
+ {
+ file_info_t * ff = 0;
+
+ f.f_base.ptr = finfo->ff_name;
+ f.f_base.len = strlen( finfo->ff_name );
+
+ string_truncate( filename, 0 );
+ path_build( &f, filename );
+
+ files = list_new( files, newstr(filename->value) );
+ ff = file_info( filename->value );
+ ff->is_file = finfo->ff_attrib & FA_DIREC ? 0 : 1;
+ ff->is_dir = finfo->ff_attrib & FA_DIREC ? 1 : 0;
+ ff->size = finfo->ff_fsize;
+ ff->time = (finfo->ff_ftime << 16) | finfo->ff_ftime;
+
+ ret = findnext( finfo );
+ }
+ # else
+ handle = _findfirst( filespec->value, finfo );
+
+ if ( ret = ( handle < 0L ) )
+ {
+ string_free( filespec );
+ PROFILE_EXIT( FILE_DIRSCAN );
+ return;
+ }
+
+ string_new( filename );
+ while ( !ret )
+ {
+ file_info_t * ff = 0;
+
+ f.f_base.ptr = finfo->name;
+ f.f_base.len = strlen( finfo->name );
+
+ string_truncate( filename, 0 );
+ path_build( &f, filename, 0 );
+
+ files = list_new( files, newstr( filename->value ) );
+ ff = file_info( filename->value );
+ ff->is_file = finfo->attrib & _A_SUBDIR ? 0 : 1;
+ ff->is_dir = finfo->attrib & _A_SUBDIR ? 1 : 0;
+ ff->size = finfo->size;
+ ff->time = finfo->time_write;
+
+ ret = _findnext( handle, finfo );
+ }
+
+ _findclose( handle );
+ # endif
+ string_free( filename );
+ string_free( filespec );
+
+ d->files = files;
+ }
+
+ /* Special case \ or d:\ : enter it */
+ {
+ unsigned long len = strlen(d->name);
+ if ( len == 1 && d->name[0] == '\\' )
+ (*func)( closure, d->name, 1 /* stat()'ed */, d->time );
+ else if ( len == 3 && d->name[1] == ':' ) {
+ (*func)( closure, d->name, 1 /* stat()'ed */, d->time );
+ /* We've just entered 3-letter drive name spelling (with trailing
+ slash), into the hash table. Now enter two-letter variant,
+ without trailing slash, so that if we try to check whether
+ "c:" exists, we hit it.
+
+ Jam core has workarounds for that. Given:
+ x = c:\whatever\foo ;
+ p = $(x:D) ;
+ p2 = $(p:D) ;
+ There will be no trailing slash in $(p), but there will be one
+ in $(p2). But, that seems rather fragile.
+ */
+ d->name[2] = 0;
+ (*func)( closure, d->name, 1 /* stat()'ed */, d->time );
+ }
+ }
+
+ /* Now enter contents of directory */
+ if ( d->files )
+ {
+ LIST * files = d->files;
+ while ( files )
+ {
+ file_info_t * ff = file_info( files->string );
+ (*func)( closure, ff->name, 1 /* stat()'ed */, ff->time );
+ files = list_next( files );
+ }
+ }
+
+ PROFILE_EXIT( FILE_DIRSCAN );
+}
+
+file_info_t * file_query( char * filename )
+{
+ file_info_t * ff = file_info( filename );
+ if ( ! ff->time )
+ {
+ struct stat statbuf;
+
+ if ( stat( *filename ? filename : ".", &statbuf ) < 0 )
+ return 0;
+
+ ff->is_file = statbuf.st_mode & S_IFREG ? 1 : 0;
+ ff->is_dir = statbuf.st_mode & S_IFDIR ? 1 : 0;
+ ff->size = statbuf.st_size;
+ ff->time = statbuf.st_mtime ? statbuf.st_mtime : 1;
+ }
+ return ff;
+}
+
+/*
+ * file_time() - get timestamp of file, if not done by file_dirscan()
+ */
+
+int
+file_time(
+ char *filename,
+ time_t *time )
+{
+ file_info_t * ff = file_query( filename );
+ if ( !ff ) return -1;
+ *time = ff->time;
+ return 0;
+}
+
+int file_is_file(char* filename)
+{
+ file_info_t * ff = file_query( filename );
+ if ( !ff ) return -1;
+ return ff->is_file;
+}
+
+int file_mkdir(char *pathname)
+{
+ return _mkdir(pathname);
+}
+
+/*
+ * file_archscan() - scan an archive for files
+ */
+
+/* Straight from SunOS */
+
+#define ARMAG "!<arch>\n"
+#define SARMAG 8
+
+#define ARFMAG "`\n"
+
+struct ar_hdr {
+ char ar_name[16];
+ char ar_date[12];
+ char ar_uid[6];
+ char ar_gid[6];
+ char ar_mode[8];
+ char ar_size[10];
+ char ar_fmag[2];
+};
+
+# define SARFMAG 2
+# define SARHDR sizeof( struct ar_hdr )
+
+void
+file_archscan(
+ char *archive,
+ scanback func,
+ void *closure )
+{
+ struct ar_hdr ar_hdr;
+ char *string_table = 0;
+ char buf[ MAXJPATH ];
+ long offset;
+ int fd;
+
+ if ( ( fd = open( archive, O_RDONLY | O_BINARY, 0 ) ) < 0 )
+ return;
+
+ if ( read( fd, buf, SARMAG ) != SARMAG ||
+ strncmp( ARMAG, buf, SARMAG ) )
+ {
+ close( fd );
+ return;
+ }
+
+ offset = SARMAG;
+
+ if ( DEBUG_BINDSCAN )
+ printf( "scan archive %s\n", archive );
+
+ while ( ( read( fd, &ar_hdr, SARHDR ) == SARHDR ) &&
+ !memcmp( ar_hdr.ar_fmag, ARFMAG, SARFMAG ) )
+ {
+ long lar_date;
+ long lar_size;
+ char *name = 0;
+ char *endname;
+ char *c;
+
+ sscanf( ar_hdr.ar_date, "%ld", &lar_date );
+ sscanf( ar_hdr.ar_size, "%ld", &lar_size );
+
+ lar_size = ( lar_size + 1 ) & ~1;
+
+ if (ar_hdr.ar_name[0] == '/' && ar_hdr.ar_name[1] == '/' )
+ {
+ /* this is the "string table" entry of the symbol table,
+ ** which holds strings of filenames that are longer than
+ ** 15 characters (ie. don't fit into a ar_name
+ */
+
+ string_table = BJAM_MALLOC_ATOMIC(lar_size+1);
+ if (read(fd, string_table, lar_size) != lar_size)
+ printf("error reading string table\n");
+ string_table[lar_size] = '\0';
+ offset += SARHDR + lar_size;
+ continue;
+ }
+ else if (ar_hdr.ar_name[0] == '/' && ar_hdr.ar_name[1] != ' ')
+ {
+ /* Long filenames are recognized by "/nnnn" where nnnn is
+ ** the offset of the string in the string table represented
+ ** in ASCII decimals.
+ */
+
+ name = string_table + atoi( ar_hdr.ar_name + 1 );
+ for ( endname = name; *endname && *endname != '\n'; ++endname) {}
+ }
+ else
+ {
+ /* normal name */
+ name = ar_hdr.ar_name;
+ endname = name + sizeof( ar_hdr.ar_name );
+ }
+
+ /* strip trailing white-space, slashes, and backslashes */
+
+ while ( endname-- > name )
+ if ( !isspace(*endname) && ( *endname != '\\' ) && ( *endname != '/' ) )
+ break;
+ *++endname = 0;
+
+ /* strip leading directory names, an NT specialty */
+
+ if ( c = strrchr( name, '/' ) )
+ name = c + 1;
+ if ( c = strrchr( name, '\\' ) )
+ name = c + 1;
+
+ sprintf( buf, "%s(%.*s)", archive, endname - name, name );
+ (*func)( closure, buf, 1 /* time valid */, (time_t)lar_date );
+
+ offset += SARHDR + lar_size;
+ lseek( fd, offset, 0 );
+ }
+
+ close( fd );
+}
+
+# endif /* NT */
diff --git a/jam-files/engine/fileos2.c b/jam-files/engine/fileos2.c
new file mode 100644
index 00000000..af2373ea
--- /dev/null
+++ b/jam-files/engine/fileos2.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/* This file is ALSO:
+ * Copyright 2001-2004 David Abrahams.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+# include "jam.h"
+# include "filesys.h"
+# include "pathsys.h"
+
+/* note that we use "fileunix.c" when compiling with EMX on OS/2 */
+# if defined(OS_OS2) && !defined(__EMX__)
+
+# include <io.h>
+# include <dos.h>
+
+/*
+ * fileos2.c - scan directories and archives on NT
+ *
+ * External routines:
+ *
+ * file_dirscan() - scan a directory for files
+ * file_time() - get timestamp of file, if not done by file_dirscan()
+ * file_archscan() - scan an archive for files
+ *
+ * File_dirscan() and file_archscan() call back a caller provided function
+ * for each file found. A flag to this callback function lets file_dirscan()
+ * and file_archscan() indicate that a timestamp is being provided with the
+ * file. If file_dirscan() or file_archscan() do not provide the file's
+ * timestamp, interested parties may later call file_time().
+ *
+ * 07/10/95 (taylor) Findfirst() returns the first file on NT.
+ * 05/03/96 (seiwald) split apart into pathnt.c
+ * 09/22/00 (seiwald) handle \ and c:\ specially: don't add extra /
+ */
+
+/*
+ * file_dirscan() - scan a directory for files
+ */
+
+void
+file_dirscan(
+ char *dir,
+ scanback func,
+ void *closure )
+{
+ PATHNAME f;
+ string filespec[1];
+ long handle;
+ int ret;
+ struct _find_t finfo[1];
+
+ /* First enter directory itself */
+
+ memset( (char *)&f, '\0', sizeof( f ) );
+
+ f.f_dir.ptr = dir;
+ f.f_dir.len = strlen(dir);
+
+ dir = *dir ? dir : ".";
+
+ /* Special case \ or d:\ : enter it */
+ string_copy( filespec, dir );
+
+ if ( f.f_dir.len == 1 && f.f_dir.ptr[0] == '\\' )
+ (*func)( closure, dir, 0 /* not stat()'ed */, (time_t)0 );
+ else if ( f.f_dir.len == 3 && f.f_dir.ptr[1] == ':' )
+ (*func)( closure, dir, 0 /* not stat()'ed */, (time_t)0 );
+ else
+ string_push_back( filespec, '/' );
+
+ string_push_back( filespec, '*' );
+
+ /* Now enter contents of directory */
+
+ if ( DEBUG_BINDSCAN )
+ printf( "scan directory %s\n", filespec->value );
+
+ /* Time info in dos find_t is not very useful. It consists */
+ /* of a separate date and time, and putting them together is */
+ /* not easy. So we leave that to a later stat() call. */
+
+ if ( !_dos_findfirst( filespec->value, _A_NORMAL|_A_RDONLY|_A_SUBDIR, finfo ) )
+ {
+ string filename[1];
+ string_new( filename );
+ do
+ {
+ f.f_base.ptr = finfo->name;
+ f.f_base.len = strlen( finfo->name );
+
+ string_truncate( filename, 0 );
+ path_build( &f, filename, 0 );
+ (*func)( closure, filename->value, 0 /* not stat()'ed */, (time_t)0 );
+ }
+ while ( !_dos_findnext( finfo ) );
+ string_free( filename );
+ }
+}
+
+/*
+ * file_time() - get timestamp of file, if not done by file_dirscan()
+ */
+
+int
+file_time(
+ char *filename,
+ time_t *time )
+{
+ /* This is called on OS2, not NT. */
+ /* NT fills in the time in the dirscan. */
+
+ struct stat statbuf;
+
+ if ( stat( filename, &statbuf ) < 0 )
+ return -1;
+
+ *time = statbuf.st_mtime;
+
+ return 0;
+}
+
+void
+file_archscan(
+ char *archive,
+ scanback func,
+ void *closure )
+{
+}
+
+# endif /* OS2 && !__EMX__ */
+
diff --git a/jam-files/engine/filesys.c b/jam-files/engine/filesys.c
new file mode 100644
index 00000000..eb62ed40
--- /dev/null
+++ b/jam-files/engine/filesys.c
@@ -0,0 +1,83 @@
+# include "jam.h"
+# include "pathsys.h"
+# include "strings.h"
+# include "newstr.h"
+# include "filesys.h"
+# include "lists.h"
+
+void file_build1( PATHNAME * f, string * file )
+{
+ if ( DEBUG_SEARCH )
+ {
+ printf("build file: ");
+ if ( f->f_root.len )
+ printf( "root = '%.*s' ", f->f_root.len, f->f_root.ptr );
+ if ( f->f_dir.len )
+ printf( "dir = '%.*s' ", f->f_dir.len, f->f_dir.ptr );
+ if ( f->f_base.len )
+ printf( "base = '%.*s' ", f->f_base.len, f->f_base.ptr );
+ printf( "\n" );
+ }
+
+ /* Start with the grist. If the current grist isn't */
+ /* surrounded by <>'s, add them. */
+
+ if ( f->f_grist.len )
+ {
+ if ( f->f_grist.ptr[0] != '<' )
+ string_push_back( file, '<' );
+ string_append_range(
+ file, f->f_grist.ptr, f->f_grist.ptr + f->f_grist.len );
+ if ( file->value[file->size - 1] != '>' )
+ string_push_back( file, '>' );
+ }
+}
+
+static struct hash * filecache_hash = 0;
+static file_info_t filecache_finfo;
+
+file_info_t * file_info(char * filename)
+{
+ file_info_t *finfo = &filecache_finfo;
+
+ if ( !filecache_hash )
+ filecache_hash = hashinit( sizeof( file_info_t ), "file_info" );
+
+ finfo->name = filename;
+ finfo->is_file = 0;
+ finfo->is_dir = 0;
+ finfo->size = 0;
+ finfo->time = 0;
+ finfo->files = 0;
+ if ( hashenter( filecache_hash, (HASHDATA**)&finfo ) )
+ {
+ /* printf( "file_info: %s\n", filename ); */
+ finfo->name = newstr( finfo->name );
+ }
+
+ return finfo;
+}
+
+static LIST * files_to_remove = L0;
+
+static void remove_files_atexit(void)
+{
+ /* we do pop front in case this exit function is called
+ more than once */
+ while ( files_to_remove )
+ {
+ remove( files_to_remove->string );
+ files_to_remove = list_pop_front( files_to_remove );
+ }
+}
+
+void file_done()
+{
+ remove_files_atexit();
+ hashdone( filecache_hash );
+}
+
+void file_remove_atexit( const char * path )
+{
+ files_to_remove = list_new( files_to_remove, newstr((char*)path) );
+}
diff --git a/jam-files/engine/filesys.h b/jam-files/engine/filesys.h
new file mode 100644
index 00000000..efc081d1
--- /dev/null
+++ b/jam-files/engine/filesys.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/* This file is ALSO:
+ * Copyright 2001-2004 David Abrahams.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+/*
+ * filesys.h - OS specific file routines
+ */
+
+#ifndef FILESYS_DWA20011025_H
+# define FILESYS_DWA20011025_H
+
+# include "pathsys.h"
+#include "hash.h"
+#include "lists.h"
+
+typedef void (*scanback)( void *closure, char *file, int found, time_t t );
+
+void file_dirscan( char *dir, scanback func, void *closure );
+void file_archscan( char *arch, scanback func, void *closure );
+
+int file_time( char *filename, time_t *time );
+
+void file_build1(PATHNAME *f, string* file) ;
+int file_is_file(char* filename);
+int file_mkdir(char *pathname);
+
+typedef struct file_info_t file_info_t ;
+struct file_info_t
+{
+ char * name;
+ short is_file;
+ short is_dir;
+ unsigned long size;
+ time_t time;
+ LIST * files;
+};
+
+
+/* Creates a pointer to information about file 'filename', creating it as
+ * necessary. If created, the structure will be default initialized.
+ */
+file_info_t * file_info( char * filename );
+
+/* Returns information about a file, queries the OS if needed. */
+file_info_t * file_query( char * filename );
+
+void file_done();
+
+/* Marks a path/file to be removed when jam exits. */
+void file_remove_atexit( const char * path );
+
+#endif
diff --git a/jam-files/engine/fileunix.c b/jam-files/engine/fileunix.c
new file mode 100644
index 00000000..680c3f53
--- /dev/null
+++ b/jam-files/engine/fileunix.c
@@ -0,0 +1,501 @@
+/*
+ * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/* This file is ALSO:
+ * Copyright 2001-2004 David Abrahams.
+ * Copyright 2005 Rene Rivera.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+# include "jam.h"
+# include "filesys.h"
+# include "strings.h"
+# include "pathsys.h"
+# include "newstr.h"
+# include <stdio.h>
+# include <sys/stat.h>
+
+#if defined(sun) || defined(__sun) || defined(linux)
+# include <unistd.h> /* needed for read and close prototype */
+#endif
+
+# ifdef USE_FILEUNIX
+
+#if defined(sun) || defined(__sun)
+# include <unistd.h> /* needed for read and close prototype */
+#endif
+
+# if defined( OS_SEQUENT ) || \
+ defined( OS_DGUX ) || \
+ defined( OS_SCO ) || \
+ defined( OS_ISC )
+# define PORTAR 1
+# endif
+
+# ifdef __EMX__
+# include <sys/types.h>
+# include <sys/stat.h>
+# endif
+
+# if defined( OS_RHAPSODY ) || \
+ defined( OS_MACOSX ) || \
+ defined( OS_NEXT )
+/* need unistd for rhapsody's proper lseek */
+# include <sys/dir.h>
+# include <unistd.h>
+# define STRUCT_DIRENT struct direct
+# else
+# include <dirent.h>
+# define STRUCT_DIRENT struct dirent
+# endif
+
+# ifdef OS_COHERENT
+# include <arcoff.h>
+# define HAVE_AR
+# endif
+
+# if defined( OS_MVS ) || \
+ defined( OS_INTERIX )
+
+#define ARMAG "!<arch>\n"
+#define SARMAG 8
+#define ARFMAG "`\n"
+
+struct ar_hdr /* archive file member header - printable ascii */
+{
+ char ar_name[16]; /* file member name - `/' terminated */
+ char ar_date[12]; /* file member date - decimal */
+ char ar_uid[6]; /* file member user id - decimal */
+ char ar_gid[6]; /* file member group id - decimal */
+ char ar_mode[8]; /* file member mode - octal */
+ char ar_size[10]; /* file member size - decimal */
+ char ar_fmag[2]; /* ARFMAG - string to end header */
+};
+
+# define HAVE_AR
+# endif
+
+# if defined( OS_QNX ) || \
+ defined( OS_BEOS ) || \
+ defined( OS_MPEIX )
+# define NO_AR
+# define HAVE_AR
+# endif
+
+# ifndef HAVE_AR
+
+# ifdef OS_AIX
+/* Define those for AIX to get the definitions for both the small and the
+ * big variant of the archive file format. */
+# define __AR_SMALL__
+# define __AR_BIG__
+# endif
+
+# include <ar.h>
+# endif
+
+/*
+ * fileunix.c - manipulate file names and scan directories on UNIX/AmigaOS
+ *
+ * External routines:
+ *
+ * file_dirscan() - scan a directory for files
+ * file_time() - get timestamp of file, if not done by file_dirscan()
+ * file_archscan() - scan an archive for files
+ *
+ * File_dirscan() and file_archscan() call back a caller provided function
+ * for each file found. A flag to this callback function lets file_dirscan()
+ * and file_archscan() indicate that a timestamp is being provided with the
+ * file. If file_dirscan() or file_archscan() do not provide the file's
+ * timestamp, interested parties may later call file_time().
+ *
+ * 04/08/94 (seiwald) - Coherent/386 support added.
+ * 12/19/94 (mikem) - solaris string table insanity support
+ * 02/14/95 (seiwald) - parse and build /xxx properly
+ * 05/03/96 (seiwald) - split into pathunix.c
+ * 11/21/96 (peterk) - BEOS does not have Unix-style archives
+ */
+
+
+/*
+ * file_dirscan() - scan a directory for files.
+ */
+
+void file_dirscan( char * dir, scanback func, void * closure )
+{
+ PROFILE_ENTER( FILE_DIRSCAN );
+
+ file_info_t * d = 0;
+
+ d = file_query( dir );
+
+ if ( !d || !d->is_dir )
+ {
+ PROFILE_EXIT( FILE_DIRSCAN );
+ return;
+ }
+
+ if ( ! d->files )
+ {
+ LIST* files = L0;
+ PATHNAME f;
+ DIR *dd;
+ STRUCT_DIRENT *dirent;
+ string filename[1];
+
+ /* First enter directory itself */
+
+ memset( (char *)&f, '\0', sizeof( f ) );
+
+ f.f_dir.ptr = dir;
+ f.f_dir.len = strlen(dir);
+
+ dir = *dir ? dir : ".";
+
+ /* Now enter contents of directory. */
+
+ if ( !( dd = opendir( dir ) ) )
+ {
+ PROFILE_EXIT( FILE_DIRSCAN );
+ return;
+ }
+
+ if ( DEBUG_BINDSCAN )
+ printf( "scan directory %s\n", dir );
+
+ string_new( filename );
+ while ( ( dirent = readdir( dd ) ) )
+ {
+ # ifdef old_sinix
+ /* Broken structure definition on sinix. */
+ f.f_base.ptr = dirent->d_name - 2;
+ # else
+ f.f_base.ptr = dirent->d_name;
+ # endif
+ f.f_base.len = strlen( f.f_base.ptr );
+
+ string_truncate( filename, 0 );
+ path_build( &f, filename, 0 );
+
+ files = list_new( files, newstr(filename->value) );
+ file_query( filename->value );
+ }
+ string_free( filename );
+
+ closedir( dd );
+
+ d->files = files;
+ }
+
+ /* Special case / : enter it */
+ {
+ unsigned long len = strlen(d->name);
+ if ( ( len == 1 ) && ( d->name[0] == '/' ) )
+ (*func)( closure, d->name, 1 /* stat()'ed */, d->time );
+ }
+
+ /* Now enter contents of directory */
+ if ( d->files )
+ {
+ LIST * files = d->files;
+ while ( files )
+ {
+ file_info_t * ff = file_info( files->string );
+ (*func)( closure, ff->name, 1 /* stat()'ed */, ff->time );
+ files = list_next( files );
+ }
+ }
+
+ PROFILE_EXIT( FILE_DIRSCAN );
+}
+
+
+file_info_t * file_query( char * filename )
+{
+ file_info_t * ff = file_info( filename );
+ if ( ! ff->time )
+ {
+ struct stat statbuf;
+
+ if ( stat( *filename ? filename : ".", &statbuf ) < 0 )
+ return 0;
+
+ ff->is_file = statbuf.st_mode & S_IFREG ? 1 : 0;
+ ff->is_dir = statbuf.st_mode & S_IFDIR ? 1 : 0;
+ ff->size = statbuf.st_size;
+ ff->time = statbuf.st_mtime ? statbuf.st_mtime : 1;
+ }
+ return ff;
+}
+
+/*
+ * file_time() - get timestamp of file, if not done by file_dirscan()
+ */
+
+int
+file_time(
+ char *filename,
+ time_t *time )
+{
+ file_info_t * ff = file_query( filename );
+ if ( !ff ) return -1;
+ *time = ff->time;
+ return 0;
+}
+
+int file_is_file(char* filename)
+{
+ file_info_t * ff = file_query( filename );
+ if ( !ff ) return -1;
+ return ff->is_file;
+}
+
+int file_mkdir(char* pathname)
+{
+ return mkdir(pathname, 0766);
+}
+
+/*
+ * file_archscan() - scan an archive for files
+ */
+
+# ifndef AIAMAG /* God-fearing UNIX */
+
+# define SARFMAG 2
+# define SARHDR sizeof( struct ar_hdr )
+
+void
+file_archscan(
+ char *archive,
+ scanback func,
+ void *closure )
+{
+# ifndef NO_AR
+ struct ar_hdr ar_hdr;
+ char buf[ MAXJPATH ];
+ long offset;
+ char *string_table = 0;
+ int fd;
+
+ if ( ( fd = open( archive, O_RDONLY, 0 ) ) < 0 )
+ return;
+
+ if ( read( fd, buf, SARMAG ) != SARMAG ||
+ strncmp( ARMAG, buf, SARMAG ) )
+ {
+ close( fd );
+ return;
+ }
+
+ offset = SARMAG;
+
+ if ( DEBUG_BINDSCAN )
+ printf( "scan archive %s\n", archive );
+
+ while ( ( read( fd, &ar_hdr, SARHDR ) == SARHDR )
+ && !( memcmp( ar_hdr.ar_fmag, ARFMAG, SARFMAG )
+#ifdef ARFZMAG
+ /* OSF also has a compressed format */
+ && memcmp( ar_hdr.ar_fmag, ARFZMAG, SARFMAG )
+#endif
+ ) )
+ {
+ char lar_name_[257];
+ char * lar_name = lar_name_ + 1;
+ long lar_date;
+ long lar_size;
+ long lar_offset;
+ char * c;
+ char * src;
+ char * dest;
+
+ strncpy( lar_name, ar_hdr.ar_name, sizeof(ar_hdr.ar_name) );
+
+ sscanf( ar_hdr.ar_date, "%ld", &lar_date );
+ sscanf( ar_hdr.ar_size, "%ld", &lar_size );
+
+ if (ar_hdr.ar_name[0] == '/')
+ {
+ if (ar_hdr.ar_name[1] == '/')
+ {
+ /* this is the "string table" entry of the symbol table,
+ ** which holds strings of filenames that are longer than
+ ** 15 characters (ie. don't fit into a ar_name
+ */
+
+ string_table = (char *)BJAM_MALLOC_ATOMIC(lar_size);
+ lseek(fd, offset + SARHDR, 0);
+ if (read(fd, string_table, lar_size) != lar_size)
+ printf("error reading string table\n");
+ }
+ else if (string_table && ar_hdr.ar_name[1] != ' ')
+ {
+ /* Long filenames are recognized by "/nnnn" where nnnn is
+ ** the offset of the string in the string table represented
+ ** in ASCII decimals.
+ */
+ dest = lar_name;
+ lar_offset = atoi(lar_name + 1);
+ src = &string_table[lar_offset];
+ while (*src != '/')
+ *dest++ = *src++;
+ *dest = '/';
+ }
+ }
+
+ c = lar_name - 1;
+ while ( ( *++c != ' ' ) && ( *c != '/' ) ) ;
+ *c = '\0';
+
+ if ( DEBUG_BINDSCAN )
+ printf( "archive name %s found\n", lar_name );
+
+ sprintf( buf, "%s(%s)", archive, lar_name );
+
+ (*func)( closure, buf, 1 /* time valid */, (time_t)lar_date );
+
+ offset += SARHDR + ( ( lar_size + 1 ) & ~1 );
+ lseek( fd, offset, 0 );
+ }
+
+ if (string_table)
+ BJAM_FREE(string_table);
+
+ close( fd );
+
+# endif /* NO_AR */
+
+}
+
+# else /* AIAMAG - RS6000 AIX */
+
+static void file_archscan_small(
+ int fd, char const *archive, scanback func, void *closure)
+{
+ struct fl_hdr fl_hdr;
+
+ struct {
+ struct ar_hdr hdr;
+ char pad[ 256 ];
+ } ar_hdr ;
+
+ char buf[ MAXJPATH ];
+ long offset;
+
+ if ( read( fd, (char *)&fl_hdr, FL_HSZ ) != FL_HSZ)
+ return;
+
+ sscanf( fl_hdr.fl_fstmoff, "%ld", &offset );
+
+ if ( DEBUG_BINDSCAN )
+ printf( "scan archive %s\n", archive );
+
+ while ( ( offset > 0 )
+ && ( lseek( fd, offset, 0 ) >= 0 )
+ && ( read( fd, &ar_hdr, sizeof( ar_hdr ) ) >= sizeof( ar_hdr.hdr ) ) )
+ {
+ long lar_date;
+ int lar_namlen;
+
+ sscanf( ar_hdr.hdr.ar_namlen, "%d" , &lar_namlen );
+ sscanf( ar_hdr.hdr.ar_date , "%ld", &lar_date );
+ sscanf( ar_hdr.hdr.ar_nxtmem, "%ld", &offset );
+
+ if ( !lar_namlen )
+ continue;
+
+ ar_hdr.hdr._ar_name.ar_name[ lar_namlen ] = '\0';
+
+ sprintf( buf, "%s(%s)", archive, ar_hdr.hdr._ar_name.ar_name );
+
+ (*func)( closure, buf, 1 /* time valid */, (time_t)lar_date );
+ }
+}
+
+/* Check for OS version which supports the big variant. */
+#ifdef AR_HSZ_BIG
+
+static void file_archscan_big(
+ int fd, char const *archive, scanback func, void *closure)
+{
+ struct fl_hdr_big fl_hdr;
+
+ struct {
+ struct ar_hdr_big hdr;
+ char pad[ 256 ];
+ } ar_hdr ;
+
+ char buf[ MAXJPATH ];
+ long long offset;
+
+ if ( read( fd, (char *)&fl_hdr, FL_HSZ_BIG) != FL_HSZ_BIG)
+ return;
+
+ sscanf( fl_hdr.fl_fstmoff, "%lld", &offset );
+
+ if ( DEBUG_BINDSCAN )
+ printf( "scan archive %s\n", archive );
+
+ while ( ( offset > 0 )
+ && ( lseek( fd, offset, 0 ) >= 0 )
+ && ( read( fd, &ar_hdr, sizeof( ar_hdr ) ) >= sizeof( ar_hdr.hdr ) ) )
+ {
+ long lar_date;
+ int lar_namlen;
+
+ sscanf( ar_hdr.hdr.ar_namlen, "%d" , &lar_namlen );
+ sscanf( ar_hdr.hdr.ar_date , "%ld" , &lar_date );
+ sscanf( ar_hdr.hdr.ar_nxtmem, "%lld", &offset );
+
+ if ( !lar_namlen )
+ continue;
+
+ ar_hdr.hdr._ar_name.ar_name[ lar_namlen ] = '\0';
+
+ sprintf( buf, "%s(%s)", archive, ar_hdr.hdr._ar_name.ar_name );
+
+ (*func)( closure, buf, 1 /* time valid */, (time_t)lar_date );
+ }
+
+}
+
+#endif /* AR_HSZ_BIG */
+
+void file_archscan(char *archive, scanback func, void *closure)
+{
+ int fd;
+ char fl_magic[SAIAMAG];
+
+ if (( fd = open(archive, O_RDONLY, 0)) < 0)
+ return;
+
+ if (read( fd, fl_magic, SAIAMAG) != SAIAMAG
+ || lseek(fd, 0, SEEK_SET) == -1)
+ {
+ close(fd);
+ return;
+ }
+
+ if (strncmp(AIAMAG, fl_magic, SAIAMAG) == 0)
+ {
+ /* read small variant */
+ file_archscan_small(fd, archive, func, closure);
+ }
+#ifdef AR_HSZ_BIG
+ else if (strncmp(AIAMAGBIG, fl_magic, SAIAMAG) == 0)
+ {
+ /* read big variant */
+ file_archscan_big(fd, archive, func, closure);
+ }
+#endif
+
+ close( fd );
+}
+
+# endif /* AIAMAG - RS6000 AIX */
+
+# endif /* USE_FILEUNIX */
diff --git a/jam-files/engine/filevms.c b/jam-files/engine/filevms.c
new file mode 100644
index 00000000..d2ab2047
--- /dev/null
+++ b/jam-files/engine/filevms.c
@@ -0,0 +1,327 @@
+/*
+ * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/* This file is ALSO:
+ * Copyright 2001-2004 David Abrahams.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+# include "jam.h"
+# include "filesys.h"
+# include "pathsys.h"
+
+# ifdef OS_VMS
+
+/*
+ * filevms.c - scan directories and libaries on VMS
+ *
+ * External routines:
+ *
+ * file_dirscan() - scan a directory for files
+ * file_time() - get timestamp of file, if not done by file_dirscan()
+ * file_archscan() - scan an archive for files
+ *
+ * File_dirscan() and file_archscan() call back a caller provided function
+ * for each file found. A flag to this callback function lets file_dirscan()
+ * and file_archscan() indicate that a timestamp is being provided with the
+ * file. If file_dirscan() or file_archscan() do not provide the file's
+ * timestamp, interested parties may later call file_time().
+ *
+ * 02/09/95 (seiwald) - bungled R=[xxx] - was using directory length!
+ * 05/03/96 (seiwald) - split into pathvms.c
+ */
+
+# include <rms.h>
+# include <iodef.h>
+# include <ssdef.h>
+# include <string.h>
+# include <stdlib.h>
+# include <stdio.h>
+# include <descrip.h>
+
+#include <lbrdef.h>
+#include <credef.h>
+#include <mhddef.h>
+#include <lhidef.h>
+#include <lib$routines.h>
+#include <starlet.h>
+
+/* Supply missing prototypes for lbr$-routines*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+int lbr$set_module(
+ void **,
+ unsigned long *,
+ struct dsc$descriptor_s *,
+ unsigned short *,
+ void * );
+
+int lbr$open( void **,
+ struct dsc$descriptor_s *,
+ void *,
+ void *,
+ void *,
+ void *,
+ void * );
+
+int lbr$ini_control(
+ void **,
+ unsigned long *,
+ unsigned long *,
+ void * );
+
+int lbr$get_index(
+ void **,
+ unsigned long *,
+ int (*func)( struct dsc$descriptor_s *, unsigned long *),
+ void * );
+
+int lbr$close(
+ void ** );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+static void
+file_cvttime(
+ unsigned int *curtime,
+ time_t *unixtime )
+{
+ static const size_t divisor = 10000000;
+ static unsigned int bastim[2] = { 0x4BEB4000, 0x007C9567 }; /* 1/1/1970 */
+ int delta[2], remainder;
+
+ lib$subx( curtime, bastim, delta );
+ lib$ediv( &divisor, delta, unixtime, &remainder );
+}
+
+# define DEFAULT_FILE_SPECIFICATION "[]*.*;0"
+
+# define min( a,b ) ((a)<(b)?(a):(b))
+
+void
+file_dirscan(
+ char *dir,
+ scanback func,
+ void *closure )
+{
+
+ struct FAB xfab;
+ struct NAM xnam;
+ struct XABDAT xab;
+ char esa[256];
+ char filename[256];
+ string filename2[1];
+ char dirname[256];
+ register int status;
+ PATHNAME f;
+
+ memset( (char *)&f, '\0', sizeof( f ) );
+
+ f.f_root.ptr = dir;
+ f.f_root.len = strlen( dir );
+
+ /* get the input file specification
+ */
+ xnam = cc$rms_nam;
+ xnam.nam$l_esa = esa;
+ xnam.nam$b_ess = sizeof( esa ) - 1;
+ xnam.nam$l_rsa = filename;
+ xnam.nam$b_rss = min( sizeof( filename ) - 1, NAM$C_MAXRSS );
+
+ xab = cc$rms_xabdat; /* initialize extended attributes */
+ xab.xab$b_cod = XAB$C_DAT; /* ask for date */
+ xab.xab$l_nxt = NULL; /* terminate XAB chain */
+
+ xfab = cc$rms_fab;
+ xfab.fab$l_dna = DEFAULT_FILE_SPECIFICATION;
+ xfab.fab$b_dns = sizeof( DEFAULT_FILE_SPECIFICATION ) - 1;
+ xfab.fab$l_fop = FAB$M_NAM;
+ xfab.fab$l_fna = dir; /* address of file name */
+ xfab.fab$b_fns = strlen( dir ); /* length of file name */
+ xfab.fab$l_nam = &xnam; /* address of NAB block */
+ xfab.fab$l_xab = (char *)&xab; /* address of XAB block */
+
+
+ status = sys$parse( &xfab );
+
+ if ( DEBUG_BINDSCAN )
+ printf( "scan directory %s\n", dir );
+
+ if ( !( status & 1 ) )
+ return;
+
+
+
+ /* Add bogus directory for [000000] */
+
+ if ( !strcmp( dir, "[000000]" ) )
+ {
+ (*func)( closure, "[000000]", 1 /* time valid */, 1 /* old but true */ );
+ }
+
+ /* Add bogus directory for [] */
+
+ if ( !strcmp( dir, "[]" ) )
+ {
+ (*func)( closure, "[]", 1 /* time valid */, 1 /* old but true */ );
+ (*func)( closure, "[-]", 1 /* time valid */, 1 /* old but true */ );
+ }
+
+ string_new( filename2 );
+ while ( (status = sys$search( &xfab )) & 1 )
+ {
+ char *s;
+ time_t time;
+
+ /* "I think that might work" - eml */
+
+ sys$open( &xfab );
+ sys$close( &xfab );
+
+ file_cvttime( (unsigned int *)&xab.xab$q_rdt, &time );
+
+ filename[xnam.nam$b_rsl] = '\0';
+
+ /* What we do with the name depends on the suffix: */
+ /* .dir is a directory */
+ /* .xxx is a file with a suffix */
+ /* . is no suffix at all */
+
+ if ( xnam.nam$b_type == 4 && !strncmp( xnam.nam$l_type, ".DIR", 4 ) )
+ {
+ /* directory */
+ sprintf( dirname, "[.%.*s]", xnam.nam$b_name, xnam.nam$l_name );
+ f.f_dir.ptr = dirname;
+ f.f_dir.len = strlen( dirname );
+ f.f_base.ptr = 0;
+ f.f_base.len = 0;
+ f.f_suffix.ptr = 0;
+ f.f_suffix.len = 0;
+ }
+ else
+ {
+ /* normal file with a suffix */
+ f.f_dir.ptr = 0;
+ f.f_dir.len = 0;
+ f.f_base.ptr = xnam.nam$l_name;
+ f.f_base.len = xnam.nam$b_name;
+ f.f_suffix.ptr = xnam.nam$l_type;
+ f.f_suffix.len = xnam.nam$b_type;
+ }
+
+ string_truncate( filename2, 0 );
+ path_build( &f, filename2, 0 );
+
+ /*
+ if ( DEBUG_SEARCH )
+ printf("root '%s' base %.*s suf %.*s = %s\n",
+ dir,
+ xnam.nam$b_name, xnam.nam$l_name,
+ xnam.nam$b_type, xnam.nam$l_type,
+ filename2 );
+ */
+
+ (*func)( closure, filename2->value, 1 /* time valid */, time );
+ }
+ string_free( filename2 );
+}
+
+int
+file_time(
+ char *filename,
+ time_t *time )
+{
+ /* This should never be called, as all files are */
+ /* timestampped in file_dirscan() and file_archscan() */
+ return -1;
+}
+
+static char *VMS_archive = 0;
+static scanback VMS_func;
+static void *VMS_closure;
+static void *context;
+
+static int
+file_archmember(
+ struct dsc$descriptor_s *module,
+ unsigned long *rfa )
+{
+ static struct dsc$descriptor_s bufdsc =
+ {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL};
+
+ struct mhddef *mhd;
+ char filename[128];
+ char buf[ MAXJPATH ];
+
+ int status;
+ time_t library_date;
+
+ register int i;
+ register char *p;
+
+ bufdsc.dsc$a_pointer = filename;
+ bufdsc.dsc$w_length = sizeof( filename );
+ status = lbr$set_module( &context, rfa, &bufdsc,
+ &bufdsc.dsc$w_length, NULL );
+
+ if ( !(status & 1) )
+ return ( 1 );
+
+ mhd = (struct mhddef *)filename;
+
+ file_cvttime( &mhd->mhd$l_datim, &library_date );
+
+ for ( i = 0, p = module->dsc$a_pointer; i < module->dsc$w_length; ++i, ++p )
+ filename[ i ] = *p;
+
+ filename[ i ] = '\0';
+
+ sprintf( buf, "%s(%s.obj)", VMS_archive, filename );
+
+ (*VMS_func)( VMS_closure, buf, 1 /* time valid */, (time_t)library_date );
+
+ return ( 1 );
+}
+
+
+void file_archscan( char * archive, scanback func, void * closure )
+{
+ static struct dsc$descriptor_s library =
+ {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL};
+
+ unsigned long lfunc = LBR$C_READ;
+ unsigned long typ = LBR$C_TYP_UNK;
+ unsigned long index = 1;
+
+ register int status;
+
+ VMS_archive = archive;
+ VMS_func = func;
+ VMS_closure = closure;
+
+ status = lbr$ini_control( &context, &lfunc, &typ, NULL );
+ if ( !( status & 1 ) )
+ return;
+
+ library.dsc$a_pointer = archive;
+ library.dsc$w_length = strlen( archive );
+
+ status = lbr$open( &context, &library, NULL, NULL, NULL, NULL, NULL );
+ if ( !( status & 1 ) )
+ return;
+
+ (void) lbr$get_index( &context, &index, file_archmember, NULL );
+
+ (void) lbr$close( &context );
+}
+
+# endif /* VMS */
diff --git a/jam-files/engine/frames.c b/jam-files/engine/frames.c
new file mode 100644
index 00000000..84889f09
--- /dev/null
+++ b/jam-files/engine/frames.c
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2001-2004 David Abrahams.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+# include "frames.h"
+# include "lists.h"
+
+void frame_init( FRAME* frame )
+{
+ frame->prev = 0;
+ lol_init(frame->args);
+ frame->module = root_module();
+ frame->rulename = "module scope";
+ frame->procedure = 0;
+}
+
+void frame_free( FRAME* frame )
+{
+ lol_free( frame->args );
+}
diff --git a/jam-files/engine/frames.h b/jam-files/engine/frames.h
new file mode 100644
index 00000000..693d77fa
--- /dev/null
+++ b/jam-files/engine/frames.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2001-2004 David Abrahams.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef FRAMES_DWA20011021_H
+#define FRAMES_DWA20011021_H
+
+#include "lists.h"
+#include "modules.h"
+
+typedef struct _PARSE PARSE;
+typedef struct frame FRAME;
+
+struct frame
+{
+ FRAME * prev;
+ /* The nearest enclosing frame for which module->user_module is true. */
+ FRAME * prev_user;
+ LOL args[ 1 ];
+ module_t * module;
+ PARSE * procedure;
+ char * rulename;
+};
+
+
+/* When call into Python is in progress, this variable points to the bjam frame
+ * that was current at the moment of call. When the call completes, the variable
+ * is not defined. Further, if Jam calls Python which calls Jam and so on, this
+ * variable only keeps the most recent Jam frame.
+ */
+extern struct frame * frame_before_python_call;
+
+void frame_init( FRAME * ); /* implemented in compile.c */
+void frame_free( FRAME * ); /* implemented in compile.c */
+
+#endif
diff --git a/jam-files/engine/glob.c b/jam-files/engine/glob.c
new file mode 100644
index 00000000..527d6c80
--- /dev/null
+++ b/jam-files/engine/glob.c
@@ -0,0 +1,152 @@
+/*
+ * Copyright 1994 Christopher Seiwald. All rights reserved.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * glob.c - match a string against a simple pattern
+ *
+ * Understands the following patterns:
+ *
+ * * any number of characters
+ * ? any single character
+ * [a-z] any single character in the range a-z
+ * [^a-z] any single character not in the range a-z
+ * \x match x
+ *
+ * External functions:
+ *
+ * glob() - match a string against a simple pattern
+ *
+ * Internal functions:
+ *
+ * globchars() - build a bitlist to check for character group match
+ */
+
+# include "jam.h"
+
+# define CHECK_BIT( tab, bit ) ( tab[ (bit)/8 ] & (1<<( (bit)%8 )) )
+# define BITLISTSIZE 16 /* bytes used for [chars] in compiled expr */
+
+static void globchars( char * s, char * e, char * b );
+
+
+/*
+ * glob() - match a string against a simple pattern.
+ */
+
+int glob( char * c, char * s )
+{
+ char bitlist[ BITLISTSIZE ];
+ char * here;
+
+ for ( ; ; )
+ switch ( *c++ )
+ {
+ case '\0':
+ return *s ? -1 : 0;
+
+ case '?':
+ if ( !*s++ )
+ return 1;
+ break;
+
+ case '[':
+ /* Scan for matching ]. */
+
+ here = c;
+ do if ( !*c++ ) return 1;
+ while ( ( here == c ) || ( *c != ']' ) );
+ ++c;
+
+ /* Build character class bitlist. */
+
+ globchars( here, c, bitlist );
+
+ if ( !CHECK_BIT( bitlist, *(unsigned char *)s ) )
+ return 1;
+ ++s;
+ break;
+
+ case '*':
+ here = s;
+
+ while ( *s )
+ ++s;
+
+ /* Try to match the rest of the pattern in a recursive */
+ /* call. If the match fails we'll back up chars, retrying. */
+
+ while ( s != here )
+ {
+ int r;
+
+ /* A fast path for the last token in a pattern. */
+ r = *c ? glob( c, s ) : *s ? -1 : 0;
+
+ if ( !r )
+ return 0;
+ if ( r < 0 )
+ return 1;
+ --s;
+ }
+ break;
+
+ case '\\':
+ /* Force literal match of next char. */
+ if ( !*c || ( *s++ != *c++ ) )
+ return 1;
+ break;
+
+ default:
+ if ( *s++ != c[ -1 ] )
+ return 1;
+ break;
+ }
+}
+
+
+/*
+ * globchars() - build a bitlist to check for character group match.
+ */
+
+static void globchars( char * s, char * e, char * b )
+{
+ int neg = 0;
+
+ memset( b, '\0', BITLISTSIZE );
+
+ if ( *s == '^' )
+ {
+ ++neg;
+ ++s;
+ }
+
+ while ( s < e )
+ {
+ int c;
+
+ if ( ( s + 2 < e ) && ( s[1] == '-' ) )
+ {
+ for ( c = s[0]; c <= s[2]; ++c )
+ b[ c/8 ] |= ( 1 << ( c % 8 ) );
+ s += 3;
+ }
+ else
+ {
+ c = *s++;
+ b[ c/8 ] |= ( 1 << ( c % 8 ) );
+ }
+ }
+
+ if ( neg )
+ {
+ int i;
+ for ( i = 0; i < BITLISTSIZE; ++i )
+ b[ i ] ^= 0377;
+ }
+
+ /* Do not include \0 in either $[chars] or $[^chars]. */
+ b[0] &= 0376;
+}
diff --git a/jam-files/engine/hash.c b/jam-files/engine/hash.c
new file mode 100644
index 00000000..fbd1a899
--- /dev/null
+++ b/jam-files/engine/hash.c
@@ -0,0 +1,459 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+# include "jam.h"
+# include "hash.h"
+# include "compile.h"
+# include <assert.h>
+
+/*
+ * hash.c - simple in-memory hashing routines
+ *
+ * External routines:
+ *
+ * hashinit() - initialize a hash table, returning a handle
+ * hashitem() - find a record in the table, and optionally enter a new one
+ * hashdone() - free a hash table, given its handle
+ *
+ * Internal routines:
+ *
+ * hashrehash() - resize and rebuild hp->tab, the hash table
+ *
+ * 4/29/93 - ensure ITEM's are aligned
+ */
+
+/* */
+#define HASH_DEBUG_PROFILE 1
+/* */
+
+char *hashsccssid="@(#)hash.c 1.14 () 6/20/88";
+
+/* Header attached to all data items entered into a hash table. */
+
+struct hashhdr
+{
+ struct item * next;
+ unsigned int keyval; /* for quick comparisons */
+};
+
+/* This structure overlays the one handed to hashenter(). Its actual size is
+ * given to hashinit().
+ */
+
+struct hashdata
+{
+ char * key;
+ /* rest of user data */
+};
+
+typedef struct item
+{
+ struct hashhdr hdr;
+ struct hashdata data;
+} ITEM ;
+
+# define MAX_LISTS 32
+
+struct hash
+{
+ /*
+ * the hash table, just an array of item pointers
+ */
+ struct {
+ int nel;
+ ITEM **base;
+ } tab;
+
+ int bloat; /* tab.nel / items.nel */
+ int inel; /* initial number of elements */
+
+ /*
+ * the array of records, maintained by these routines
+ * essentially a microallocator
+ */
+ struct {
+ int more; /* how many more ITEMs fit in lists[ list ] */
+ ITEM *free; /* free list of items */
+ char *next; /* where to put more ITEMs in lists[ list ] */
+ int datalen; /* length of records in this hash table */
+ int size; /* sizeof( ITEM ) + aligned datalen */
+ int nel; /* total ITEMs held by all lists[] */
+ int list; /* index into lists[] */
+
+ struct {
+ int nel; /* total ITEMs held by this list */
+ char *base; /* base of ITEMs array */
+ } lists[ MAX_LISTS ];
+ } items;
+
+ char * name; /* just for hashstats() */
+};
+
+static void hashrehash( struct hash *hp );
+static void hashstat( struct hash *hp );
+static void * hash_mem_alloc(size_t datalen, size_t size);
+static void hash_mem_free(size_t datalen, void * data);
+#ifdef OPT_BOEHM_GC
+static void hash_mem_finalizer(char * key, struct hash * hp);
+#endif
+
+static unsigned int jenkins_one_at_a_time_hash(const unsigned char *key)
+{
+ unsigned int hash = 0;
+
+ while ( *key )
+ {
+ hash += *key++;
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ }
+ hash += (hash << 3);
+ hash ^= (hash >> 11);
+ hash += (hash << 15);
+
+ return hash;
+}
+
+/*
+static unsigned int knuth_hash(const unsigned char *key)
+{
+ unsigned int keyval = *key;
+ while ( *key )
+ keyval = keyval * 2147059363 + *key++;
+ return keyval;
+}
+*/
+
+static unsigned int hash_keyval( const char * key_ )
+{
+ /*
+ return knuth_hash((const unsigned char *)key_);
+ */
+ return jenkins_one_at_a_time_hash((const unsigned char *)key_);
+}
+
+#define hash_bucket(hp,keyval) ((hp)->tab.base + ( (keyval) % (hp)->tab.nel ))
+
+/* Find the hash item for the given data. Returns pointer to the
+ item and if given a pointer to the item before the found item.
+ If it's the first item in a bucket, there is no previous item,
+ and zero is returned for the previous item instead.
+*/
+static ITEM * hash_search(
+ struct hash *hp,
+ unsigned int keyval,
+ const char * keydata,
+ ITEM * * previous )
+{
+ ITEM * i = *hash_bucket(hp,keyval);
+ ITEM * p = 0;
+
+ for ( ; i; i = i->hdr.next )
+ {
+ if ( ( keyval == i->hdr.keyval ) &&
+ !strcmp( i->data.key, keydata ) )
+ {
+ if (previous)
+ {
+ *previous = p;
+ }
+ return i;
+ }
+ p = i;
+ }
+
+ return 0;
+}
+
+/*
+ * hash_free() - remove the given item from the table if it's there.
+ * Returns 1 if found, 0 otherwise.
+ *
+ * NOTE: 2nd argument is HASHDATA*, not HASHDATA** as elsewhere.
+ */
+int
+hash_free(
+ register struct hash *hp,
+ HASHDATA *data)
+{
+ ITEM * i = 0;
+ ITEM * prev = 0;
+ unsigned int keyval = hash_keyval(data->key);
+
+ i = hash_search( hp, keyval, data->key, &prev );
+ if (i)
+ {
+ /* mark it free so we skip it during enumeration */
+ i->data.key = 0;
+ /* unlink the record from the hash chain */
+ if (prev) prev->hdr.next = i->hdr.next;
+ else *hash_bucket(hp,keyval) = i->hdr.next;
+ /* link it into the freelist */
+ i->hdr.next = hp->items.free;
+ hp->items.free = i;
+ /* we have another item */
+ hp->items.more++;
+
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * hashitem() - find a record in the table, and optionally enter a new one
+ */
+
+int
+hashitem(
+ register struct hash *hp,
+ HASHDATA **data,
+ int enter )
+{
+ register ITEM *i;
+ char *b = (*data)->key;
+ unsigned int keyval = hash_keyval(b);
+
+ #ifdef HASH_DEBUG_PROFILE
+ profile_frame prof[1];
+ if ( DEBUG_PROFILE )
+ profile_enter( 0, prof );
+ #endif
+
+ if ( enter && !hp->items.more )
+ hashrehash( hp );
+
+ if ( !enter && !hp->items.nel )
+ {
+ #ifdef HASH_DEBUG_PROFILE
+ if ( DEBUG_PROFILE )
+ profile_exit( prof );
+ #endif
+ return 0;
+ }
+
+ i = hash_search( hp, keyval, (*data)->key, 0 );
+ if (i)
+ {
+ *data = &i->data;
+ #ifdef HASH_DEBUG_PROFILE
+ if ( DEBUG_PROFILE ) profile_exit( prof );
+ #endif
+ return !0;
+ }
+
+ if ( enter )
+ {
+ ITEM * * base = hash_bucket(hp,keyval);
+
+ /* try to grab one from the free list */
+ if ( hp->items.free )
+ {
+ i = hp->items.free;
+ hp->items.free = i->hdr.next;
+ assert( i->data.key == 0 );
+ }
+ else
+ {
+ i = (ITEM *)hp->items.next;
+ hp->items.next += hp->items.size;
+ }
+ hp->items.more--;
+ memcpy( (char *)&i->data, (char *)*data, hp->items.datalen );
+ i->hdr.keyval = keyval;
+ i->hdr.next = *base;
+ *base = i;
+ *data = &i->data;
+ #ifdef OPT_BOEHM_GC
+ if (sizeof(HASHDATA) == hp->items.datalen)
+ {
+ GC_REGISTER_FINALIZER(i->data.key,&hash_mem_finalizer,hp,0,0);
+ }
+ #endif
+ }
+
+ #ifdef HASH_DEBUG_PROFILE
+ if ( DEBUG_PROFILE )
+ profile_exit( prof );
+ #endif
+ return 0;
+}
+
+/*
+ * hashrehash() - resize and rebuild hp->tab, the hash table
+ */
+
+static void hashrehash( register struct hash *hp )
+{
+ int i = ++hp->items.list;
+ hp->items.more = i ? 2 * hp->items.nel : hp->inel;
+ hp->items.next = (char *)hash_mem_alloc( hp->items.datalen, hp->items.more * hp->items.size );
+ hp->items.free = 0;
+
+ hp->items.lists[i].nel = hp->items.more;
+ hp->items.lists[i].base = hp->items.next;
+ hp->items.nel += hp->items.more;
+
+ if ( hp->tab.base )
+ hash_mem_free( hp->items.datalen, (char *)hp->tab.base );
+
+ hp->tab.nel = hp->items.nel * hp->bloat;
+ hp->tab.base = (ITEM **)hash_mem_alloc( hp->items.datalen, hp->tab.nel * sizeof(ITEM **) );
+
+ memset( (char *)hp->tab.base, '\0', hp->tab.nel * sizeof( ITEM * ) );
+
+ for ( i = 0; i < hp->items.list; ++i )
+ {
+ int nel = hp->items.lists[i].nel;
+ char *next = hp->items.lists[i].base;
+
+ for ( ; nel--; next += hp->items.size )
+ {
+ register ITEM *i = (ITEM *)next;
+ ITEM **ip = hp->tab.base + i->hdr.keyval % hp->tab.nel;
+ /* code currently assumes rehashing only when there are no free items */
+ assert( i->data.key != 0 );
+
+ i->hdr.next = *ip;
+ *ip = i;
+ }
+ }
+}
+
+void hashenumerate( struct hash * hp, void (* f)( void *, void * ), void * data )
+{
+ int i;
+ for ( i = 0; i <= hp->items.list; ++i )
+ {
+ char * next = hp->items.lists[i].base;
+ int nel = hp->items.lists[i].nel;
+ if ( i == hp->items.list )
+ nel -= hp->items.more;
+
+ for ( ; nel--; next += hp->items.size )
+ {
+ ITEM * i = (ITEM *)next;
+ if ( i->data.key != 0 ) /* DO not enumerate freed items. */
+ f( &i->data, data );
+ }
+ }
+}
+
+/* --- */
+
+# define ALIGNED(x) ( ( x + sizeof( ITEM ) - 1 ) & ~( sizeof( ITEM ) - 1 ) )
+
+/*
+ * hashinit() - initialize a hash table, returning a handle
+ */
+
+struct hash *
+hashinit(
+ int datalen,
+ char *name )
+{
+ struct hash *hp = (struct hash *)hash_mem_alloc( datalen, sizeof( *hp ) );
+
+ hp->bloat = 3;
+ hp->tab.nel = 0;
+ hp->tab.base = (ITEM **)0;
+ hp->items.more = 0;
+ hp->items.free = 0;
+ hp->items.datalen = datalen;
+ hp->items.size = sizeof( struct hashhdr ) + ALIGNED( datalen );
+ hp->items.list = -1;
+ hp->items.nel = 0;
+ hp->inel = 11 /* 47 */;
+ hp->name = name;
+
+ return hp;
+}
+
+/*
+ * hashdone() - free a hash table, given its handle
+ */
+
+void
+hashdone( struct hash *hp )
+{
+ int i;
+
+ if ( !hp )
+ return;
+
+ if ( DEBUG_MEM || DEBUG_PROFILE )
+ hashstat( hp );
+
+ if ( hp->tab.base )
+ hash_mem_free( hp->items.datalen, (char *)hp->tab.base );
+ for ( i = 0; i <= hp->items.list; ++i )
+ hash_mem_free( hp->items.datalen, hp->items.lists[i].base );
+ hash_mem_free( hp->items.datalen, (char *)hp );
+}
+
+static void * hash_mem_alloc(size_t datalen, size_t size)
+{
+ if (sizeof(HASHDATA) == datalen)
+ {
+ return BJAM_MALLOC_RAW(size);
+ }
+ else
+ {
+ return BJAM_MALLOC(size);
+ }
+}
+
+static void hash_mem_free(size_t datalen, void * data)
+{
+ if (sizeof(HASHDATA) == datalen)
+ {
+ BJAM_FREE_RAW(data);
+ }
+ else
+ {
+ BJAM_FREE(data);
+ }
+}
+
+#ifdef OPT_BOEHM_GC
+static void hash_mem_finalizer(char * key, struct hash * hp)
+{
+ HASHDATA d;
+ d.key = key;
+ hash_free(hp,&d);
+}
+#endif
+
+
+/* ---- */
+
+static void hashstat( struct hash * hp )
+{
+ ITEM * * tab = hp->tab.base;
+ int nel = hp->tab.nel;
+ int count = 0;
+ int sets = 0;
+ int run = ( tab[ nel - 1 ] != (ITEM *)0 );
+ int i;
+ int here;
+
+ for ( i = nel; i > 0; --i )
+ {
+ if ( ( here = ( *tab++ != (ITEM *)0 ) ) )
+ count++;
+ if ( here && !run )
+ sets++;
+ run = here;
+ }
+
+ printf( "%s table: %d+%d+%d (%dK+%luK) items+table+hash, %f density\n",
+ hp->name,
+ count,
+ hp->items.nel,
+ hp->tab.nel,
+ hp->items.nel * hp->items.size / 1024,
+ (long unsigned)hp->tab.nel * sizeof( ITEM ** ) / 1024,
+ (float)count / (float)sets );
+}
diff --git a/jam-files/engine/hash.h b/jam-files/engine/hash.h
new file mode 100644
index 00000000..7195b414
--- /dev/null
+++ b/jam-files/engine/hash.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * hash.h - simple in-memory hashing routines
+ */
+
+#ifndef BOOST_JAM_HASH_H
+#define BOOST_JAM_HASH_H
+
+typedef struct hashdata HASHDATA;
+
+struct hash * hashinit ( int datalen, char * name );
+int hashitem ( struct hash * hp, HASHDATA * * data, int enter );
+void hashdone ( struct hash * hp );
+void hashenumerate( struct hash * hp, void (* f)( void *, void * ), void * data );
+int hash_free ( struct hash * hp, HASHDATA * data);
+
+#define hashenter( hp, data ) ( !hashitem( hp, data, !0 ) )
+#define hashcheck( hp, data ) hashitem( hp, data, 0 )
+
+#endif
diff --git a/jam-files/engine/hcache.c b/jam-files/engine/hcache.c
new file mode 100644
index 00000000..70bb798c
--- /dev/null
+++ b/jam-files/engine/hcache.c
@@ -0,0 +1,434 @@
+/*
+ * This file has been donated to Jam.
+ */
+
+# include "jam.h"
+# include "lists.h"
+# include "parse.h"
+# include "rules.h"
+# include "regexp.h"
+# include "headers.h"
+# include "newstr.h"
+# include "hash.h"
+# include "hcache.h"
+# include "variable.h"
+# include "search.h"
+
+#ifdef OPT_HEADER_CACHE_EXT
+
+/*
+ * Craig W. McPheeters, Alias|Wavefront.
+ *
+ * hcache.c hcache.h - handle cacheing of #includes in source files.
+ *
+ * Create a cache of files scanned for headers. When starting jam, look for the
+ * cache file and load it if present. When finished the binding phase, create a
+ * new header cache. The cache contains files, their timestamps and the header
+ * files found in their scan. During the binding phase of jam, look in the
+ * header cache first for the headers contained in a file. If the cache is
+ * present and valid, use its contents. This results in dramatic speedups with
+ * large projects (eg. 3min -> 1min startup for one project.)
+ *
+ * External routines:
+ * hcache_init() - read and parse the local .jamdeps file.
+ * hcache_done() - write a new .jamdeps file.
+ * hcache() - return list of headers on target. Use cache or do a scan.
+ *
+ * The dependency file format is an ASCII file with 1 line per target. Each line
+ * has the following fields:
+ * @boundname@ timestamp @file@ @file@ @file@ ... \n
+ */
+
+typedef struct hcachedata HCACHEDATA ;
+
+struct hcachedata
+{
+ char * boundname;
+ time_t time;
+ LIST * includes;
+ LIST * hdrscan; /* the HDRSCAN value for this target */
+ int age; /* if too old, we'll remove it from cache */
+ HCACHEDATA * next;
+};
+
+
+static struct hash * hcachehash = 0;
+static HCACHEDATA * hcachelist = 0;
+
+static int queries = 0;
+static int hits = 0;
+
+#define CACHE_FILE_VERSION "version 4"
+#define CACHE_RECORD_HEADER "header"
+#define CACHE_RECORD_END "end"
+
+
+/*
+ * Return the name of the header cache file. May return NULL.
+ *
+ * The user sets this by setting the HCACHEFILE variable in a Jamfile. We cache
+ * the result so the user can not change the cache file during header scanning.
+ */
+
+static char * cache_name( void )
+{
+ static char * name = 0;
+ if ( !name )
+ {
+ LIST * hcachevar = var_get( "HCACHEFILE" );
+
+ if ( hcachevar )
+ {
+ TARGET * t = bindtarget( hcachevar->string );
+
+ pushsettings( t->settings );
+ /* Do not expect the cache file to be generated, so pass 0 as the
+ * third argument to search. Expect the location to be specified via
+ * LOCATE, so pass 0 as the fourth arugment.
+ */
+ t->boundname = search( t->name, &t->time, 0, 0 );
+ popsettings( t->settings );
+
+ if ( hcachevar )
+ name = copystr( t->boundname );
+ }
+ }
+ return name;
+}
+
+
+/*
+ * Return the maximum age a cache entry can have before it is purged ftom the
+ * cache.
+ */
+
+static int cache_maxage( void )
+{
+ int age = 100;
+ LIST * var = var_get( "HCACHEMAXAGE" );
+ if ( var )
+ {
+ age = atoi( var->string );
+ if ( age < 0 )
+ age = 0;
+ }
+ return age;
+}
+
+
+/*
+ * Read a netstring. The caveat is that the string can not contain ASCII 0. The
+ * returned value is as returned by newstr(), so it need not be freed.
+ */
+
+char * read_netstring( FILE * f )
+{
+ unsigned long len;
+ static char * buf = NULL;
+ static unsigned long buf_len = 0;
+
+ if ( fscanf( f, " %9lu", &len ) != 1 )
+ return NULL;
+ if ( fgetc( f ) != (int)'\t' )
+ return NULL;
+
+ if ( len > 1024 * 64 )
+ return NULL; /* sanity check */
+
+ if ( len > buf_len )
+ {
+ unsigned long new_len = buf_len * 2;
+ if ( new_len < len )
+ new_len = len;
+ buf = (char *)BJAM_REALLOC( buf, new_len + 1 );
+ if ( buf )
+ buf_len = new_len;
+ }
+
+ if ( !buf )
+ return NULL;
+
+ if ( fread( buf, 1, len, f ) != len )
+ return NULL;
+ if ( fgetc( f ) != (int)'\n' )
+ return NULL;
+
+ buf[ len ] = 0;
+ return newstr( buf );
+}
+
+
+/*
+ * Write a netstring.
+ */
+
+void write_netstring( FILE * f, char const * s )
+{
+ if ( !s )
+ s = "";
+ fprintf( f, "%lu\t%s\n", (long unsigned)strlen( s ), s );
+}
+
+
+void hcache_init()
+{
+ HCACHEDATA cachedata;
+ HCACHEDATA * c;
+ FILE * f;
+ char * version;
+ int header_count = 0;
+ char * hcachename;
+
+ hcachehash = hashinit( sizeof( HCACHEDATA ), "hcache" );
+
+ if ( !( hcachename = cache_name() ) )
+ return;
+
+ if ( !( f = fopen( hcachename, "rb" ) ) )
+ return;
+
+ version = read_netstring( f );
+ if ( !version || strcmp( version, CACHE_FILE_VERSION ) )
+ {
+ fclose( f );
+ return;
+ }
+
+ while ( 1 )
+ {
+ char * record_type;
+ char * time_str;
+ char * age_str;
+ char * includes_count_str;
+ char * hdrscan_count_str;
+ int i;
+ int count;
+ LIST * l;
+
+ record_type = read_netstring( f );
+ if ( !record_type )
+ {
+ fprintf( stderr, "invalid %s\n", hcachename );
+ goto bail;
+ }
+ if ( !strcmp( record_type, CACHE_RECORD_END ) )
+ break;
+ if ( strcmp( record_type, CACHE_RECORD_HEADER ) )
+ {
+ fprintf( stderr, "invalid %s with record separator <%s>\n",
+ hcachename, record_type ? record_type : "<null>" );
+ goto bail;
+ }
+
+ c = &cachedata;
+
+ c->boundname = read_netstring( f );
+ time_str = read_netstring( f );
+ age_str = read_netstring( f );
+ includes_count_str = read_netstring( f );
+
+ if ( !c->boundname || !time_str || !age_str || !includes_count_str )
+ {
+ fprintf( stderr, "invalid %s\n", hcachename );
+ goto bail;
+ }
+
+ c->time = atoi( time_str );
+ c->age = atoi( age_str ) + 1;
+
+ count = atoi( includes_count_str );
+ for ( l = 0, i = 0; i < count; ++i )
+ {
+ char * s = read_netstring( f );
+ if ( !s )
+ {
+ fprintf( stderr, "invalid %s\n", hcachename );
+ goto bail;
+ }
+ l = list_new( l, s );
+ }
+ c->includes = l;
+
+ hdrscan_count_str = read_netstring( f );
+ if ( !includes_count_str )
+ {
+ list_free( c->includes );
+ fprintf( stderr, "invalid %s\n", hcachename );
+ goto bail;
+ }
+
+ count = atoi( hdrscan_count_str );
+ for ( l = 0, i = 0; i < count; ++i )
+ {
+ char * s = read_netstring( f );
+ if ( !s )
+ {
+ fprintf( stderr, "invalid %s\n", hcachename );
+ goto bail;
+ }
+ l = list_new( l, s );
+ }
+ c->hdrscan = l;
+
+ if ( !hashenter( hcachehash, (HASHDATA * *)&c ) )
+ {
+ fprintf( stderr, "can't insert header cache item, bailing on %s\n",
+ hcachename );
+ goto bail;
+ }
+
+ c->next = hcachelist;
+ hcachelist = c;
+
+ ++header_count;
+ }
+
+ if ( DEBUG_HEADER )
+ printf( "hcache read from file %s\n", hcachename );
+
+ bail:
+ fclose( f );
+}
+
+
+void hcache_done()
+{
+ FILE * f;
+ HCACHEDATA * c;
+ int header_count = 0;
+ char * hcachename;
+ int maxage;
+
+ if ( !hcachehash )
+ return;
+
+ if ( !( hcachename = cache_name() ) )
+ return;
+
+ if ( !( f = fopen( hcachename, "wb" ) ) )
+ return;
+
+ maxage = cache_maxage();
+
+ /* Print out the version. */
+ write_netstring( f, CACHE_FILE_VERSION );
+
+ c = hcachelist;
+ for ( c = hcachelist; c; c = c->next )
+ {
+ LIST * l;
+ char time_str[ 30 ];
+ char age_str[ 30 ];
+ char includes_count_str[ 30 ];
+ char hdrscan_count_str[ 30 ];
+
+ if ( maxage == 0 )
+ c->age = 0;
+ else if ( c->age > maxage )
+ continue;
+
+ sprintf( includes_count_str, "%lu", (long unsigned) list_length( c->includes ) );
+ sprintf( hdrscan_count_str, "%lu", (long unsigned) list_length( c->hdrscan ) );
+ sprintf( time_str, "%lu", (long unsigned) c->time );
+ sprintf( age_str, "%lu", (long unsigned) c->age );
+
+ write_netstring( f, CACHE_RECORD_HEADER );
+ write_netstring( f, c->boundname );
+ write_netstring( f, time_str );
+ write_netstring( f, age_str );
+ write_netstring( f, includes_count_str );
+ for ( l = c->includes; l; l = list_next( l ) )
+ write_netstring( f, l->string );
+ write_netstring( f, hdrscan_count_str );
+ for ( l = c->hdrscan; l; l = list_next( l ) )
+ write_netstring( f, l->string );
+ fputs( "\n", f );
+ ++header_count;
+ }
+ write_netstring( f, CACHE_RECORD_END );
+
+ if ( DEBUG_HEADER )
+ printf( "hcache written to %s. %d dependencies, %.0f%% hit rate\n",
+ hcachename, header_count, queries ? 100.0 * hits / queries : 0 );
+
+ fclose ( f );
+}
+
+
+LIST * hcache( TARGET * t, int rec, regexp * re[], LIST * hdrscan )
+{
+ HCACHEDATA cachedata;
+ HCACHEDATA * c = &cachedata;
+
+ LIST * l = 0;
+
+ ++queries;
+
+ c->boundname = t->boundname;
+
+ if (hashcheck (hcachehash, (HASHDATA **) &c))
+ {
+ if (c->time == t->time)
+ {
+ LIST *l1 = hdrscan, *l2 = c->hdrscan;
+ while (l1 && l2) {
+ if (l1->string != l2->string) {
+ l1 = NULL;
+ } else {
+ l1 = list_next(l1);
+ l2 = list_next(l2);
+ }
+ }
+ if (l1 || l2) {
+ if (DEBUG_HEADER)
+ printf("HDRSCAN out of date in cache for %s\n",
+ t->boundname);
+
+ printf("HDRSCAN out of date for %s\n", t->boundname);
+ printf(" real : ");
+ list_print(hdrscan);
+ printf("\n cached: ");
+ list_print(c->hdrscan);
+ printf("\n");
+
+ list_free(c->includes);
+ list_free(c->hdrscan);
+ c->includes = 0;
+ c->hdrscan = 0;
+ } else {
+ if (DEBUG_HEADER)
+ printf ("using header cache for %s\n", t->boundname);
+ c->age = 0;
+ ++hits;
+ l = list_copy (0, c->includes);
+ return l;
+ }
+ } else {
+ if (DEBUG_HEADER)
+ printf ("header cache out of date for %s\n", t->boundname);
+ list_free (c->includes);
+ list_free(c->hdrscan);
+ c->includes = 0;
+ c->hdrscan = 0;
+ }
+ } else {
+ if (hashenter (hcachehash, (HASHDATA **)&c)) {
+ c->boundname = newstr (c->boundname);
+ c->next = hcachelist;
+ hcachelist = c;
+ }
+ }
+
+ /* 'c' points at the cache entry. Its out of date. */
+
+ l = headers1 (0, t->boundname, rec, re);
+
+ c->time = t->time;
+ c->age = 0;
+ c->includes = list_copy (0, l);
+ c->hdrscan = list_copy(0, hdrscan);
+
+ return l;
+}
+
+#endif
diff --git a/jam-files/engine/hcache.h b/jam-files/engine/hcache.h
new file mode 100644
index 00000000..c316e3bc
--- /dev/null
+++ b/jam-files/engine/hcache.h
@@ -0,0 +1,18 @@
+/*
+ * This file is not part of Jam
+ */
+
+/*
+ * hcache.h - handle #includes in source files
+ */
+#ifndef HCACHE_H
+# define HCACHE_H
+
+# include "regexp.h"
+# include "lists.h"
+
+void hcache_init(void);
+void hcache_done(void);
+LIST *hcache(TARGET *t, int rec, regexp *re[], LIST *hdrscan);
+
+#endif
diff --git a/jam-files/engine/hdrmacro.c b/jam-files/engine/hdrmacro.c
new file mode 100644
index 00000000..43031d48
--- /dev/null
+++ b/jam-files/engine/hdrmacro.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright 1993, 2000 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/* This file is ALSO:
+ * Copyright 2001-2004 David Abrahams.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+# include "jam.h"
+# include "lists.h"
+# include "parse.h"
+# include "compile.h"
+# include "rules.h"
+# include "variable.h"
+# include "regexp.h"
+# include "hdrmacro.h"
+# include "hash.h"
+# include "newstr.h"
+# include "strings.h"
+
+/*
+ * hdrmacro.c - handle header files that define macros used in
+ * #include statements.
+ *
+ * we look for lines like "#define MACRO <....>" or '#define MACRO " "'
+ * in the target file. When found, we
+ *
+ * we then phony up a rule invocation like:
+ *
+ * $(HDRRULE) <target> : <resolved included files> ;
+ *
+ * External routines:
+ * headers1() - scan a target for "#include MACRO" lines and try
+ * to resolve them when needed
+ *
+ * Internal routines:
+ * headers1() - using regexp, scan a file and build include LIST
+ *
+ * 04/13/94 (seiwald) - added shorthand L0 for null list pointer
+ * 09/10/00 (seiwald) - replaced call to compile_rule with evaluate_rule,
+ * so that headers() doesn't have to mock up a parse structure
+ * just to invoke a rule.
+ */
+
+/* this type is used to store a dictionary of file header macros */
+typedef struct header_macro
+{
+ char * symbol;
+ char * filename; /* we could maybe use a LIST here ?? */
+} HEADER_MACRO;
+
+static struct hash * header_macros_hash = 0;
+
+
+/*
+ * headers() - scan a target for include files and call HDRRULE
+ */
+
+# define MAXINC 10
+
+void
+macro_headers( TARGET *t )
+{
+ static regexp *re = 0;
+ FILE *f;
+ char buf[ 1024 ];
+
+ if ( DEBUG_HEADER )
+ printf( "macro header scan for %s\n", t->name );
+
+ /* this regexp is used to detect lines of the form */
+ /* "#define MACRO <....>" or "#define MACRO "....." */
+ /* in the header macro files.. */
+ if ( re == 0 )
+ {
+ re = regex_compile(
+ "^[ ]*#[ ]*define[ ]*([A-Za-z][A-Za-z0-9_]*)[ ]*"
+ "[<\"]([^\">]*)[\">].*$" );
+ }
+
+ if ( !( f = fopen( t->boundname, "r" ) ) )
+ return;
+
+ while ( fgets( buf, sizeof( buf ), f ) )
+ {
+ HEADER_MACRO var;
+ HEADER_MACRO *v = &var;
+
+ if ( regexec( re, buf ) && re->startp[1] )
+ {
+ /* we detected a line that looks like "#define MACRO filename */
+ re->endp[1][0] = '\0';
+ re->endp[2][0] = '\0';
+
+ if ( DEBUG_HEADER )
+ printf( "macro '%s' used to define filename '%s' in '%s'\n",
+ re->startp[1], re->startp[2], t->boundname );
+
+ /* add macro definition to hash table */
+ if ( !header_macros_hash )
+ header_macros_hash = hashinit( sizeof( HEADER_MACRO ), "hdrmacros" );
+
+ v->symbol = re->startp[1];
+ v->filename = 0;
+ if ( hashenter( header_macros_hash, (HASHDATA **)&v ) )
+ {
+ v->symbol = newstr( re->startp[1] ); /* never freed */
+ v->filename = newstr( re->startp[2] ); /* never freed */
+ }
+ /* XXXX: FOR NOW, WE IGNORE MULTIPLE MACRO DEFINITIONS !! */
+ /* WE MIGHT AS WELL USE A LIST TO STORE THEM.. */
+ }
+ }
+
+ fclose( f );
+}
+
+
+char * macro_header_get( const char * macro_name )
+{
+ HEADER_MACRO var;
+ HEADER_MACRO * v = &var;
+
+ v->symbol = (char* )macro_name;
+
+ if ( header_macros_hash && hashcheck( header_macros_hash, (HASHDATA **)&v ) )
+ {
+ if ( DEBUG_HEADER )
+ printf( "### macro '%s' evaluated to '%s'\n", macro_name, v->filename );
+ return v->filename;
+ }
+ return 0;
+}
diff --git a/jam-files/engine/hdrmacro.h b/jam-files/engine/hdrmacro.h
new file mode 100644
index 00000000..08cc1116
--- /dev/null
+++ b/jam-files/engine/hdrmacro.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * hdrmacro.h - parses header files for #define MACRO <filename> or
+ * #define MACRO "filename" definitions
+ */
+
+void macro_headers( TARGET *t );
+
+char* macro_header_get( const char* macro_name );
diff --git a/jam-files/engine/headers.c b/jam-files/engine/headers.c
new file mode 100644
index 00000000..b9d8f637
--- /dev/null
+++ b/jam-files/engine/headers.c
@@ -0,0 +1,203 @@
+/*
+ * Copyright 1993, 2000 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+/* This file is ALSO:
+ * Copyright 2001-2004 David Abrahams.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+# include "jam.h"
+# include "lists.h"
+# include "parse.h"
+# include "compile.h"
+# include "rules.h"
+# include "variable.h"
+# include "regexp.h"
+# include "headers.h"
+# include "hdrmacro.h"
+# include "newstr.h"
+
+#ifdef OPT_HEADER_CACHE_EXT
+# include "hcache.h"
+#endif
+
+/*
+ * headers.c - handle #includes in source files
+ *
+ * Using regular expressions provided as the variable $(HDRSCAN),
+ * headers() searches a file for #include files and phonies up a
+ * rule invocation:
+ *
+ * $(HDRRULE) <target> : <include files> ;
+ *
+ * External routines:
+ * headers() - scan a target for include files and call HDRRULE
+ *
+ * Internal routines:
+ * headers1() - using regexp, scan a file and build include LIST
+ *
+ * 04/13/94 (seiwald) - added shorthand L0 for null list pointer
+ * 09/10/00 (seiwald) - replaced call to compile_rule with evaluate_rule,
+ * so that headers() doesn't have to mock up a parse structure
+ * just to invoke a rule.
+ */
+
+#ifndef OPT_HEADER_CACHE_EXT
+static LIST *headers1( LIST *l, char *file, int rec, regexp *re[]);
+#endif
+
+/*
+ * headers() - scan a target for include files and call HDRRULE
+ */
+
+# define MAXINC 10
+
+void
+headers( TARGET *t )
+{
+ LIST * hdrscan;
+ LIST * hdrrule;
+ #ifndef OPT_HEADER_CACHE_EXT
+ LIST * headlist = 0;
+ #endif
+ regexp * re[ MAXINC ];
+ int rec = 0;
+
+ if ( !( hdrscan = var_get( "HDRSCAN" ) ) ||
+ !( hdrrule = var_get( "HDRRULE" ) ) )
+ return;
+
+ if ( DEBUG_HEADER )
+ printf( "header scan %s\n", t->name );
+
+ /* Compile all regular expressions in HDRSCAN */
+ while ( ( rec < MAXINC ) && hdrscan )
+ {
+ re[ rec++ ] = regex_compile( hdrscan->string );
+ hdrscan = list_next( hdrscan );
+ }
+
+ /* Doctor up call to HDRRULE rule */
+ /* Call headers1() to get LIST of included files. */
+ {
+ FRAME frame[1];
+ frame_init( frame );
+ lol_add( frame->args, list_new( L0, t->name ) );
+#ifdef OPT_HEADER_CACHE_EXT
+ lol_add( frame->args, hcache( t, rec, re, hdrscan ) );
+#else
+ lol_add( frame->args, headers1( headlist, t->boundname, rec, re ) );
+#endif
+
+ if ( lol_get( frame->args, 1 ) )
+ {
+ /* The third argument to HDRRULE is the bound name of
+ * $(<) */
+ lol_add( frame->args, list_new( L0, t->boundname ) );
+
+ list_free( evaluate_rule( hdrrule->string, frame ) );
+ }
+
+ /* Clean up. */
+ frame_free( frame );
+ }
+}
+
+
+/*
+ * headers1() - using regexp, scan a file and build include LIST.
+ */
+
+#ifdef OPT_HEADER_CACHE_EXT
+LIST *
+#else
+static LIST *
+#endif
+headers1(
+ LIST * l,
+ char * file,
+ int rec,
+ regexp * re[] )
+{
+ FILE * f;
+ char buf[ 1024 ];
+ int i;
+ static regexp * re_macros = 0;
+
+#ifdef OPT_IMPROVED_PATIENCE_EXT
+ static int count = 0;
+ ++count;
+ if ( ((count == 100) || !( count % 1000 )) && DEBUG_MAKE )
+ printf("...patience...\n");
+#endif
+
+ /* the following regexp is used to detect cases where a */
+ /* file is included through a line line "#include MACRO" */
+ if ( re_macros == 0 )
+ re_macros = regex_compile(
+ "^[ ]*#[ ]*include[ ]*([A-Za-z][A-Za-z0-9_]*).*$" );
+
+ if ( !( f = fopen( file, "r" ) ) )
+ return l;
+
+ while ( fgets( buf, sizeof( buf ), f ) )
+ {
+ int size = strlen( buf );
+ /* Remove trailing \r and \n, if any. */
+ while ( ( size > 0 ) &&
+ ( buf[ size - 1 ] == '\n' ) &&
+ ( buf[ size - 1 ] == '\r' ) )
+ {
+ buf[ size - 1 ] = '\0';
+ --size;
+ }
+
+ for ( i = 0; i < rec; ++i )
+ if ( regexec( re[i], buf ) && re[i]->startp[1] )
+ {
+ re[i]->endp[1][0] = '\0';
+
+ if ( DEBUG_HEADER )
+ printf( "header found: %s\n", re[i]->startp[1] );
+
+ l = list_new( l, newstr( re[i]->startp[1] ) );
+ }
+
+ /* special treatment for #include MACRO */
+ if ( regexec( re_macros, buf ) && re_macros->startp[1] )
+ {
+ char* header_filename;
+
+ re_macros->endp[1][0] = '\0';
+
+ if ( DEBUG_HEADER )
+ printf( "macro header found: %s", re_macros->startp[1] );
+
+ header_filename = macro_header_get( re_macros->startp[1] );
+ if ( header_filename )
+ {
+ if ( DEBUG_HEADER )
+ printf( " resolved to '%s'\n", header_filename );
+ l = list_new( l, newstr( header_filename ) );
+ }
+ else
+ {
+ if ( DEBUG_HEADER )
+ printf( " ignored !!\n" );
+ }
+ }
+ }
+
+ fclose( f );
+
+ return l;
+}
+
+
+void regerror( char * s )
+{
+ printf( "re error %s\n", s );
+}
diff --git a/jam-files/engine/headers.h b/jam-files/engine/headers.h
new file mode 100644
index 00000000..624475fe
--- /dev/null
+++ b/jam-files/engine/headers.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * headers.h - handle #includes in source files
+ */
+
+void headers( TARGET *t );
+
+#ifdef OPT_HEADER_CACHE_EXT
+struct regexp;
+LIST *headers1( LIST *l, char *file, int rec, struct regexp *re[] );
+#endif
diff --git a/jam-files/engine/jam.c b/jam-files/engine/jam.c
new file mode 100644
index 00000000..e11d082b
--- /dev/null
+++ b/jam-files/engine/jam.c
@@ -0,0 +1,632 @@
+/*
+ * /+\
+ * +\ Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
+ * \+/
+ *
+ * This file is part of jam.
+ *
+ * License is hereby granted to use this software and distribute it
+ * freely, as long as this copyright notice is retained and modifications
+ * are clearly marked.
+ *
+ * ALL WARRANTIES ARE HEREBY DISCLAIMED.
+ */
+
+/* This file is ALSO:
+ * Copyright 2001-2004 David Abrahams.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+/*
+ * jam.c - make redux
+ *
+ * See Jam.html for usage information.
+ *
+ * These comments document the code.
+ *
+ * The top half of the code is structured such:
+ *
+ * jam
+ * / | \
+ * +---+ | \
+ * / | \
+ * jamgram option \
+ * / | \ \
+ * / | \ \
+ * / | \ |
+ * scan | compile make
+ * | | / | \ / | \
+ * | | / | \ / | \
+ * | | / | \ / | \
+ * jambase parse | rules search make1
+ * | | | \
+ * | | | \
+ * | | | \
+ * builtins timestamp command execute
+ * |
+ * |
+ * |
+ * filesys
+ *
+ *
+ * The support routines are called by all of the above, but themselves
+ * are layered thus:
+ *
+ * variable|expand
+ * / | | |
+ * / | | |
+ * / | | |
+ * lists | | pathsys
+ * \ | |
+ * \ | |
+ * \ | |
+ * newstr |
+ * \ |
+ * \ |
+ * \ |
+ * hash
+ *
+ * Roughly, the modules are:
+ *
+ * builtins.c - jam's built-in rules
+ * command.c - maintain lists of commands
+ * compile.c - compile parsed jam statements
+ * execunix.c - execute a shell script on UNIX
+ * execvms.c - execute a shell script, ala VMS
+ * expand.c - expand a buffer, given variable values
+ * file*.c - scan directories and archives on *
+ * hash.c - simple in-memory hashing routines
+ * hdrmacro.c - handle header file parsing for filename macro definitions
+ * headers.c - handle #includes in source files
+ * jambase.c - compilable copy of Jambase
+ * jamgram.y - jam grammar
+ * lists.c - maintain lists of strings
+ * make.c - bring a target up to date, once rules are in place
+ * make1.c - execute command to bring targets up to date
+ * newstr.c - string manipulation routines
+ * option.c - command line option processing
+ * parse.c - make and destroy parse trees as driven by the parser
+ * path*.c - manipulate file names on *
+ * hash.c - simple in-memory hashing routines
+ * regexp.c - Henry Spencer's regexp
+ * rules.c - access to RULEs, TARGETs, and ACTIONs
+ * scan.c - the jam yacc scanner
+ * search.c - find a target along $(SEARCH) or $(LOCATE)
+ * timestamp.c - get the timestamp of a file or archive member
+ * variable.c - handle jam multi-element variables
+ *
+ * 05/04/94 (seiwald) - async multiprocess (-j) support
+ * 02/08/95 (seiwald) - -n implies -d2.
+ * 02/22/95 (seiwald) - -v for version info.
+ * 09/11/00 (seiwald) - PATCHLEVEL folded into VERSION.
+ * 01/10/01 (seiwald) - pathsys.h split from filesys.h
+ */
+
+
+#include "jam.h"
+#include "option.h"
+#include "patchlevel.h"
+
+/* These get various function declarations. */
+#include "lists.h"
+#include "parse.h"
+#include "variable.h"
+#include "compile.h"
+#include "builtins.h"
+#include "rules.h"
+#include "newstr.h"
+#include "scan.h"
+#include "timestamp.h"
+#include "make.h"
+#include "strings.h"
+#include "expand.h"
+#include "filesys.h"
+#include "output.h"
+
+/* Macintosh is "special" */
+#ifdef OS_MAC
+ #include <QuickDraw.h>
+#endif
+
+/* And UNIX for this. */
+#ifdef unix
+ #include <sys/utsname.h>
+ #include <signal.h>
+#endif
+
+struct globs globs =
+{
+ 0, /* noexec */
+ 1, /* jobs */
+ 0, /* quitquick */
+ 0, /* newestfirst */
+ 0, /* pipes action stdout and stderr merged to action output */
+#ifdef OS_MAC
+ { 0, 0 }, /* debug - suppress tracing output */
+#else
+ { 0, 1 }, /* debug ... */
+#endif
+ 0, /* output commands, not run them */
+ 0 /* action timeout */
+};
+
+/* Symbols to be defined as true for use in Jambase. */
+static char * othersyms[] = { OSMAJOR, OSMINOR, OSPLAT, JAMVERSYM, 0 };
+
+
+/* Known for sure:
+ * mac needs arg_enviro
+ * OS2 needs extern environ
+ */
+
+#ifdef OS_MAC
+ #define use_environ arg_environ
+ #ifdef MPW
+ QDGlobals qd;
+ #endif
+#endif
+
+/* on Win32-LCC */
+#if defined( OS_NT ) && defined( __LCC__ )
+ #define use_environ _environ
+#endif
+
+# if defined( __MWERKS__)
+ #define use_environ _environ
+ extern char * * _environ;
+#endif
+
+#ifndef use_environ
+ #define use_environ environ
+ #if !defined( __WATCOM__ ) && !defined( OS_OS2 ) && !defined( OS_NT )
+ extern char **environ;
+ #endif
+#endif
+
+#if YYDEBUG != 0
+ extern int yydebug;
+#endif
+
+#ifndef NDEBUG
+static void run_unit_tests()
+{
+#if defined( USE_EXECNT )
+ extern void execnt_unit_test();
+ execnt_unit_test();
+#endif
+ string_unit_test();
+ var_expand_unit_test();
+}
+#endif
+
+int anyhow = 0;
+
+#ifdef HAVE_PYTHON
+ extern PyObject * bjam_call ( PyObject * self, PyObject * args );
+ extern PyObject * bjam_import_rule ( PyObject * self, PyObject * args );
+ extern PyObject * bjam_define_action( PyObject * self, PyObject * args );
+ extern PyObject * bjam_variable ( PyObject * self, PyObject * args );
+ extern PyObject * bjam_backtrace ( PyObject * self, PyObject * args );
+ extern PyObject * bjam_caller ( PyObject * self, PyObject * args );
+#endif
+
+char *saved_argv0;
+
+int main( int argc, char * * argv, char * * arg_environ )
+{
+ int n;
+ char * s;
+ struct bjam_option optv[N_OPTS];
+ char const * all = "all";
+ int status;
+ int arg_c = argc;
+ char * * arg_v = argv;
+ char const * progname = argv[0];
+
+ saved_argv0 = argv[0];
+
+ BJAM_MEM_INIT();
+
+# ifdef OS_MAC
+ InitGraf(&qd.thePort);
+# endif
+
+ --argc;
+ ++argv;
+
+ if ( getoptions( argc, argv, "-:l:d:j:p:f:gs:t:ano:qv", optv ) < 0 )
+ {
+ printf( "\nusage: %s [ options ] targets...\n\n", progname );
+
+ printf( "-a Build all targets, even if they are current.\n" );
+ printf( "-dx Set the debug level to x (0-9).\n" );
+ printf( "-fx Read x instead of Jambase.\n" );
+ /* printf( "-g Build from newest sources first.\n" ); */
+ printf( "-jx Run up to x shell commands concurrently.\n" );
+ printf( "-lx Limit actions to x number of seconds after which they are stopped.\n" );
+ printf( "-n Don't actually execute the updating actions.\n" );
+ printf( "-ox Write the updating actions to file x.\n" );
+ printf( "-px x=0, pipes action stdout and stderr merged into action output.\n" );
+ printf( "-q Quit quickly as soon as a target fails.\n" );
+ printf( "-sx=y Set variable x=y, overriding environment.\n" );
+ printf( "-tx Rebuild x, even if it is up-to-date.\n" );
+ printf( "-v Print the version of jam and exit.\n" );
+ printf( "--x Option is ignored.\n\n" );
+
+ exit( EXITBAD );
+ }
+
+ /* Version info. */
+ if ( ( s = getoptval( optv, 'v', 0 ) ) )
+ {
+ printf( "Boost.Jam " );
+ printf( "Version %s. %s.\n", VERSION, OSMINOR );
+ printf( " Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. \n" );
+ printf( " Copyright 2001 David Turner.\n" );
+ printf( " Copyright 2001-2004 David Abrahams.\n" );
+ printf( " Copyright 2002-2008 Rene Rivera.\n" );
+ printf( " Copyright 2003-2008 Vladimir Prus.\n" );
+
+ return EXITOK;
+ }
+
+ /* Pick up interesting options. */
+ if ( ( s = getoptval( optv, 'n', 0 ) ) )
+ globs.noexec++, globs.debug[2] = 1;
+
+ if ( ( s = getoptval( optv, 'p', 0 ) ) )
+ {
+ /* Undocumented -p3 (acts like both -p1 -p2) means separate pipe action
+ * stdout and stderr.
+ */
+ globs.pipe_action = atoi( s );
+ if ( ( 3 < globs.pipe_action ) || ( globs.pipe_action < 0 ) )
+ {
+ printf(
+ "Invalid pipe descriptor '%d', valid values are -p[0..3].\n",
+ globs.pipe_action );
+ exit( EXITBAD );
+ }
+ }
+
+ if ( ( s = getoptval( optv, 'q', 0 ) ) )
+ globs.quitquick = 1;
+
+ if ( ( s = getoptval( optv, 'a', 0 ) ) )
+ anyhow++;
+
+ if ( ( s = getoptval( optv, 'j', 0 ) ) )
+ {
+ globs.jobs = atoi( s );
+ if (globs.jobs == 0)
+ {
+ printf("Invalid value for the '-j' option.\n");
+ exit(EXITBAD);
+ }
+ }
+
+ if ( ( s = getoptval( optv, 'g', 0 ) ) )
+ globs.newestfirst = 1;
+
+ if ( ( s = getoptval( optv, 'l', 0 ) ) )
+ globs.timeout = atoi( s );
+
+ /* Turn on/off debugging */
+ for ( n = 0; ( s = getoptval( optv, 'd', n ) ); ++n )
+ {
+ int i;
+
+ /* First -d, turn off defaults. */
+ if ( !n )
+ for ( i = 0; i < DEBUG_MAX; ++i )
+ globs.debug[i] = 0;
+
+ i = atoi( s );
+
+ if ( ( i < 0 ) || ( i >= DEBUG_MAX ) )
+ {
+ printf( "Invalid debug level '%s'.\n", s );
+ continue;
+ }
+
+ /* n turns on levels 1-n. */
+ /* +n turns on level n. */
+ if ( *s == '+' )
+ globs.debug[i] = 1;
+ else while ( i )
+ globs.debug[i--] = 1;
+ }
+
+ {
+ PROFILE_ENTER( MAIN );
+
+#ifdef HAVE_PYTHON
+ {
+ PROFILE_ENTER( MAIN_PYTHON );
+ Py_Initialize();
+ {
+ static PyMethodDef BjamMethods[] = {
+ {"call", bjam_call, METH_VARARGS,
+ "Call the specified bjam rule."},
+ {"import_rule", bjam_import_rule, METH_VARARGS,
+ "Imports Python callable to bjam."},
+ {"define_action", bjam_define_action, METH_VARARGS,
+ "Defines a command line action."},
+ {"variable", bjam_variable, METH_VARARGS,
+ "Obtains a variable from bjam's global module."},
+ {"backtrace", bjam_backtrace, METH_VARARGS,
+ "Returns bjam backtrace from the last call into Python."},
+ {"caller", bjam_caller, METH_VARARGS,
+ "Returns the module from which the last call into Python is made."},
+ {NULL, NULL, 0, NULL}
+ };
+
+ Py_InitModule( "bjam", BjamMethods );
+ }
+ PROFILE_EXIT( MAIN_PYTHON );
+ }
+#endif
+
+#ifndef NDEBUG
+ run_unit_tests();
+#endif
+#if YYDEBUG != 0
+ if ( DEBUG_PARSE )
+ yydebug = 1;
+#endif
+
+ /* Set JAMDATE. */
+ var_set( "JAMDATE", list_new( L0, outf_time(time(0)) ), VAR_SET );
+
+ /* Set JAM_VERSION. */
+ var_set( "JAM_VERSION",
+ list_new( list_new( list_new( L0,
+ newstr( VERSION_MAJOR_SYM ) ),
+ newstr( VERSION_MINOR_SYM ) ),
+ newstr( VERSION_PATCH_SYM ) ),
+ VAR_SET );
+
+ /* Set JAMUNAME. */
+#ifdef unix
+ {
+ struct utsname u;
+
+ if ( uname( &u ) >= 0 )
+ {
+ var_set( "JAMUNAME",
+ list_new(
+ list_new(
+ list_new(
+ list_new(
+ list_new( L0,
+ newstr( u.sysname ) ),
+ newstr( u.nodename ) ),
+ newstr( u.release ) ),
+ newstr( u.version ) ),
+ newstr( u.machine ) ), VAR_SET );
+ }
+ }
+#endif /* unix */
+
+ /* Load up environment variables. */
+
+ /* First into the global module, with splitting, for backward
+ * compatibility.
+ */
+ var_defines( use_environ, 1 );
+
+ /* Then into .ENVIRON, without splitting. */
+ enter_module( bindmodule(".ENVIRON") );
+ var_defines( use_environ, 0 );
+ exit_module( bindmodule(".ENVIRON") );
+
+ /*
+ * Jam defined variables OS & OSPLAT. We load them after environment, so
+ * that setting OS in environment does not change Jam's notion of the
+ * current platform.
+ */
+ var_defines( othersyms, 1 );
+
+ /* Load up variables set on command line. */
+ for ( n = 0; ( s = getoptval( optv, 's', n ) ); ++n )
+ {
+ char *symv[2];
+ symv[ 0 ] = s;
+ symv[ 1 ] = 0;
+ var_defines( symv, 1 );
+ enter_module( bindmodule(".ENVIRON") );
+ var_defines( symv, 0 );
+ exit_module( bindmodule(".ENVIRON") );
+ }
+
+ /* Set the ARGV to reflect the complete list of arguments of invocation.
+ */
+ for ( n = 0; n < arg_c; ++n )
+ var_set( "ARGV", list_new( L0, newstr( arg_v[n] ) ), VAR_APPEND );
+
+ /* Initialize built-in rules. */
+ load_builtins();
+
+ /* Add the targets in the command line to the update list. */
+ for ( n = 1; n < arg_c; ++n )
+ {
+ if ( arg_v[ n ][ 0 ] == '-' )
+ {
+ char * f = "-:l:d:j:f:gs:t:ano:qv";
+ for ( ; *f; ++f ) if ( *f == arg_v[ n ][ 1 ] ) break;
+ if ( ( f[ 1 ] == ':' ) && ( arg_v[ n ][ 2 ] == '\0' ) ) ++n;
+ }
+ else
+ {
+ mark_target_for_updating( arg_v[ n ] );
+ }
+ }
+
+ if (!targets_to_update())
+ mark_target_for_updating("all");
+
+ /* Parse ruleset. */
+ {
+ FRAME frame[ 1 ];
+ frame_init( frame );
+ for ( n = 0; ( s = getoptval( optv, 'f', n ) ); ++n )
+ parse_file( s, frame );
+
+ if ( !n )
+ parse_file( "+", frame );
+ }
+
+ status = yyanyerrors();
+
+ /* Manually touch -t targets. */
+ for ( n = 0; ( s = getoptval( optv, 't', n ) ); ++n )
+ touch_target( s );
+
+ /* If an output file is specified, set globs.cmdout to that. */
+ if ( ( s = getoptval( optv, 'o', 0 ) ) )
+ {
+ if ( !( globs.cmdout = fopen( s, "w" ) ) )
+ {
+ printf( "Failed to write to '%s'\n", s );
+ exit( EXITBAD );
+ }
+ ++globs.noexec;
+ }
+
+ /* The build system may set the PARALLELISM variable to override -j
+ options. */
+ {
+ LIST *p = L0;
+ p = var_get ("PARALLELISM");
+ if (p)
+ {
+ int j = atoi (p->string);
+ if (j == -1)
+ {
+ printf( "Invalid value of PARALLELISM: %s\n", p->string);
+ }
+ else
+ {
+ globs.jobs = j;
+ }
+ }
+ }
+
+ /* KEEP_GOING overrides -q option. */
+ {
+ LIST *p = L0;
+ p = var_get ("KEEP_GOING");
+ if (p)
+ {
+ int v = atoi (p->string);
+ if (v == 0)
+ globs.quitquick = 1;
+ else
+ globs.quitquick = 0;
+ }
+ }
+
+ /* Now make target. */
+ {
+ PROFILE_ENTER( MAIN_MAKE );
+
+ LIST * targets = targets_to_update();
+ if (targets)
+ {
+ int targets_count = list_length( targets );
+ const char * * targets2 = (const char * *)
+ BJAM_MALLOC( targets_count * sizeof( char * ) );
+ int n = 0;
+ for ( ; targets; targets = list_next( targets ) )
+ targets2[ n++ ] = targets->string;
+ status |= make( targets_count, targets2, anyhow );
+ free( targets );
+ }
+ else
+ {
+ status = last_update_now_status;
+ }
+
+ PROFILE_EXIT( MAIN_MAKE );
+ }
+
+ PROFILE_EXIT( MAIN );
+ }
+
+ if ( DEBUG_PROFILE )
+ profile_dump();
+
+ /* Widely scattered cleanup. */
+ var_done();
+ file_done();
+ rules_done();
+ stamps_done();
+ str_done();
+
+ /* Close cmdout. */
+ if ( globs.cmdout )
+ fclose( globs.cmdout );
+
+#ifdef HAVE_PYTHON
+ Py_Finalize();
+#endif
+
+ BJAM_MEM_CLOSE();
+
+ return status ? EXITBAD : EXITOK;
+}
+
+#if defined(_WIN32)
+#include <windows.h>
+char *executable_path(char *argv0) {
+ char buf[1024];
+ DWORD ret = GetModuleFileName(NULL, buf, sizeof(buf));
+ if (ret == 0 || ret == sizeof(buf)) return NULL;
+ return strdup (buf);
+}
+#elif defined(__APPLE__) /* Not tested */
+#include <mach-o/dyld.h>
+char *executable_path(char *argv0) {
+ char buf[1024];
+ uint32_t size = sizeof(buf);
+ int ret = _NSGetExecutablePath(buf, &size);
+ if (ret != 0) return NULL;
+ return strdup(buf);
+}
+#elif defined(sun) || defined(__sun) /* Not tested */
+#include <stdlib.h>
+
+char *executable_path(char *argv0) {
+ return strdup(getexecname());
+}
+#elif defined(__FreeBSD__)
+#include <sys/sysctl.h>
+char *executable_path(char *argv0) {
+ int mib[4];
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PATHNAME;
+ mib[3] = -1;
+ char buf[1024];
+ size_t size = sizeof(buf);
+ sysctl(mib, 4, buf, &size, NULL, 0);
+ if (size == 0 || size == sizeof(buf)) return NULL;
+ return strndup(buf, size);
+}
+#elif defined(__linux__)
+#include <unistd.h>
+char *executable_path(char *argv0) {
+ char buf[1024];
+ ssize_t ret = readlink("/proc/self/exe", buf, sizeof(buf));
+ if (ret == 0 || ret == sizeof(buf)) return NULL;
+ return strndup(buf, ret);
+}
+#else
+char *executable_path(char *argv0) {
+ /* If argv0 is absolute path, assume it's the right absolute path. */
+ if (argv0[0] == "/")
+ return strdup(argv0);
+ return NULL;
+}
+#endif
diff --git a/jam-files/engine/jam.h b/jam-files/engine/jam.h
new file mode 100644
index 00000000..73a7a04c
--- /dev/null
+++ b/jam-files/engine/jam.h
@@ -0,0 +1,579 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/* This file is ALSO:
+ * Copyright 2001-2004 David Abrahams.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+/*
+ * jam.h - includes and globals for jam
+ *
+ * 04/08/94 (seiwald) - Coherent/386 support added.
+ * 04/21/94 (seiwald) - DGUX is __DGUX__, not just __DGUX.
+ * 05/04/94 (seiwald) - new globs.jobs (-j jobs)
+ * 11/01/94 (wingerd) - let us define path of Jambase at compile time.
+ * 12/30/94 (wingerd) - changed command buffer size for NT (MS-DOS shell).
+ * 02/22/95 (seiwald) - Jambase now in /usr/local/lib.
+ * 04/30/95 (seiwald) - FreeBSD added. Live Free or Die.
+ * 05/10/95 (seiwald) - SPLITPATH character set up here.
+ * 08/20/95 (seiwald) - added LINUX.
+ * 08/21/95 (seiwald) - added NCR.
+ * 10/23/95 (seiwald) - added SCO.
+ * 01/03/96 (seiwald) - SINIX (nixdorf) added.
+ * 03/13/96 (seiwald) - Jambase now compiled in; remove JAMBASE variable.
+ * 04/29/96 (seiwald) - AIX now has 31 and 42 OSVERs.
+ * 11/21/96 (peterk) - added BeOS with MW CW mwcc
+ * 12/21/96 (seiwald) - OSPLAT now defined for NT.
+ * 07/19/99 (sickel) - Mac OS X Server and Client support added
+ * 02/18/00 (belmonte)- Support for Cygwin.
+ * 09/12/00 (seiwald) - OSSYMS split to OSMAJOR/OSMINOR/OSPLAT
+ * 12/29/00 (seiwald) - OSVER dropped.
+ */
+
+#ifndef JAM_H_VP_2003_08_01
+#define JAM_H_VP_2003_08_01
+
+#ifdef HAVE_PYTHON
+#include <Python.h>
+#endif
+
+/* Assume popen support is available unless known otherwise. */
+#define HAVE_POPEN 1
+
+/*
+ * VMS, OPENVMS
+ */
+
+#ifdef VMS
+
+#include <types.h>
+#include <file.h>
+#include <stat.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <unixlib.h>
+
+#define OSMINOR "OS=VMS"
+#define OSMAJOR "VMS=true"
+#define OS_VMS
+#define MAXLINE 1024 /* longest 'together' actions */
+#define SPLITPATH ','
+#define EXITOK 1
+#define EXITBAD 0
+#define DOWNSHIFT_PATHS
+
+/* This may be inaccurate. */
+#ifndef __DECC
+#define OSPLAT "OSPLAT=VAX"
+#endif
+
+#endif
+
+/*
+ * Windows NT
+ */
+
+#ifdef NT
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <malloc.h>
+#ifndef __MWERKS__
+ #include <memory.h>
+#endif
+#include <signal.h>
+#include <string.h>
+#include <time.h>
+
+#define OSMAJOR "NT=true"
+#define OSMINOR "OS=NT"
+#define OS_NT
+#define SPLITPATH ';'
+/* Windows NT 3.51 only allows 996 chars per line, but we deal with the problem
+ * in "execnt.c".
+ */
+#define MAXLINE (maxline()) /* longest 'together' actions */
+#define USE_EXECNT
+#define USE_PATHUNIX
+#define PATH_DELIM '\\'
+#define DOWNSHIFT_PATHS
+
+/* AS400 cross-compile from NT. */
+
+#ifdef AS400
+ #undef OSMINOR
+ #undef OSMAJOR
+ #define OSMAJOR "AS400=true"
+ #define OSMINOR "OS=AS400"
+ #define OS_AS400
+#endif
+
+/* Metrowerks Standard Library on Windows. */
+
+#ifdef __MSL__
+ #undef HAVE_POPEN
+#endif
+
+# endif
+
+/*
+ * Windows MingW32
+ */
+
+#ifdef MINGW
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <malloc.h>
+#include <memory.h>
+#include <signal.h>
+#include <string.h>
+#include <time.h>
+
+#define OSMAJOR "MINGW=true"
+#define OSMINOR "OS=MINGW"
+#define OS_NT
+#define SPLITPATH ';'
+#define MAXLINE 996 /* longest 'together' actions */
+#define USE_EXECUNIX
+#define USE_PATHUNIX
+#define PATH_DELIM '\\'
+#define DOWNSHIFT_PATHS
+
+#endif
+
+/*
+ * OS2
+ */
+
+#ifdef __OS2__
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <malloc.h>
+#include <signal.h>
+#include <string.h>
+#include <time.h>
+
+#define OSMAJOR "OS2=true"
+#define OSMINOR "OS=OS2"
+#define OS_OS2
+#define SPLITPATH ';'
+#define MAXLINE 996 /* longest 'together' actions */
+#define USE_EXECUNIX
+#define USE_PATHUNIX
+#define PATH_DELIM '\\'
+#define DOWNSHIFT_PATHS
+
+#ifdef __EMX__
+ #define USE_FILEUNIX
+#endif
+
+#endif
+
+/*
+ * Macintosh MPW
+ */
+
+#ifdef macintosh
+
+#include <time.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#define OSMAJOR "MAC=true"
+#define OSMINOR "OS=MAC"
+#define OS_MAC
+#define SPLITPATH ','
+
+#endif
+
+/*
+ * God fearing UNIX.
+ */
+
+#ifndef OSMINOR
+
+#define OSMAJOR "UNIX=true"
+#define USE_EXECUNIX
+#define USE_FILEUNIX
+#define USE_PATHUNIX
+#define PATH_DELIM '/'
+
+#ifdef _AIX
+ #define unix
+ #define MAXLINE 23552 /* 24k - 1k, longest 'together' actions */
+ #define OSMINOR "OS=AIX"
+ #define OS_AIX
+ #define NO_VFORK
+#endif
+#ifdef AMIGA
+ #define OSMINOR "OS=AMIGA"
+ #define OS_AMIGA
+#endif
+#ifdef __BEOS__
+ #define unix
+ #define OSMINOR "OS=BEOS"
+ #define OS_BEOS
+ #define NO_VFORK
+#endif
+#ifdef __bsdi__
+ #define OSMINOR "OS=BSDI"
+ #define OS_BSDI
+#endif
+#if defined (COHERENT) && defined (_I386)
+ #define OSMINOR "OS=COHERENT"
+ #define OS_COHERENT
+ #define NO_VFORK
+#endif
+#if defined(__cygwin__) || defined(__CYGWIN__)
+ #define OSMINOR "OS=CYGWIN"
+ #define OS_CYGWIN
+#endif
+#if defined(__FreeBSD__) && !defined(__DragonFly__)
+ #define OSMINOR "OS=FREEBSD"
+ #define OS_FREEBSD
+#endif
+#ifdef __DragonFly__
+ #define OSMINOR "OS=DRAGONFLYBSD"
+ #define OS_DRAGONFLYBSD
+#endif
+#ifdef __DGUX__
+ #define OSMINOR "OS=DGUX"
+ #define OS_DGUX
+#endif
+#ifdef __hpux
+ #define OSMINOR "OS=HPUX"
+ #define OS_HPUX
+#endif
+#ifdef __OPENNT
+ #define unix
+ #define OSMINOR "OS=INTERIX"
+ #define OS_INTERIX
+ #define NO_VFORK
+#endif
+#ifdef __sgi
+ #define OSMINOR "OS=IRIX"
+ #define OS_IRIX
+ #define NO_VFORK
+#endif
+#ifdef __ISC
+ #define OSMINOR "OS=ISC"
+ #define OS_ISC
+ #define NO_VFORK
+#endif
+#ifdef linux
+ #define OSMINOR "OS=LINUX"
+ #define OS_LINUX
+#endif
+#ifdef __Lynx__
+ #define OSMINOR "OS=LYNX"
+ #define OS_LYNX
+ #define NO_VFORK
+ #define unix
+#endif
+#ifdef __MACHTEN__
+ #define OSMINOR "OS=MACHTEN"
+ #define OS_MACHTEN
+#endif
+#ifdef mpeix
+ #define unix
+ #define OSMINOR "OS=MPEIX"
+ #define OS_MPEIX
+ #define NO_VFORK
+#endif
+#ifdef __MVS__
+ #define unix
+ #define OSMINOR "OS=MVS"
+ #define OS_MVS
+#endif
+#ifdef _ATT4
+ #define OSMINOR "OS=NCR"
+ #define OS_NCR
+#endif
+#ifdef __NetBSD__
+ #define unix
+ #define OSMINOR "OS=NETBSD"
+ #define OS_NETBSD
+ #define NO_VFORK
+#endif
+#ifdef __QNX__
+ #define unix
+ #ifdef __QNXNTO__
+ #define OSMINOR "OS=QNXNTO"
+ #define OS_QNXNTO
+ #else
+ #define OSMINOR "OS=QNX"
+ #define OS_QNX
+ #define NO_VFORK
+ #define MAXLINE 996
+ #endif
+#endif
+#ifdef NeXT
+ #ifdef __APPLE__
+ #define OSMINOR "OS=RHAPSODY"
+ #define OS_RHAPSODY
+ #else
+ #define OSMINOR "OS=NEXT"
+ #define OS_NEXT
+ #endif
+#endif
+#ifdef __APPLE__
+ #define unix
+ #define OSMINOR "OS=MACOSX"
+ #define OS_MACOSX
+#endif
+#ifdef __osf__
+ #ifndef unix
+ #define unix
+ #endif
+ #define OSMINOR "OS=OSF"
+ #define OS_OSF
+#endif
+#ifdef _SEQUENT_
+ #define OSMINOR "OS=PTX"
+ #define OS_PTX
+#endif
+#ifdef M_XENIX
+ #define OSMINOR "OS=SCO"
+ #define OS_SCO
+ #define NO_VFORK
+#endif
+#ifdef sinix
+ #define unix
+ #define OSMINOR "OS=SINIX"
+ #define OS_SINIX
+#endif
+#ifdef sun
+ #if defined(__svr4__) || defined(__SVR4)
+ #define OSMINOR "OS=SOLARIS"
+ #define OS_SOLARIS
+ #else
+ #define OSMINOR "OS=SUNOS"
+ #define OS_SUNOS
+ #endif
+#endif
+#ifdef ultrix
+ #define OSMINOR "OS=ULTRIX"
+ #define OS_ULTRIX
+#endif
+#ifdef _UNICOS
+ #define OSMINOR "OS=UNICOS"
+ #define OS_UNICOS
+#endif
+#if defined(__USLC__) && !defined(M_XENIX)
+ #define OSMINOR "OS=UNIXWARE"
+ #define OS_UNIXWARE
+#endif
+#ifdef __OpenBSD__
+ #define OSMINOR "OS=OPENBSD"
+ #define OS_OPENBSD
+ #define unix
+#endif
+#if defined (__FreeBSD_kernel__) && !defined(__FreeBSD__)
+ #define OSMINOR "OS=KFREEBSD"
+ #define OS_KFREEBSD
+#endif
+#ifndef OSMINOR
+ #define OSMINOR "OS=UNKNOWN"
+#endif
+
+/* All the UNIX includes */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef OS_MPEIX
+ #include <sys/file.h>
+#endif
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <signal.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifndef OS_QNX
+ #include <memory.h>
+#endif
+
+#ifndef OS_ULTRIX
+ #include <stdlib.h>
+#endif
+
+#if !defined( OS_BSDI ) && \
+ !defined( OS_FREEBSD ) && \
+ !defined( OS_DRAGONFLYBSD ) && \
+ !defined( OS_NEXT ) && \
+ !defined( OS_MACHTEN ) && \
+ !defined( OS_MACOSX ) && \
+ !defined( OS_RHAPSODY ) && \
+ !defined( OS_MVS ) && \
+ !defined( OS_OPENBSD )
+ #include <malloc.h>
+#endif
+
+#endif
+
+/*
+ * OSPLAT definitions - suppressed when it is a one-of-a-kind.
+ */
+
+#if defined( _M_PPC ) || \
+ defined( PPC ) || \
+ defined( ppc ) || \
+ defined( __powerpc__ ) || \
+ defined( __ppc__ )
+ #define OSPLAT "OSPLAT=PPC"
+#endif
+
+#if defined( _ALPHA_ ) || \
+ defined( __alpha__ )
+ #define OSPLAT "OSPLAT=AXP"
+#endif
+
+#if defined( _i386_ ) || \
+ defined( __i386__ ) || \
+ defined( __i386 ) || \
+ defined( _M_IX86 )
+ #define OSPLAT "OSPLAT=X86"
+#endif
+
+#if defined( __ia64__ ) || \
+ defined( __IA64__ ) || \
+ defined( __ia64 )
+ #define OSPLAT "OSPLAT=IA64"
+#endif
+
+#if defined( __x86_64__ ) || \
+ defined( __amd64__ ) || \
+ defined( _M_AMD64 )
+ #define OSPLAT "OSPLAT=X86_64"
+#endif
+
+
+#if defined( __sparc__ ) || \
+ defined( __sparc )
+ #define OSPLAT "OSPLAT=SPARC"
+#endif
+
+#ifdef __mips__
+ #define OSPLAT "OSPLAT=MIPS"
+#endif
+
+#ifdef __arm__
+ #define OSPLAT "OSPLAT=ARM"
+#endif
+
+#ifdef __s390__
+ #define OSPLAT "OSPLAT=390"
+#endif
+
+#ifdef __hppa
+ #define OSPLAT "OSPLAT=PARISC"
+#endif
+
+#ifndef OSPLAT
+ #define OSPLAT ""
+#endif
+
+/*
+ * Jam implementation misc.
+ */
+
+#ifndef MAXLINE
+ #define MAXLINE 102400 /* longest 'together' actions' */
+#endif
+
+#ifndef EXITOK
+ #define EXITOK 0
+ #define EXITBAD 1
+#endif
+
+#ifndef SPLITPATH
+ #define SPLITPATH ':'
+#endif
+
+/* You probably do not need to muck with these. */
+
+#define MAXSYM 1024 /* longest symbol in the environment */
+#define MAXJPATH 1024 /* longest filename */
+
+#define MAXJOBS 64 /* silently enforced -j limit */
+#define MAXARGC 32 /* words in $(JAMSHELL) */
+
+/* Jam private definitions below. */
+
+#define DEBUG_MAX 14
+
+
+struct globs
+{
+ int noexec;
+ int jobs;
+ int quitquick;
+ int newestfirst; /* build newest sources first */
+ int pipe_action;
+ char debug[ DEBUG_MAX ];
+ FILE * cmdout; /* print cmds, not run them */
+ long timeout; /* number of seconds to limit actions to,
+ * default 0 for no limit.
+ */
+ int dart; /* output build and test results formatted for Dart */
+};
+
+extern struct globs globs;
+
+#define DEBUG_MAKE ( globs.debug[ 1 ] ) /* show actions when executed */
+#define DEBUG_MAKEQ ( globs.debug[ 2 ] ) /* show even quiet actions */
+#define DEBUG_EXEC ( globs.debug[ 2 ] ) /* show text of actons */
+#define DEBUG_MAKEPROG ( globs.debug[ 3 ] ) /* show progress of make0 */
+#define DEBUG_BIND ( globs.debug[ 3 ] ) /* show when files bound */
+
+#define DEBUG_EXECCMD ( globs.debug[ 4 ] ) /* show execcmds()'s work */
+
+#define DEBUG_COMPILE ( globs.debug[ 5 ] ) /* show rule invocations */
+
+#define DEBUG_HEADER ( globs.debug[ 6 ] ) /* show result of header scan */
+#define DEBUG_BINDSCAN ( globs.debug[ 6 ] ) /* show result of dir scan */
+#define DEBUG_SEARCH ( globs.debug[ 6 ] ) /* show attempts at binding */
+
+#define DEBUG_VARSET ( globs.debug[ 7 ] ) /* show variable settings */
+#define DEBUG_VARGET ( globs.debug[ 8 ] ) /* show variable fetches */
+#define DEBUG_VAREXP ( globs.debug[ 8 ] ) /* show variable expansions */
+#define DEBUG_IF ( globs.debug[ 8 ] ) /* show 'if' calculations */
+#define DEBUG_LISTS ( globs.debug[ 9 ] ) /* show list manipulation */
+#define DEBUG_SCAN ( globs.debug[ 9 ] ) /* show scanner tokens */
+#define DEBUG_MEM ( globs.debug[ 9 ] ) /* show memory use */
+
+#define DEBUG_PROFILE ( globs.debug[ 10 ] ) /* dump rule execution times */
+#define DEBUG_PARSE ( globs.debug[ 11 ] ) /* debug parsing */
+#define DEBUG_GRAPH ( globs.debug[ 12 ] ) /* debug dependencies */
+#define DEBUG_FATE ( globs.debug[ 13 ] ) /* show changes to fate in make0() */
+
+/* Everyone gets the memory definitions. */
+#include "mem.h"
+
+/* They also get the profile functions. */
+#include "debug.h"
+
+#endif
diff --git a/jam-files/engine/jambase.c b/jam-files/engine/jambase.c
new file mode 100644
index 00000000..b15282bc
--- /dev/null
+++ b/jam-files/engine/jambase.c
@@ -0,0 +1,1691 @@
+/* Generated by mkjambase from Jambase */
+char *jambase[] = {
+/* Jambase */
+"if $(NT)\n",
+"{\n",
+"SLASH ?= \\\\ ;\n",
+"}\n",
+"SLASH ?= / ;\n",
+"rule find-to-root ( dir : patterns + )\n",
+"{\n",
+"local globs = [ GLOB $(dir) : $(patterns) ] ;\n",
+"while ! $(globs) && $(dir:P) != $(dir)\n",
+"{\n",
+"dir = $(dir:P) ;\n",
+"globs = [ GLOB $(dir) : $(patterns) ] ;\n",
+"}\n",
+"return $(globs) ;\n",
+"}\n",
+".boost-build-file = ;\n",
+".bootstrap-file = ;\n",
+"BOOST_BUILD_PATH.user-value = $(BOOST_BUILD_PATH) ;\n",
+"if ! $(BOOST_BUILD_PATH) && $(UNIX)\n",
+"{\n",
+"BOOST_BUILD_PATH = /usr/share/boost-build ;\n",
+"}\n",
+"rule _poke ( module-name ? : variables + : value * )\n",
+"{\n",
+"module $(<)\n",
+"{\n",
+"$(>) = $(3) ;\n",
+"}\n",
+"}\n",
+"rule boost-build ( dir ? )\n",
+"{\n",
+"if $(.bootstrap-file)\n",
+"{\n",
+"ECHO \"Error: Illegal attempt to re-bootstrap the build system by invoking\" ;\n",
+"ECHO ;\n",
+"ECHO \" 'boost-build\" $(dir) \";'\" ;\n",
+"ECHO ;\n",
+"EXIT \"Please consult the documentation at 'http://www.boost.org'.\" ;\n",
+"}\n",
+"BOOST_BUILD_PATH = $(dir:R=$(.boost-build-file:D)) $(BOOST_BUILD_PATH) ;\n",
+"_poke .ENVIRON : BOOST_BUILD_PATH : $(BOOST_BUILD_PATH) ;\n",
+"local bootstrap-file = [ GLOB $(BOOST_BUILD_PATH) : bootstrap.jam ] ;\n",
+".bootstrap-file = $(bootstrap-file[1]) ;\n",
+"if ! $(.bootstrap-file)\n",
+"{\n",
+"ECHO \"Unable to load Boost.Build: could not find build system.\" ;\n",
+"ECHO --------------------------------------------------------- ;\n",
+"ECHO \"$(.boost-build-file) attempted to load the build system by invoking\" ;\n",
+"ECHO ;\n",
+"ECHO \" 'boost-build\" $(dir) \";'\" ;\n",
+"ECHO ;\n",
+"ECHO \"but we were unable to find \\\"bootstrap.jam\\\" in the specified directory\" ;\n",
+"ECHO \"or in BOOST_BUILD_PATH (searching \"$(BOOST_BUILD_PATH:J=\", \")\").\" ;\n",
+"ECHO ;\n",
+"EXIT \"Please consult the documentation at 'http://www.boost.org'.\" ;\n",
+"}\n",
+"if [ MATCH .*(--debug-configuration).* : $(ARGV) ]\n",
+"{\n",
+"ECHO \"notice: loading Boost.Build from\"\n",
+"[ NORMALIZE_PATH $(.bootstrap-file:D) ] ;\n",
+"}\n",
+"include $(.bootstrap-file) ;\n",
+"}\n",
+"if [ MATCH .*(b2).* : $(ARGV[1]:BL) ] \n",
+"|| [ MATCH .*(bjam).* : $(ARGV[1]:BL) ]\n",
+"|| $(BOOST_ROOT) # A temporary measure so Jam works with Boost.Build v1.\n",
+"{\n",
+"local search-path = $(BOOST_BUILD_PATH) $(BOOST_ROOT) ;\n",
+"local self = [ SELF_PATH ] ;\n",
+"local boost-build-relative = ../../share/boost-build ;\n",
+"local self-based-path = [ NORMALIZE_PATH $(boost-build-relative:R=$(self)) ] ;\n",
+"local boost-build-files =\n",
+"[ find-to-root [ PWD ] : boost-build.jam ]\n",
+"[ GLOB $(self-based-path) : boost-build.jam ]\n",
+"[ GLOB $(search-path) : boost-build.jam ] ;\n",
+".boost-build-file = $(boost-build-files[1]) ;\n",
+"if ! $(.boost-build-file)\n",
+"{\n",
+"ECHO \"Unable to load Boost.Build: could not find \\\"boost-build.jam\\\"\" ;\n",
+"ECHO --------------------------------------------------------------- ;\n",
+"if ! [ MATCH .*(bjam).* : $(ARGV[1]:BL) ]\n",
+"{\n",
+"ECHO \"BOOST_ROOT must be set, either in the environment, or \" ;\n",
+"ECHO \"on the command-line with -sBOOST_ROOT=..., to the root\" ;\n",
+"ECHO \"of the boost installation.\" ;\n",
+"ECHO ;\n",
+"}\n",
+"ECHO \"Attempted search from\" [ PWD ] \"up to the root\" ;\n",
+"ECHO \"at\" $(self-based-path) ;\n",
+"ECHO \"and in these directories from BOOST_BUILD_PATH and BOOST_ROOT: \"$(search-path:J=\", \")\".\" ;\n",
+"EXIT \"Please consult the documentation at 'http://www.boost.org'.\" ;\n",
+"}\n",
+"if [ MATCH .*(--debug-configuration).* : $(ARGV) ]\n",
+"{\n",
+"ECHO \"notice: found boost-build.jam at\"\n",
+"[ NORMALIZE_PATH $(.boost-build-file) ] ;\n",
+"}\n",
+"include $(.boost-build-file) ;\n",
+"if ! $(.bootstrap-file)\n",
+"{\n",
+"ECHO \"Unable to load Boost.Build\" ;\n",
+"ECHO -------------------------- ;\n",
+"ECHO \"\\\"$(.boost-build-file)\\\" was found by searching from\" [ PWD ] \"up to the root\" ;\n",
+"ECHO \"and in these directories from BOOST_BUILD_PATH and BOOST_ROOT: \"$(search-path:J=\", \")\".\" ;\n",
+"ECHO ;\n",
+"ECHO \"However, it failed to call the \\\"boost-build\\\" rule to indicate\" ;\n",
+"ECHO \"the location of the build system.\" ;\n",
+"ECHO ;\n",
+"EXIT \"Please consult the documentation at 'http://www.boost.org'.\" ;\n",
+"}\n",
+"}\n",
+"else\n",
+"{\n",
+"if $(NT)\n",
+"{\n",
+"local SUPPORTED_TOOLSETS = \"BORLANDC\" \"VC7\" \"VISUALC\" \"VISUALC16\" \"INTELC\" \"WATCOM\"\n",
+"\"MINGW\" \"LCC\" ;\n",
+"TOOLSET = \"\" ;\n",
+"if $(JAM_TOOLSET)\n",
+"{\n",
+"local t ;\n",
+"for t in $(SUPPORTED_TOOLSETS)\n",
+"{\n",
+"$(t) = $($(t):J=\" \") ; # reconstitute paths with spaces in them\n",
+"if $(t) = $(JAM_TOOLSET) { TOOLSET = $(t) ; }\n",
+"}\n",
+"if ! $(TOOLSET)\n",
+"{\n",
+"ECHO \"The JAM_TOOLSET environment variable is defined but its value\" ;\n",
+"ECHO \"is invalid, please use one of the following:\" ;\n",
+"ECHO ;\n",
+"for t in $(SUPPORTED_TOOLSETS) { ECHO \" \" $(t) ; }\n",
+"EXIT ;\n",
+"}\n",
+"}\n",
+"if ! $(TOOLSET)\n",
+"{\n",
+"if $(BCCROOT)\n",
+"{\n",
+"TOOLSET = BORLANDC ;\n",
+"BORLANDC = $(BCCROOT:J=\" \") ;\n",
+"}\n",
+"else if $(MSVC)\n",
+"{\n",
+"TOOLSET = VISUALC16 ;\n",
+"VISUALC16 = $(MSVC:J=\" \") ;\n",
+"}\n",
+"else if $(MSVCNT)\n",
+"{\n",
+"TOOLSET = VISUALC ;\n",
+"VISUALC = $(MSVCNT:J=\" \") ;\n",
+"}\n",
+"else if $(MSVCDir)\n",
+"{\n",
+"TOOLSET = VISUALC ;\n",
+"VISUALC = $(MSVCDir:J=\" \") ;\n",
+"}\n",
+"else if $(MINGW)\n",
+"{\n",
+"TOOLSET = MINGW ;\n",
+"}\n",
+"else\n",
+"{\n",
+"ECHO \"Jam cannot be run because, either:\" ;\n",
+"ECHO \" a. You didn't set BOOST_ROOT to indicate the root of your\" ;\n",
+"ECHO \" Boost installation.\" ;\n",
+"ECHO \" b. You are trying to use stock Jam but didn't indicate which\" ;\n",
+"ECHO \" compilation toolset to use. To do so, follow these simple\" ;\n",
+"ECHO \" instructions:\" ;\n",
+"ECHO ;\n",
+"ECHO \" - define one of the following environment variable, with the\" ;\n",
+"ECHO \" appropriate value according to this list:\" ;\n",
+"ECHO ;\n",
+"ECHO \" Variable Toolset Description\" ;\n",
+"ECHO ;\n",
+"ECHO \" BORLANDC Borland C++ BC++ install path\" ;\n",
+"ECHO \" VISUALC Microsoft Visual C++ VC++ install path\" ;\n",
+"ECHO \" VISUALC16 Microsoft Visual C++ 16 bit VC++ 16 bit install\" ;\n",
+"ECHO \" INTELC Intel C/C++ IC++ install path\" ;\n",
+"ECHO \" WATCOM Watcom C/C++ Watcom install path\" ;\n",
+"ECHO \" MINGW MinGW (gcc) MinGW install path\" ;\n",
+"ECHO \" LCC Win32-LCC LCC-Win32 install path\" ;\n",
+"ECHO ;\n",
+"ECHO \" - define the JAM_TOOLSET environment variable with the *name*\" ;\n",
+"ECHO \" of the toolset variable you want to use.\" ;\n",
+"ECHO ;\n",
+"ECHO \" e.g.: set VISUALC=C:\\\\Visual6\" ;\n",
+"ECHO \" set JAM_TOOLSET=VISUALC\" ;\n",
+"EXIT ;\n",
+"}\n",
+"}\n",
+"CP ?= copy ;\n",
+"RM ?= del /f/q ;\n",
+"SLASH ?= \\\\ ;\n",
+"SUFLIB ?= .lib ;\n",
+"SUFOBJ ?= .obj ;\n",
+"SUFEXE ?= .exe ;\n",
+"if $(TOOLSET) = BORLANDC\n",
+"{\n",
+"ECHO \"Compiler is Borland C++\" ;\n",
+"AR ?= tlib /C /P64 ;\n",
+"CC ?= bcc32 ;\n",
+"CCFLAGS ?= -q -y -d -v -w-par -w-ccc -w-rch -w-pro -w-aus ;\n",
+"C++ ?= bcc32 ;\n",
+"C++FLAGS ?= -q -y -d -v -w-par -w-ccc -w-rch -w-pro -w-aus -P ;\n",
+"LINK ?= $(CC) ;\n",
+"LINKFLAGS ?= $(CCFLAGS) ;\n",
+"STDLIBPATH ?= $(BORLANDC)\\\\lib ;\n",
+"STDHDRS ?= $(BORLANDC)\\\\include ;\n",
+"NOARSCAN ?= true ;\n",
+"}\n",
+"else if $(TOOLSET) = VISUALC16\n",
+"{\n",
+"ECHO \"Compiler is Microsoft Visual C++ 16 bit\" ;\n",
+"AR ?= lib /nologo ;\n",
+"CC ?= cl /nologo ;\n",
+"CCFLAGS ?= /D \\\"WIN\\\" ;\n",
+"C++ ?= $(CC) ;\n",
+"C++FLAGS ?= $(CCFLAGS) ;\n",
+"LINK ?= $(CC) ;\n",
+"LINKFLAGS ?= $(CCFLAGS) ;\n",
+"LINKLIBS ?=\n",
+"\\\"$(VISUALC16)\\\\lib\\\\mlibce.lib\\\"\n",
+"\\\"$(VISUALC16)\\\\lib\\\\oldnames.lib\\\"\n",
+";\n",
+"LINKLIBS ?= ;\n",
+"NOARSCAN ?= true ;\n",
+"OPTIM ?= \"\" ;\n",
+"STDHDRS ?= $(VISUALC16)\\\\include ;\n",
+"UNDEFFLAG ?= \"/u _\" ;\n",
+"}\n",
+"else if $(TOOLSET) = VISUALC\n",
+"{\n",
+"ECHO \"Compiler is Microsoft Visual C++\" ;\n",
+"AR ?= lib ;\n",
+"AS ?= masm386 ;\n",
+"CC ?= cl /nologo ;\n",
+"CCFLAGS ?= \"\" ;\n",
+"C++ ?= $(CC) ;\n",
+"C++FLAGS ?= $(CCFLAGS) ;\n",
+"LINK ?= link /nologo ;\n",
+"LINKFLAGS ?= \"\" ;\n",
+"LINKLIBS ?= \\\"$(VISUALC)\\\\lib\\\\advapi32.lib\\\"\n",
+"\\\"$(VISUALC)\\\\lib\\\\gdi32.lib\\\"\n",
+"\\\"$(VISUALC)\\\\lib\\\\user32.lib\\\"\n",
+"\\\"$(VISUALC)\\\\lib\\\\kernel32.lib\\\" ;\n",
+"OPTIM ?= \"\" ;\n",
+"STDHDRS ?= $(VISUALC)\\\\include ;\n",
+"UNDEFFLAG ?= \"/u _\" ;\n",
+"}\n",
+"else if $(TOOLSET) = VC7\n",
+"{\n",
+"ECHO \"Compiler is Microsoft Visual C++ .NET\" ;\n",
+"AR ?= lib ;\n",
+"AS ?= masm386 ;\n",
+"CC ?= cl /nologo ;\n",
+"CCFLAGS ?= \"\" ;\n",
+"C++ ?= $(CC) ;\n",
+"C++FLAGS ?= $(CCFLAGS) ;\n",
+"LINK ?= link /nologo ;\n",
+"LINKFLAGS ?= \"\" ;\n",
+"LINKLIBS ?= \\\"$(VISUALC)\\\\PlatformSDK\\\\lib\\\\advapi32.lib\\\"\n",
+"\\\"$(VISUALC)\\\\PlatformSDK\\\\lib\\\\gdi32.lib\\\"\n",
+"\\\"$(VISUALC)\\\\PlatformSDK\\\\lib\\\\user32.lib\\\"\n",
+"\\\"$(VISUALC)\\\\PlatformSDK\\\\lib\\\\kernel32.lib\\\" ;\n",
+"OPTIM ?= \"\" ;\n",
+"STDHDRS ?= \\\"$(VISUALC)\\\\include\\\"\n",
+"\\\"$(VISUALC)\\\\PlatformSDK\\\\include\\\" ;\n",
+"UNDEFFLAG ?= \"/u _\" ;\n",
+"}\n",
+"else if $(TOOLSET) = INTELC\n",
+"{\n",
+"ECHO \"Compiler is Intel C/C++\" ;\n",
+"if ! $(VISUALC)\n",
+"{\n",
+"ECHO \"As a special exception, when using the Intel C++ compiler, you need\" ;\n",
+"ECHO \"to define the VISUALC environment variable to indicate the location\" ;\n",
+"ECHO \"of your Visual C++ installation. Aborting..\" ;\n",
+"EXIT ;\n",
+"}\n",
+"AR ?= lib ;\n",
+"AS ?= masm386 ;\n",
+"CC ?= icl /nologo ;\n",
+"CCFLAGS ?= \"\" ;\n",
+"C++ ?= $(CC) ;\n",
+"C++FLAGS ?= $(CCFLAGS) ;\n",
+"LINK ?= link /nologo ;\n",
+"LINKFLAGS ?= \"\" ;\n",
+"LINKLIBS ?= $(VISUALC)\\\\lib\\\\advapi32.lib\n",
+"$(VISUALC)\\\\lib\\\\kernel32.lib\n",
+";\n",
+"OPTIM ?= \"\" ;\n",
+"STDHDRS ?= $(INTELC)\\include $(VISUALC)\\\\include ;\n",
+"UNDEFFLAG ?= \"/u _\" ;\n",
+"}\n",
+"else if $(TOOLSET) = WATCOM\n",
+"{\n",
+"ECHO \"Compiler is Watcom C/C++\" ;\n",
+"AR ?= wlib ;\n",
+"CC ?= wcc386 ;\n",
+"CCFLAGS ?= /zq /DWIN32 /I$(WATCOM)\\\\h ; # zq=quiet\n",
+"C++ ?= wpp386 ;\n",
+"C++FLAGS ?= $(CCFLAGS) ;\n",
+"CP ?= copy ;\n",
+"DOT ?= . ;\n",
+"DOTDOT ?= .. ;\n",
+"LINK ?= wcl386 ;\n",
+"LINKFLAGS ?= /zq ; # zq=quiet\n",
+"LINKLIBS ?= ;\n",
+"MV ?= move ;\n",
+"NOARSCAN ?= true ;\n",
+"OPTIM ?= ;\n",
+"RM ?= del /f ;\n",
+"SLASH ?= \\\\ ;\n",
+"STDHDRS ?= $(WATCOM)\\\\h $(WATCOM)\\\\h\\\\nt ;\n",
+"SUFEXE ?= .exe ;\n",
+"SUFLIB ?= .lib ;\n",
+"SUFOBJ ?= .obj ;\n",
+"UNDEFFLAG ?= \"/u _\" ;\n",
+"}\n",
+"else if $(TOOLSET) = MINGW\n",
+"{\n",
+"ECHO \"Compiler is GCC with Mingw\" ;\n",
+"AR ?= ar -ru ;\n",
+"CC ?= gcc ;\n",
+"CCFLAGS ?= \"\" ;\n",
+"C++ ?= $(CC) ;\n",
+"C++FLAGS ?= $(CCFLAGS) ;\n",
+"LINK ?= $(CC) ;\n",
+"LINKFLAGS ?= \"\" ;\n",
+"LINKLIBS ?= \"\" ;\n",
+"OPTIM ?= ;\n",
+"SUFOBJ = .o ;\n",
+"SUFLIB = .a ;\n",
+"SLASH = / ;\n",
+"}\n",
+"else if $(TOOLSET) = LCC\n",
+"{\n",
+"ECHO \"Compiler is Win32-LCC\" ;\n",
+"AR ?= lcclib ;\n",
+"CC ?= lcc ;\n",
+"CCFLAGS ?= \"\" ;\n",
+"C++ ?= $(CC) ;\n",
+"C++FLAGS ?= $(CCFLAGS) ;\n",
+"LINK ?= lcclnk ;\n",
+"LINKFLAGS ?= \"\" ;\n",
+"LINKLIBS ?= \"\" ;\n",
+"OPTIM ?= ;\n",
+"NOARSCAN = true ;\n",
+"}\n",
+"else\n",
+"{\n",
+"EXIT On NT, set BCCROOT, MSVCNT, MINGW or MSVC to the root of the\n",
+"Borland or Microsoft directories. ;\n",
+"}\n",
+"}\n",
+"else if $(OS2)\n",
+"{\n",
+"local SUPPORTED_TOOLSETS = \"EMX\" \"WATCOM\" ;\n",
+"TOOLSET = \"\" ;\n",
+"if $(JAM_TOOLSET)\n",
+"{\n",
+"local t ;\n",
+"for t in $(SUPPORTED_TOOLSETS)\n",
+"{\n",
+"$(t) = $($(t):J=\" \") ; # reconstitute paths with spaces in them\n",
+"if $(t) = $(JAM_TOOLSET) { TOOLSET = $(t) ; }\n",
+"}\n",
+"if ! $(TOOLSET)\n",
+"{\n",
+"ECHO \"The JAM_TOOLSET environment variable is defined but its value\" ;\n",
+"ECHO \"is invalid, please use one of the following:\" ;\n",
+"ECHO ;\n",
+"for t in $(SUPPORTED_TOOLSETS) { ECHO \" \" $(t) ; }\n",
+"EXIT ;\n",
+"}\n",
+"}\n",
+"if ! $(TOOLSET)\n",
+"{\n",
+"if $(watcom)\n",
+"{\n",
+"WATCOM = $(watcom:J=\" \") ;\n",
+"TOOLSET = WATCOM ;\n",
+"}\n",
+"else\n",
+"{\n",
+"ECHO \"Jam cannot be run because you didn't indicate which compilation toolset\" ;\n",
+"ECHO \"to use. To do so, follow these simple instructions:\" ;\n",
+"ECHO ;\n",
+"ECHO \" - define one of the following environment variable, with the\" ;\n",
+"ECHO \" appropriate value according to this list:\" ;\n",
+"ECHO ;\n",
+"ECHO \" Variable Toolset Description\" ;\n",
+"ECHO ;\n",
+"ECHO \" WATCOM Watcom C/C++ Watcom install path\" ;\n",
+"ECHO \" EMX EMX (gcc) EMX install path\" ;\n",
+"ECHO \" VISUALAGE IBM Visual Age C/C++ VisualAge install path\" ;\n",
+"ECHO ;\n",
+"ECHO \" - define the JAM_TOOLSET environment variable with the *name*\" ;\n",
+"ECHO \" of the toolset variable you want to use.\" ;\n",
+"ECHO ;\n",
+"ECHO \" e.g.: set WATCOM=C:\\WATCOM\" ;\n",
+"ECHO \" set JAM_TOOLSET=WATCOM\" ;\n",
+"ECHO ;\n",
+"EXIT ;\n",
+"}\n",
+"}\n",
+"RM = del /f ;\n",
+"CP = copy ;\n",
+"MV ?= move ;\n",
+"DOT ?= . ;\n",
+"DOTDOT ?= .. ;\n",
+"SUFLIB ?= .lib ;\n",
+"SUFOBJ ?= .obj ;\n",
+"SUFEXE ?= .exe ;\n",
+"if $(TOOLSET) = WATCOM\n",
+"{\n",
+"AR ?= wlib ;\n",
+"BINDIR ?= \\\\os2\\\\apps ;\n",
+"CC ?= wcc386 ;\n",
+"CCFLAGS ?= /zq /DOS2 /I$(WATCOM)\\\\h ; # zq=quiet\n",
+"C++ ?= wpp386 ;\n",
+"C++FLAGS ?= $(CCFLAGS) ;\n",
+"LINK ?= wcl386 ;\n",
+"LINKFLAGS ?= /zq ; # zq=quiet\n",
+"LINKLIBS ?= ;\n",
+"NOARSCAN ?= true ;\n",
+"OPTIM ?= ;\n",
+"SLASH ?= \\\\ ;\n",
+"STDHDRS ?= $(WATCOM)\\\\h ;\n",
+"UNDEFFLAG ?= \"/u _\" ;\n",
+"}\n",
+"else if $(TOOLSET) = EMX\n",
+"{\n",
+"ECHO \"Compiler is GCC-EMX\" ;\n",
+"AR ?= ar -ru ;\n",
+"CC ?= gcc ;\n",
+"CCFLAGS ?= \"\" ;\n",
+"C++ ?= $(CC) ;\n",
+"C++FLAGS ?= $(CCFLAGS) ;\n",
+"LINK ?= $(CC) ;\n",
+"LINKFLAGS ?= \"\" ;\n",
+"LINKLIBS ?= \"\" ;\n",
+"OPTIM ?= ;\n",
+"SUFOBJ = .o ;\n",
+"SUFLIB = .a ;\n",
+"UNDEFFLAG ?= \"-U\" ;\n",
+"SLASH = / ;\n",
+"}\n",
+"else\n",
+"{\n",
+"EXIT \"Sorry, but the $(JAM_TOOLSET) toolset isn't supported for now\" ;\n",
+"}\n",
+"}\n",
+"else if $(VMS)\n",
+"{\n",
+"C++ ?= cxx ;\n",
+"C++FLAGS ?= ;\n",
+"CC ?= cc ;\n",
+"CCFLAGS ?= ;\n",
+"CHMOD ?= set file/prot= ;\n",
+"CP ?= copy/replace ;\n",
+"CRELIB ?= true ;\n",
+"DOT ?= [] ;\n",
+"DOTDOT ?= [-] ;\n",
+"EXEMODE ?= (w:e) ;\n",
+"FILEMODE ?= (w:r) ;\n",
+"HDRS ?= ;\n",
+"LINK ?= link ;\n",
+"LINKFLAGS ?= \"\" ;\n",
+"LINKLIBS ?= ;\n",
+"MKDIR ?= create/dir ;\n",
+"MV ?= rename ;\n",
+"OPTIM ?= \"\" ;\n",
+"RM ?= delete ;\n",
+"RUNVMS ?= mcr ;\n",
+"SHELLMODE ?= (w:er) ;\n",
+"SLASH ?= . ;\n",
+"STDHDRS ?= decc$library_include ;\n",
+"SUFEXE ?= .exe ;\n",
+"SUFLIB ?= .olb ;\n",
+"SUFOBJ ?= .obj ;\n",
+"switch $(OS)\n",
+"{\n",
+"case OPENVMS : CCFLAGS ?= /stand=vaxc ;\n",
+"case VMS : LINKLIBS ?= sys$library:vaxcrtl.olb/lib ;\n",
+"}\n",
+"}\n",
+"else if $(MAC)\n",
+"{\n",
+"local OPT ;\n",
+"CW ?= \"{CW}\" ;\n",
+"MACHDRS ?=\n",
+"\"$(UMACHDRS):Universal:Interfaces:CIncludes\"\n",
+"\"$(CW):MSL:MSL_C:MSL_Common:Include\"\n",
+"\"$(CW):MSL:MSL_C:MSL_MacOS:Include\" ;\n",
+"MACLIBS ?=\n",
+"\"$(CW):MacOS Support:Universal:Libraries:StubLibraries:Interfacelib\"\n",
+"\"$(CW):MacOS Support:Universal:Libraries:StubLibraries:Mathlib\" ;\n",
+"MPWLIBS ?=\n",
+"\"$(CW):MacOS Support:Libraries:Runtime:Runtime PPC:MSL MPWCRuntime.lib\"\n",
+"\"$(CW):MSL:MSL_C:MSL_MacOS:Lib:PPC:MSL C.PPC MPW.Lib\" ;\n",
+"MPWNLLIBS ?=\n",
+"\"$(CW):MacOS Support:Libraries:Runtime:Runtime PPC:MSL MPWCRuntime.lib\"\n",
+"\"$(CW):MSL:MSL_C:MSL_MacOS:Lib:PPC:MSL C.PPC MPW(NL).Lib\" ;\n",
+"SIOUXHDRS ?= ;\n",
+"SIOUXLIBS ?=\n",
+"\"$(CW):MacOS Support:Libraries:Runtime:Runtime PPC:MSL RuntimePPC.lib\"\n",
+"\"$(CW):MSL:MSL_C:MSL_MacOS:Lib:PPC:MSL SIOUX.PPC.Lib\"\n",
+"\"$(CW):MSL:MSL_C:MSL_MacOS:Lib:PPC:MSL C.PPC.Lib\" ;\n",
+"C++ ?= mwcppc ;\n",
+"C++FLAGS ?= -w off -nomapcr ;\n",
+"CC ?= mwcppc ;\n",
+"CCFLAGS ?= -w off -nomapcr ;\n",
+"CP ?= duplicate -y ;\n",
+"DOT ?= \":\" ;\n",
+"DOTDOT ?= \"::\" ;\n",
+"HDRS ?= $(MACHDRS) $(MPWHDRS) ;\n",
+"LINK ?= mwlinkppc ;\n",
+"LINKFLAGS ?= -mpwtool -warn ;\n",
+"LINKLIBS ?= $(MACLIBS) $(MPWLIBS) ;\n",
+"MKDIR ?= newfolder ;\n",
+"MV ?= rename -y ;\n",
+"NOARSCAN ?= true ;\n",
+"OPTIM ?= ;\n",
+"RM ?= delete -y ;\n",
+"SLASH ?= \":\" ;\n",
+"STDHDRS ?= ;\n",
+"SUFLIB ?= .lib ;\n",
+"SUFOBJ ?= .o ;\n",
+"}\n",
+"else if $(OS) = BEOS && $(METROWERKS)\n",
+"{\n",
+"AR ?= mwld -xml -o ;\n",
+"BINDIR ?= /boot/apps ;\n",
+"CC ?= mwcc ;\n",
+"CCFLAGS ?= -nosyspath ;\n",
+"C++ ?= $(CC) ;\n",
+"C++FLAGS ?= -nosyspath ;\n",
+"FORTRAN ?= \"\" ;\n",
+"LIBDIR ?= /boot/develop/libraries ;\n",
+"LINK ?= mwld ;\n",
+"LINKFLAGS ?= \"\" ;\n",
+"MANDIR ?= /boot/documentation/\"Shell Tools\"/HTML ;\n",
+"NOARSCAN ?= true ;\n",
+"STDHDRS ?= /boot/develop/headers/posix ;\n",
+"}\n",
+"else if $(OS) = BEOS\n",
+"{\n",
+"BINDIR ?= /boot/apps ;\n",
+"CC ?= gcc ;\n",
+"C++ ?= $(CC) ;\n",
+"FORTRAN ?= \"\" ;\n",
+"LIBDIR ?= /boot/develop/libraries ;\n",
+"LINK ?= gcc ;\n",
+"LINKLIBS ?= -lnet ;\n",
+"NOARSCAN ?= true ;\n",
+"STDHDRS ?= /boot/develop/headers/posix ;\n",
+"}\n",
+"else if $(UNIX)\n",
+"{\n",
+"switch $(OS)\n",
+"{\n",
+"case AIX :\n",
+"LINKLIBS ?= -lbsd ;\n",
+"case AMIGA :\n",
+"CC ?= gcc ;\n",
+"YACC ?= \"bison -y\" ;\n",
+"case CYGWIN :\n",
+"CC ?= gcc ;\n",
+"CCFLAGS += -D__cygwin__ ;\n",
+"LEX ?= flex ;\n",
+"RANLIB ?= \"\" ;\n",
+"SUFEXE ?= .exe ;\n",
+"YACC ?= \"bison -y\" ;\n",
+"case DGUX :\n",
+"RANLIB ?= \"\" ;\n",
+"RELOCATE ?= true ;\n",
+"case HPUX :\n",
+"YACC = ;\n",
+"CFLAGS += -Ae ;\n",
+"CCFLAGS += -Ae ;\n",
+"RANLIB ?= \"\" ;\n",
+"case INTERIX :\n",
+"CC ?= gcc ;\n",
+"RANLIB ?= \"\" ;\n",
+"case IRIX :\n",
+"RANLIB ?= \"\" ;\n",
+"case MPEIX :\n",
+"CC ?= gcc ;\n",
+"C++ ?= gcc ;\n",
+"CCFLAGS += -D_POSIX_SOURCE ;\n",
+"HDRS += /usr/include ;\n",
+"RANLIB ?= \"\" ;\n",
+"NOARSCAN ?= true ;\n",
+"NOARUPDATE ?= true ;\n",
+"case MVS :\n",
+"RANLIB ?= \"\" ;\n",
+"case NEXT :\n",
+"AR ?= libtool -o ;\n",
+"RANLIB ?= \"\" ;\n",
+"case MACOSX :\n",
+"AR ?= libtool -o ;\n",
+"C++ ?= c++ ;\n",
+"MANDIR ?= /usr/local/share/man ;\n",
+"RANLIB ?= \"\" ;\n",
+"case NCR :\n",
+"RANLIB ?= \"\" ;\n",
+"case PTX :\n",
+"RANLIB ?= \"\" ;\n",
+"case QNX :\n",
+"AR ?= wlib ;\n",
+"CC ?= cc ;\n",
+"CCFLAGS ?= -Q ; # quiet\n",
+"C++ ?= $(CC) ;\n",
+"C++FLAGS ?= -Q ; # quiet\n",
+"LINK ?= $(CC) ;\n",
+"LINKFLAGS ?= -Q ; # quiet\n",
+"NOARSCAN ?= true ;\n",
+"RANLIB ?= \"\" ;\n",
+"case SCO :\n",
+"RANLIB ?= \"\" ;\n",
+"RELOCATE ?= true ;\n",
+"case SINIX :\n",
+"RANLIB ?= \"\" ;\n",
+"case SOLARIS :\n",
+"RANLIB ?= \"\" ;\n",
+"AR ?= \"/usr/ccs/bin/ar ru\" ;\n",
+"case UNICOS :\n",
+"NOARSCAN ?= true ;\n",
+"OPTIM ?= -O0 ;\n",
+"case UNIXWARE :\n",
+"RANLIB ?= \"\" ;\n",
+"RELOCATE ?= true ;\n",
+"}\n",
+"CCFLAGS ?= ;\n",
+"C++FLAGS ?= $(CCFLAGS) ;\n",
+"CHMOD ?= chmod ;\n",
+"CHGRP ?= chgrp ;\n",
+"CHOWN ?= chown ;\n",
+"LEX ?= lex ;\n",
+"LINKFLAGS ?= $(CCFLAGS) ;\n",
+"LINKLIBS ?= ;\n",
+"OPTIM ?= -O ;\n",
+"RANLIB ?= ranlib ;\n",
+"YACC ?= yacc ;\n",
+"YACCFILES ?= y.tab ;\n",
+"YACCFLAGS ?= -d ;\n",
+"}\n",
+"AR ?= ar ru ;\n",
+"AS ?= as ;\n",
+"ASFLAGS ?= ;\n",
+"AWK ?= awk ;\n",
+"BINDIR ?= /usr/local/bin ;\n",
+"C++ ?= cc ;\n",
+"C++FLAGS ?= ;\n",
+"CC ?= cc ;\n",
+"CCFLAGS ?= ;\n",
+"CP ?= cp -f ;\n",
+"CRELIB ?= ;\n",
+"DOT ?= . ;\n",
+"DOTDOT ?= .. ;\n",
+"EXEMODE ?= 711 ;\n",
+"FILEMODE ?= 644 ;\n",
+"FORTRAN ?= f77 ;\n",
+"FORTRANFLAGS ?= ;\n",
+"HDRS ?= ;\n",
+"INSTALLGRIST ?= installed ;\n",
+"JAMFILE ?= Jamfile ;\n",
+"JAMRULES ?= Jamrules ;\n",
+"LEX ?= ;\n",
+"LIBDIR ?= /usr/local/lib ;\n",
+"LINK ?= $(CC) ;\n",
+"LINKFLAGS ?= ;\n",
+"LINKLIBS ?= ;\n",
+"LN ?= ln ;\n",
+"MANDIR ?= /usr/local/man ;\n",
+"MKDIR ?= mkdir ;\n",
+"MV ?= mv -f ;\n",
+"OPTIM ?= ;\n",
+"RCP ?= rcp ;\n",
+"RM ?= rm -f ;\n",
+"RSH ?= rsh ;\n",
+"SED ?= sed ;\n",
+"SHELLHEADER ?= \"#!/bin/sh\" ;\n",
+"SHELLMODE ?= 755 ;\n",
+"SLASH ?= / ;\n",
+"STDHDRS ?= /usr/include ;\n",
+"SUFEXE ?= \"\" ;\n",
+"SUFLIB ?= .a ;\n",
+"SUFOBJ ?= .o ;\n",
+"UNDEFFLAG ?= \"-u _\" ;\n",
+"YACC ?= ;\n",
+"YACCFILES ?= ;\n",
+"YACCFLAGS ?= ;\n",
+"HDRPATTERN =\n",
+"\"^[ ]*#[ ]*include[ ]*[<\\\"]([^\\\">]*)[\\\">].*$\" ;\n",
+"OSFULL = $(OS)$(OSVER)$(OSPLAT) $(OS)$(OSPLAT) $(OS)$(OSVER) $(OS) ;\n",
+"DEPENDS all : shell files lib exe obj ;\n",
+"DEPENDS all shell files lib exe obj : first ;\n",
+"NOTFILE all first shell files lib exe obj dirs clean uninstall ;\n",
+"ALWAYS clean uninstall ;\n",
+"rule As\n",
+"{\n",
+"DEPENDS $(<) : $(>) ;\n",
+"ASFLAGS on $(<) += $(ASFLAGS) $(SUBDIRASFLAGS) ;\n",
+"}\n",
+"rule Bulk\n",
+"{\n",
+"local i ;\n",
+"for i in $(>)\n",
+"{\n",
+"File $(i:D=$(<)) : $(i) ;\n",
+"}\n",
+"}\n",
+"rule Cc\n",
+"{\n",
+"local _h ;\n",
+"DEPENDS $(<) : $(>) ;\n",
+"CCFLAGS on $(<) += $(CCFLAGS) $(SUBDIRCCFLAGS) ;\n",
+"if $(RELOCATE)\n",
+"{\n",
+"CcMv $(<) : $(>) ;\n",
+"}\n",
+"_h = $(SEARCH_SOURCE) $(HDRS) $(SUBDIRHDRS) ;\n",
+"if $(VMS) && $(_h)\n",
+"{\n",
+"SLASHINC on $(<) = \"/inc=(\" $(_h[1]) ,$(_h[2-]) \")\" ;\n",
+"}\n",
+"else if $(MAC) && $(_h)\n",
+"{\n",
+"local _i _j ;\n",
+"_j = $(_h[1]) ;\n",
+"for _i in $(_h[2-])\n",
+"{\n",
+"_j = $(_j),$(_i) ;\n",
+"}\n",
+"MACINC on $(<) = \\\"$(_j)\\\" ;\n",
+"}\n",
+"}\n",
+"rule C++\n",
+"{\n",
+"local _h ;\n",
+"DEPENDS $(<) : $(>) ;\n",
+"C++FLAGS on $(<) += $(C++FLAGS) $(SUBDIRC++FLAGS) ;\n",
+"if $(RELOCATE)\n",
+"{\n",
+"CcMv $(<) : $(>) ;\n",
+"}\n",
+"_h = $(SEARCH_SOURCE) $(HDRS) $(SUBDIRHDRS) ;\n",
+"if $(VMS) && $(_h)\n",
+"{\n",
+"SLASHINC on $(<) = \"/inc=(\" $(_h[1]) ,$(_h[2-]) \")\" ;\n",
+"}\n",
+"else if $(MAC) && $(_h)\n",
+"{\n",
+"local _i _j ;\n",
+"_j = $(_h[1]) ;\n",
+"for _i in $(_h[2-])\n",
+"{\n",
+"_j = $(_j),$(_i) ;\n",
+"}\n",
+"MACINC on $(<) = \\\"$(_j)\\\" ;\n",
+"}\n",
+"}\n",
+"rule Chmod\n",
+"{\n",
+"if $(CHMOD) { Chmod1 $(<) ; }\n",
+"}\n",
+"rule File\n",
+"{\n",
+"DEPENDS files : $(<) ;\n",
+"DEPENDS $(<) : $(>) ;\n",
+"SEARCH on $(>) = $(SEARCH_SOURCE) ;\n",
+"MODE on $(<) = $(FILEMODE) ;\n",
+"Chmod $(<) ;\n",
+"}\n",
+"rule Fortran\n",
+"{\n",
+"DEPENDS $(<) : $(>) ;\n",
+"}\n",
+"rule GenFile\n",
+"{\n",
+"local _t = [ FGristSourceFiles $(<) ] ;\n",
+"local _s = [ FAppendSuffix $(>[1]) : $(SUFEXE) ] ;\n",
+"Depends $(_t) : $(_s) $(>[2-]) ;\n",
+"GenFile1 $(_t) : $(_s) $(>[2-]) ;\n",
+"Clean clean : $(_t) ;\n",
+"}\n",
+"rule GenFile1\n",
+"{\n",
+"MakeLocate $(<) : $(LOCATE_SOURCE) ;\n",
+"SEARCH on $(>) = $(SEARCH_SOURCE) ;\n",
+"}\n",
+"rule HardLink\n",
+"{\n",
+"DEPENDS files : $(<) ;\n",
+"DEPENDS $(<) : $(>) ;\n",
+"SEARCH on $(>) = $(SEARCH_SOURCE) ;\n",
+"}\n",
+"rule HdrMacroFile\n",
+"{\n",
+"HDRMACRO $(<) ;\n",
+"}\n",
+"rule HdrRule\n",
+"{\n",
+"local s ;\n",
+"if $(HDRGRIST)\n",
+"{\n",
+"s = $(>:G=$(HDRGRIST)) ;\n",
+"} else {\n",
+"s = $(>) ;\n",
+"}\n",
+"INCLUDES $(<) : $(s) ;\n",
+"SEARCH on $(s) = $(HDRSEARCH) ;\n",
+"NOCARE $(s) ;\n",
+"HDRSEARCH on $(s) = $(HDRSEARCH) ;\n",
+"HDRSCAN on $(s) = $(HDRSCAN) ;\n",
+"HDRRULE on $(s) = $(HDRRULE) ;\n",
+"HDRGRIST on $(s) = $(HDRGRIST) ;\n",
+"}\n",
+"rule InstallInto\n",
+"{\n",
+"local i t ;\n",
+"t = $(>:G=$(INSTALLGRIST)) ;\n",
+"Depends install : $(t) ;\n",
+"Clean uninstall : $(t) ;\n",
+"SEARCH on $(>) = $(SEARCH_SOURCE) ;\n",
+"MakeLocate $(t) : $(<) ;\n",
+"for i in $(>)\n",
+"{\n",
+"local tt = $(i:G=$(INSTALLGRIST)) ;\n",
+"Depends $(tt) : $(i) ;\n",
+"Install $(tt) : $(i) ;\n",
+"Chmod $(tt) ;\n",
+"if $(OWNER) && $(CHOWN)\n",
+"{\n",
+"Chown $(tt) ;\n",
+"OWNER on $(tt) = $(OWNER) ;\n",
+"}\n",
+"if $(GROUP) && $(CHGRP)\n",
+"{\n",
+"Chgrp $(tt) ;\n",
+"GROUP on $(tt) = $(GROUP) ;\n",
+"}\n",
+"}\n",
+"}\n",
+"rule InstallBin\n",
+"{\n",
+"local _t = [ FAppendSuffix $(>) : $(SUFEXE) ] ;\n",
+"InstallInto $(<) : $(_t) ;\n",
+"MODE on $(_t:G=installed) = $(EXEMODE) ;\n",
+"}\n",
+"rule InstallFile\n",
+"{\n",
+"InstallInto $(<) : $(>) ;\n",
+"MODE on $(>:G=installed) = $(FILEMODE) ;\n",
+"}\n",
+"rule InstallLib\n",
+"{\n",
+"InstallInto $(<) : $(>) ;\n",
+"MODE on $(>:G=installed) = $(FILEMODE) ;\n",
+"}\n",
+"rule InstallMan\n",
+"{\n",
+"local i s d ;\n",
+"for i in $(>)\n",
+"{\n",
+"switch $(i:S)\n",
+"{\n",
+"case .1 : s = 1 ; case .2 : s = 2 ; case .3 : s = 3 ;\n",
+"case .4 : s = 4 ; case .5 : s = 5 ; case .6 : s = 6 ;\n",
+"case .7 : s = 7 ; case .8 : s = 8 ; case .l : s = l ;\n",
+"case .n : s = n ; case .man : s = 1 ;\n",
+"}\n",
+"d = man$(s) ;\n",
+"InstallInto $(d:R=$(<)) : $(i) ;\n",
+"}\n",
+"MODE on $(>:G=installed) = $(FILEMODE) ;\n",
+"}\n",
+"rule InstallShell\n",
+"{\n",
+"InstallInto $(<) : $(>) ;\n",
+"MODE on $(>:G=installed) = $(SHELLMODE) ;\n",
+"}\n",
+"rule Lex\n",
+"{\n",
+"LexMv $(<) : $(>) ;\n",
+"DEPENDS $(<) : $(>) ;\n",
+"MakeLocate $(<) : $(LOCATE_SOURCE) ;\n",
+"Clean clean : $(<) ;\n",
+"}\n",
+"rule Library\n",
+"{\n",
+"LibraryFromObjects $(<) : $(>:S=$(SUFOBJ)) ;\n",
+"Objects $(>) ;\n",
+"}\n",
+"rule LibraryFromObjects\n",
+"{\n",
+"local _i _l _s ;\n",
+"_s = [ FGristFiles $(>) ] ;\n",
+"_l = $(<:S=$(SUFLIB)) ;\n",
+"if $(KEEPOBJS)\n",
+"{\n",
+"DEPENDS obj : $(_s) ;\n",
+"}\n",
+"else\n",
+"{\n",
+"DEPENDS lib : $(_l) ;\n",
+"}\n",
+"if ! $(_l:D)\n",
+"{\n",
+"MakeLocate $(_l) $(_l)($(_s:BS)) : $(LOCATE_TARGET) ;\n",
+"}\n",
+"if $(NOARSCAN)\n",
+"{\n",
+"DEPENDS $(_l) : $(_s) ;\n",
+"}\n",
+"else\n",
+"{\n",
+"DEPENDS $(_l) : $(_l)($(_s:BS)) ;\n",
+"for _i in $(_s)\n",
+"{\n",
+"DEPENDS $(_l)($(_i:BS)) : $(_i) ;\n",
+"}\n",
+"}\n",
+"Clean clean : $(_l) ;\n",
+"if $(CRELIB) { CreLib $(_l) : $(_s[1]) ; }\n",
+"Archive $(_l) : $(_s) ;\n",
+"if $(RANLIB) { Ranlib $(_l) ; }\n",
+"if ! ( $(NOARSCAN) || $(KEEPOBJS) ) { RmTemps $(_l) : $(_s) ; }\n",
+"}\n",
+"rule Link\n",
+"{\n",
+"MODE on $(<) = $(EXEMODE) ;\n",
+"Chmod $(<) ;\n",
+"}\n",
+"rule LinkLibraries\n",
+"{\n",
+"local _t = [ FAppendSuffix $(<) : $(SUFEXE) ] ;\n",
+"DEPENDS $(_t) : $(>:S=$(SUFLIB)) ;\n",
+"NEEDLIBS on $(_t) += $(>:S=$(SUFLIB)) ;\n",
+"}\n",
+"rule Main\n",
+"{\n",
+"MainFromObjects $(<) : $(>:S=$(SUFOBJ)) ;\n",
+"Objects $(>) ;\n",
+"}\n",
+"rule MainFromObjects\n",
+"{\n",
+"local _s _t ;\n",
+"_s = [ FGristFiles $(>) ] ;\n",
+"_t = [ FAppendSuffix $(<) : $(SUFEXE) ] ;\n",
+"if $(_t) != $(<)\n",
+"{\n",
+"DEPENDS $(<) : $(_t) ;\n",
+"NOTFILE $(<) ;\n",
+"}\n",
+"DEPENDS exe : $(_t) ;\n",
+"DEPENDS $(_t) : $(_s) ;\n",
+"MakeLocate $(_t) : $(LOCATE_TARGET) ;\n",
+"Clean clean : $(_t) ;\n",
+"Link $(_t) : $(_s) ;\n",
+"}\n",
+"rule MakeLocate\n",
+"{\n",
+"if $(>)\n",
+"{\n",
+"LOCATE on $(<) = $(>) ;\n",
+"Depends $(<) : $(>[1]) ;\n",
+"MkDir $(>[1]) ;\n",
+"}\n",
+"}\n",
+"rule MkDir\n",
+"{\n",
+"NOUPDATE $(<) ;\n",
+"if $(<) != $(DOT) && ! $($(<)-mkdir)\n",
+"{\n",
+"local s ;\n",
+"$(<)-mkdir = true ;\n",
+"MkDir1 $(<) ;\n",
+"Depends dirs : $(<) ;\n",
+"s = $(<:P) ;\n",
+"if $(NT)\n",
+"{\n",
+"switch $(s)\n",
+"{\n",
+"case *: : s = ;\n",
+"case *:\\\\ : s = ;\n",
+"}\n",
+"}\n",
+"if $(s) && $(s) != $(<)\n",
+"{\n",
+"Depends $(<) : $(s) ;\n",
+"MkDir $(s) ;\n",
+"}\n",
+"else if $(s)\n",
+"{\n",
+"NOTFILE $(s) ;\n",
+"}\n",
+"}\n",
+"}\n",
+"rule Object\n",
+"{\n",
+"local h ;\n",
+"Clean clean : $(<) ;\n",
+"MakeLocate $(<) : $(LOCATE_TARGET) ;\n",
+"SEARCH on $(>) = $(SEARCH_SOURCE) ;\n",
+"HDRS on $(<) = $(SEARCH_SOURCE) $(HDRS) $(SUBDIRHDRS) ;\n",
+"if $(SEARCH_SOURCE)\n",
+"{\n",
+"h = $(SEARCH_SOURCE) ;\n",
+"}\n",
+"else\n",
+"{\n",
+"h = \"\" ;\n",
+"}\n",
+"HDRRULE on $(>) = HdrRule ;\n",
+"HDRSCAN on $(>) = $(HDRPATTERN) ;\n",
+"HDRSEARCH on $(>) = $(HDRS) $(SUBDIRHDRS) $(h) $(STDHDRS) ;\n",
+"HDRGRIST on $(>) = $(HDRGRIST) ;\n",
+"switch $(>:S)\n",
+"{\n",
+"case .asm : As $(<) : $(>) ;\n",
+"case .c : Cc $(<) : $(>) ;\n",
+"case .C : C++ $(<) : $(>) ;\n",
+"case .cc : C++ $(<) : $(>) ;\n",
+"case .cpp : C++ $(<) : $(>) ;\n",
+"case .f : Fortran $(<) : $(>) ;\n",
+"case .l : Cc $(<) : $(<:S=.c) ;\n",
+"Lex $(<:S=.c) : $(>) ;\n",
+"case .s : As $(<) : $(>) ;\n",
+"case .y : Cc $(<) : $(<:S=.c) ;\n",
+"Yacc $(<:S=.c) : $(>) ;\n",
+"case * : UserObject $(<) : $(>) ;\n",
+"}\n",
+"}\n",
+"rule ObjectCcFlags\n",
+"{\n",
+"CCFLAGS on [ FGristFiles $(<:S=$(SUFOBJ)) ] += $(>) ;\n",
+"}\n",
+"rule ObjectC++Flags\n",
+"{\n",
+"C++FLAGS on [ FGristFiles $(<:S=$(SUFOBJ)) ] += $(>) ;\n",
+"}\n",
+"rule ObjectHdrs\n",
+"{\n",
+"HDRS on [ FGristFiles $(<:S=$(SUFOBJ)) ] += $(>) ;\n",
+"}\n",
+"rule Objects\n",
+"{\n",
+"local _i ;\n",
+"for _i in [ FGristFiles $(<) ]\n",
+"{\n",
+"Object $(_i:S=$(SUFOBJ)) : $(_i) ;\n",
+"DEPENDS obj : $(_i:S=$(SUFOBJ)) ;\n",
+"}\n",
+"}\n",
+"rule RmTemps\n",
+"{\n",
+"TEMPORARY $(>) ;\n",
+"}\n",
+"rule Setuid\n",
+"{\n",
+"MODE on [ FAppendSuffix $(<) : $(SUFEXE) ] = 4711 ;\n",
+"}\n",
+"rule Shell\n",
+"{\n",
+"DEPENDS shell : $(<) ;\n",
+"DEPENDS $(<) : $(>) ;\n",
+"SEARCH on $(>) = $(SEARCH_SOURCE) ;\n",
+"MODE on $(<) = $(SHELLMODE) ;\n",
+"Clean clean : $(<) ;\n",
+"Chmod $(<) ;\n",
+"}\n",
+"rule SubDir\n",
+"{\n",
+"local _r _s ;\n",
+"if ! $($(<[1]))\n",
+"{\n",
+"if ! $(<[1])\n",
+"{\n",
+"EXIT SubDir syntax error ;\n",
+"}\n",
+"$(<[1]) = [ FSubDir $(<[2-]) ] ;\n",
+"}\n",
+"if ! $($(<[1])-included)\n",
+"{\n",
+"$(<[1])-included = TRUE ;\n",
+"_r = $($(<[1])RULES) ;\n",
+"if ! $(_r)\n",
+"{\n",
+"_r = $(JAMRULES:R=$($(<[1]))) ;\n",
+"}\n",
+"include $(_r) ;\n",
+"}\n",
+"_s = [ FDirName $(<[2-]) ] ;\n",
+"SUBDIR = $(_s:R=$($(<[1]))) ;\n",
+"SUBDIR_TOKENS = $(<[2-]) ;\n",
+"SEARCH_SOURCE = $(SUBDIR) ;\n",
+"LOCATE_SOURCE = $(ALL_LOCATE_TARGET) $(SUBDIR) ;\n",
+"LOCATE_TARGET = $(ALL_LOCATE_TARGET) $(SUBDIR) ;\n",
+"SOURCE_GRIST = [ FGrist $(<[2-]) ] ;\n",
+"SUBDIRCCFLAGS = ;\n",
+"SUBDIRC++FLAGS = ;\n",
+"SUBDIRHDRS = ;\n",
+"}\n",
+"rule SubDirCcFlags\n",
+"{\n",
+"SUBDIRCCFLAGS += $(<) ;\n",
+"}\n",
+"rule SubDirC++Flags\n",
+"{\n",
+"SUBDIRC++FLAGS += $(<) ;\n",
+"}\n",
+"rule SubDirHdrs\n",
+"{\n",
+"SUBDIRHDRS += $(<) ;\n",
+"}\n",
+"rule SubInclude\n",
+"{\n",
+"local _s ;\n",
+"if ! $($(<[1]))\n",
+"{\n",
+"EXIT Top level of source tree has not been set with $(<[1]) ;\n",
+"}\n",
+"_s = [ FDirName $(<[2-]) ] ;\n",
+"include $(JAMFILE:D=$(_s):R=$($(<[1]))) ;\n",
+"}\n",
+"rule Undefines\n",
+"{\n",
+"UNDEFS on [ FAppendSuffix $(<) : $(SUFEXE) ] += $(UNDEFFLAG)$(>) ;\n",
+"}\n",
+"rule UserObject\n",
+"{\n",
+"EXIT \"Unknown suffix on\" $(>) \"- see UserObject rule in Jamfile(5).\" ;\n",
+"}\n",
+"rule Yacc\n",
+"{\n",
+"local _h ;\n",
+"_h = $(<:BS=.h) ;\n",
+"MakeLocate $(<) $(_h) : $(LOCATE_SOURCE) ;\n",
+"if $(YACC)\n",
+"{\n",
+"DEPENDS $(<) $(_h) : $(>) ;\n",
+"Yacc1 $(<) $(_h) : $(>) ;\n",
+"YaccMv $(<) $(_h) : $(>) ;\n",
+"Clean clean : $(<) $(_h) ;\n",
+"}\n",
+"INCLUDES $(<) : $(_h) ;\n",
+"}\n",
+"rule FGrist\n",
+"{\n",
+"local _g _i ;\n",
+"_g = $(<[1]) ;\n",
+"for _i in $(<[2-])\n",
+"{\n",
+"_g = $(_g)!$(_i) ;\n",
+"}\n",
+"return $(_g) ;\n",
+"}\n",
+"rule FGristFiles\n",
+"{\n",
+"if ! $(SOURCE_GRIST)\n",
+"{\n",
+"return $(<) ;\n",
+"}\n",
+"else\n",
+"{\n",
+"return $(<:G=$(SOURCE_GRIST)) ;\n",
+"}\n",
+"}\n",
+"rule FGristSourceFiles\n",
+"{\n",
+"if ! $(SOURCE_GRIST)\n",
+"{\n",
+"return $(<) ;\n",
+"}\n",
+"else\n",
+"{\n",
+"local _i _o ;\n",
+"for _i in $(<)\n",
+"{\n",
+"switch $(_i)\n",
+"{\n",
+"case *.h : _o += $(_i) ;\n",
+"case * : _o += $(_i:G=$(SOURCE_GRIST)) ;\n",
+"}\n",
+"}\n",
+"return $(_o) ;\n",
+"}\n",
+"}\n",
+"rule FConcat\n",
+"{\n",
+"local _t _r ;\n",
+"$(_r) = $(<[1]) ;\n",
+"for _t in $(<[2-])\n",
+"{\n",
+"$(_r) = $(_r)$(_t) ;\n",
+"}\n",
+"return $(_r) ;\n",
+"}\n",
+"rule FSubDir\n",
+"{\n",
+"local _i _d ;\n",
+"if ! $(<[1])\n",
+"{\n",
+"_d = $(DOT) ;\n",
+"}\n",
+"else\n",
+"{\n",
+"_d = $(DOTDOT) ;\n",
+"for _i in $(<[2-])\n",
+"{\n",
+"_d = $(_d:R=$(DOTDOT)) ;\n",
+"}\n",
+"}\n",
+"return $(_d) ;\n",
+"}\n",
+"rule FDirName\n",
+"{\n",
+"local _s _i ;\n",
+"if ! $(<)\n",
+"{\n",
+"_s = $(DOT) ;\n",
+"}\n",
+"else if $(VMS)\n",
+"{\n",
+"switch $(<[1])\n",
+"{\n",
+"case *:* : _s = $(<[1]) ;\n",
+"case \\\\[*\\\\] : _s = $(<[1]) ;\n",
+"case * : _s = [.$(<[1])] ;\n",
+"}\n",
+"for _i in [.$(<[2-])]\n",
+"{\n",
+"_s = $(_i:R=$(_s)) ;\n",
+"}\n",
+"}\n",
+"else if $(MAC)\n",
+"{\n",
+"_s = $(DOT) ;\n",
+"for _i in $(<)\n",
+"{\n",
+"_s = $(_i:R=$(_s)) ;\n",
+"}\n",
+"}\n",
+"else\n",
+"{\n",
+"_s = $(<[1]) ;\n",
+"for _i in $(<[2-])\n",
+"{\n",
+"_s = $(_i:R=$(_s)) ;\n",
+"}\n",
+"}\n",
+"return $(_s) ;\n",
+"}\n",
+"rule _makeCommon\n",
+"{\n",
+"if $($(<)[1]) && $($(<)[1]) = $($(>)[1])\n",
+"{\n",
+"$(<) = $($(<)[2-]) ;\n",
+"$(>) = $($(>)[2-]) ;\n",
+"_makeCommon $(<) : $(>) ;\n",
+"}\n",
+"}\n",
+"rule FRelPath\n",
+"{\n",
+"local _l _r ;\n",
+"_l = $(<) ;\n",
+"_r = $(>) ;\n",
+"_makeCommon _l : _r ;\n",
+"_l = [ FSubDir $(_l) ] ;\n",
+"_r = [ FDirName $(_r) ] ;\n",
+"if $(_r) = $(DOT) {\n",
+"return $(_l) ;\n",
+"} else {\n",
+"return $(_r:R=$(_l)) ;\n",
+"}\n",
+"}\n",
+"rule FAppendSuffix\n",
+"{\n",
+"if $(>)\n",
+"{\n",
+"local _i _o ;\n",
+"for _i in $(<)\n",
+"{\n",
+"if $(_i:S)\n",
+"{\n",
+"_o += $(_i) ;\n",
+"}\n",
+"else\n",
+"{\n",
+"_o += $(_i:S=$(>)) ;\n",
+"}\n",
+"}\n",
+"return $(_o) ;\n",
+"}\n",
+"else\n",
+"{\n",
+"return $(<) ;\n",
+"}\n",
+"}\n",
+"rule unmakeDir\n",
+"{\n",
+"if $(>[1]:D) && $(>[1]:D) != $(>[1]) && $(>[1]:D) != \\\\\\\\\n",
+"{\n",
+"unmakeDir $(<) : $(>[1]:D) $(>[1]:BS) $(>[2-]) ;\n",
+"}\n",
+"else\n",
+"{\n",
+"$(<) = $(>) ;\n",
+"}\n",
+"}\n",
+"rule FConvertToSlashes\n",
+"{\n",
+"local _d, _s, _i ;\n",
+"unmakeDir _d : $(<) ;\n",
+"_s = $(_d[1]) ;\n",
+"for _i in $(_d[2-])\n",
+"{\n",
+"_s = $(_s)/$(_i) ;\n",
+"}\n",
+"return $(_s) ;\n",
+"}\n",
+"actions updated together piecemeal Archive\n",
+"{\n",
+"$(AR) $(<) $(>)\n",
+"}\n",
+"actions As\n",
+"{\n",
+"$(AS) $(ASFLAGS) -I$(HDRS) -o $(<) $(>)\n",
+"}\n",
+"actions C++\n",
+"{\n",
+"$(C++) -c $(C++FLAGS) $(OPTIM) -I$(HDRS) -o $(<) $(>)\n",
+"}\n",
+"actions Cc\n",
+"{\n",
+"$(CC) -c $(CCFLAGS) $(OPTIM) -I$(HDRS) -o $(<) $(>)\n",
+"}\n",
+"actions Chgrp\n",
+"{\n",
+"$(CHGRP) $(GROUP) $(<)\n",
+"}\n",
+"actions Chmod1\n",
+"{\n",
+"$(CHMOD) $(MODE) $(<)\n",
+"}\n",
+"actions Chown\n",
+"{\n",
+"$(CHOWN) $(OWNER) $(<)\n",
+"}\n",
+"actions piecemeal together existing Clean\n",
+"{\n",
+"$(RM) $(>)\n",
+"}\n",
+"actions File\n",
+"{\n",
+"$(CP) $(>) $(<)\n",
+"}\n",
+"actions GenFile1\n",
+"{\n",
+"$(>[1]) $(<) $(>[2-])\n",
+"}\n",
+"actions Fortran\n",
+"{\n",
+"$(FORTRAN) $(FORTRANFLAGS) -o $(<) $(>)\n",
+"}\n",
+"actions HardLink\n",
+"{\n",
+"$(RM) $(<) && $(LN) $(>) $(<)\n",
+"}\n",
+"actions Install\n",
+"{\n",
+"$(CP) $(>) $(<)\n",
+"}\n",
+"actions Lex\n",
+"{\n",
+"$(LEX) $(>)\n",
+"}\n",
+"actions LexMv\n",
+"{\n",
+"$(MV) lex.yy.c $(<)\n",
+"}\n",
+"actions Link bind NEEDLIBS\n",
+"{\n",
+"$(LINK) $(LINKFLAGS) -o $(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)\n",
+"}\n",
+"actions MkDir1\n",
+"{\n",
+"$(MKDIR) $(<)\n",
+"}\n",
+"actions together Ranlib\n",
+"{\n",
+"$(RANLIB) $(<)\n",
+"}\n",
+"actions quietly updated piecemeal together RmTemps\n",
+"{\n",
+"$(RM) $(>)\n",
+"}\n",
+"actions Shell\n",
+"{\n",
+"$(AWK) '\n",
+"NR == 1 { print \"$(SHELLHEADER)\" }\n",
+"NR == 1 && /^[#:]/ { next }\n",
+"/^##/ { next }\n",
+"{ print }\n",
+"' < $(>) > $(<)\n",
+"}\n",
+"actions Yacc1\n",
+"{\n",
+"$(YACC) $(YACCFLAGS) $(>)\n",
+"}\n",
+"actions YaccMv\n",
+"{\n",
+"$(MV) $(YACCFILES).c $(<[1])\n",
+"$(MV) $(YACCFILES).h $(<[2])\n",
+"}\n",
+"if $(RELOCATE)\n",
+"{\n",
+"actions C++\n",
+"{\n",
+"$(C++) -c $(C++FLAGS) $(OPTIM) -I$(HDRS) $(>)\n",
+"}\n",
+"actions Cc\n",
+"{\n",
+"$(CC) -c $(CCFLAGS) $(OPTIM) -I$(HDRS) $(>)\n",
+"}\n",
+"actions ignore CcMv\n",
+"{\n",
+"[ $(<) != $(>:BS=$(SUFOBJ)) ] && $(MV) $(>:BS=$(SUFOBJ)) $(<)\n",
+"}\n",
+"}\n",
+"if $(NOARUPDATE)\n",
+"{\n",
+"actions Archive\n",
+"{\n",
+"$(AR) $(<) $(>)\n",
+"}\n",
+"}\n",
+"if $(NT)\n",
+"{\n",
+"if $(TOOLSET) = VISUALC || $(TOOLSET) = VC7 || $(TOOLSET) = INTELC\n",
+"{\n",
+"actions updated together piecemeal Archive\n",
+"{\n",
+"if exist $(<) set _$(<:B)_=$(<)\n",
+"$(AR) /out:$(<) %_$(<:B)_% $(>)\n",
+"}\n",
+"actions As\n",
+"{\n",
+"$(AS) /Ml /p /v /w2 $(>) $(<) ,nul,nul;\n",
+"}\n",
+"actions Cc\n",
+"{\n",
+"$(CC) /c $(CCFLAGS) $(OPTIM) /Fo$(<) /I$(HDRS) /I$(STDHDRS) $(>)\n",
+"}\n",
+"actions C++\n",
+"{\n",
+"$(C++) /c $(C++FLAGS) $(OPTIM) /Fo$(<) /I$(HDRS) /I$(STDHDRS) /Tp$(>)\n",
+"}\n",
+"actions Link bind NEEDLIBS\n",
+"{\n",
+"$(LINK) $(LINKFLAGS) /out:$(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)\n",
+"}\n",
+"}\n",
+"else if $(TOOLSET) = VISUALC16\n",
+"{\n",
+"actions updated together piecemeal Archive\n",
+"{\n",
+"$(AR) $(<) -+$(>)\n",
+"}\n",
+"actions Cc\n",
+"{\n",
+"$(CC) /c $(CCFLAGS) $(OPTIM) /Fo$(<) /I$(HDRS) $(>)\n",
+"}\n",
+"actions C++\n",
+"{\n",
+"$(C++) /c $(C++FLAGS) $(OPTIM) /Fo$(<) /I$(HDRS) /Tp$(>)\n",
+"}\n",
+"actions Link bind NEEDLIBS\n",
+"{\n",
+"$(LINK) $(LINKFLAGS) /out:$(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)\n",
+"}\n",
+"}\n",
+"else if $(TOOLSET) = BORLANDC\n",
+"{\n",
+"actions updated together piecemeal Archive\n",
+"{\n",
+"$(AR) $(<) -+$(>)\n",
+"}\n",
+"actions Link bind NEEDLIBS\n",
+"{\n",
+"$(LINK) -e$(<) $(LINKFLAGS) $(UNDEFS) -L$(LINKLIBS) $(NEEDLIBS) $(>)\n",
+"}\n",
+"actions C++\n",
+"{\n",
+"$(C++) -c $(C++FLAGS) $(OPTIM) -I$(HDRS) -o$(<) $(>)\n",
+"}\n",
+"actions Cc\n",
+"{\n",
+"$(CC) -c $(CCFLAGS) $(OPTIM) -I$(HDRS) -o$(<) $(>)\n",
+"}\n",
+"}\n",
+"else if $(TOOLSET) = MINGW\n",
+"{\n",
+"actions together piecemeal Archive\n",
+"{\n",
+"$(AR) $(<) $(>:T)\n",
+"}\n",
+"actions Cc\n",
+"{\n",
+"$(CC) -c $(CCFLAGS) $(OPTIM) -I$(HDRS) -o$(<) $(>)\n",
+"}\n",
+"actions C++\n",
+"{\n",
+"$(C++) -c $(C++FLAGS) $(OPTIM) -I$(HDRS) -o$(<) $(>)\n",
+"}\n",
+"}\n",
+"else if $(TOOLSET) = WATCOM\n",
+"{\n",
+"actions together piecemeal Archive\n",
+"{\n",
+"$(AR) $(<) +-$(>)\n",
+"}\n",
+"actions Cc\n",
+"{\n",
+"$(CC) $(CCFLAGS) $(OPTIM) /Fo=$(<) /I$(HDRS) $(>)\n",
+"}\n",
+"actions C++\n",
+"{\n",
+"$(C++) $(C++FLAGS) $(OPTIM) /Fo=$(<) /I$(HDRS) $(>)\n",
+"}\n",
+"actions Link bind NEEDLIBS\n",
+"{\n",
+"$(LINK) $(LINKFLAGS) /Fe=$(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)\n",
+"}\n",
+"actions Shell\n",
+"{\n",
+"$(CP) $(>) $(<)\n",
+"}\n",
+"}\n",
+"else if $(TOOLSET) = LCC\n",
+"{\n",
+"actions together piecemeal Archive\n",
+"{\n",
+"$(AR) /out:$(<) $(>)\n",
+"}\n",
+"actions Cc\n",
+"{\n",
+"$(CC) $(CCFLAGS) $(OPTIM) -Fo$(<) -I$(HDRS) $(>)\n",
+"}\n",
+"actions Link bind NEEDLIBS\n",
+"{\n",
+"$(LINK) $(LINKFLAGS) -o $(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)\n",
+"}\n",
+"actions Shell\n",
+"{\n",
+"$(CP) $(>) $(<)\n",
+"}\n",
+"}\n",
+"}\n",
+"else if $(OS2)\n",
+"{\n",
+"if $(TOOLSET) = WATCOM\n",
+"{\n",
+"actions together piecemeal Archive\n",
+"{\n",
+"$(AR) $(<) +-$(>)\n",
+"}\n",
+"actions Cc\n",
+"{\n",
+"$(CC) $(CCFLAGS) $(OPTIM) /Fo=$(<) /I$(HDRS) $(>)\n",
+"}\n",
+"actions C++\n",
+"{\n",
+"$(C++) $(C++FLAGS) $(OPTIM) /Fo=$(<) /I$(HDRS) $(>)\n",
+"}\n",
+"actions Link bind NEEDLIBS\n",
+"{\n",
+"$(LINK) $(LINKFLAGS) /Fe=$(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)\n",
+"}\n",
+"actions Shell\n",
+"{\n",
+"$(CP) $(>) $(<)\n",
+"}\n",
+"}\n",
+"else if $(TOOLSET) = EMX\n",
+"{\n",
+"actions together piecemeal Archive\n",
+"{\n",
+"$(AR) $(<) $(>:T)\n",
+"}\n",
+"actions Cc\n",
+"{\n",
+"$(CC) -c $(CCFLAGS) $(OPTIM) -I$(HDRS) -o$(<) $(>)\n",
+"}\n",
+"actions C++\n",
+"{\n",
+"$(C++) -c $(C++FLAGS) $(OPTIM) -I$(HDRS) -o$(<) $(>)\n",
+"}\n",
+"}\n",
+"}\n",
+"else if $(VMS)\n",
+"{\n",
+"actions updated together piecemeal Archive\n",
+"{\n",
+"lib/replace $(<) $(>[1]) ,$(>[2-])\n",
+"}\n",
+"actions Cc\n",
+"{\n",
+"$(CC)/obj=$(<) $(CCFLAGS) $(OPTIM) $(SLASHINC) $(>)\n",
+"}\n",
+"actions C++\n",
+"{\n",
+"$(C++)/obj=$(<) $(C++FLAGS) $(OPTIM) $(SLASHINC) $(>)\n",
+"}\n",
+"actions piecemeal together existing Clean\n",
+"{\n",
+"$(RM) $(>[1]);* ,$(>[2-]);*\n",
+"}\n",
+"actions together quietly CreLib\n",
+"{\n",
+"if f$search(\"$(<)\") .eqs. \"\" then lib/create $(<)\n",
+"}\n",
+"actions GenFile1\n",
+"{\n",
+"mcr $(>[1]) $(<) $(>[2-])\n",
+"}\n",
+"actions Link bind NEEDLIBS\n",
+"{\n",
+"$(LINK)/exe=$(<) $(LINKFLAGS) $(>[1]) ,$(>[2-]) ,$(NEEDLIBS)/lib ,$(LINKLIBS)\n",
+"}\n",
+"actions quietly updated piecemeal together RmTemps\n",
+"{\n",
+"$(RM) $(>[1]);* ,$(>[2-]);*\n",
+"}\n",
+"actions Shell\n",
+"{\n",
+"$(CP) $(>) $(<)\n",
+"}\n",
+"}\n",
+"else if $(MAC)\n",
+"{\n",
+"actions together Archive\n",
+"{\n",
+"$(LINK) -library -o $(<) $(>)\n",
+"}\n",
+"actions Cc\n",
+"{\n",
+"set -e MWCincludes $(MACINC)\n",
+"$(CC) -o $(<) $(CCFLAGS) $(OPTIM) $(>)\n",
+"}\n",
+"actions C++\n",
+"{\n",
+"set -e MWCincludes $(MACINC)\n",
+"$(CC) -o $(<) $(C++FLAGS) $(OPTIM) $(>)\n",
+"}\n",
+"actions Link bind NEEDLIBS\n",
+"{\n",
+"$(LINK) -o $(<) $(LINKFLAGS) $(>) $(NEEDLIBS) \"$(LINKLIBS)\"\n",
+"}\n",
+"}\n",
+"rule BULK { Bulk $(<) : $(>) ; }\n",
+"rule FILE { File $(<) : $(>) ; }\n",
+"rule HDRRULE { HdrRule $(<) : $(>) ; }\n",
+"rule INSTALL { Install $(<) : $(>) ; }\n",
+"rule LIBRARY { Library $(<) : $(>) ; }\n",
+"rule LIBS { LinkLibraries $(<) : $(>) ; }\n",
+"rule LINK { Link $(<) : $(>) ; }\n",
+"rule MAIN { Main $(<) : $(>) ; }\n",
+"rule SETUID { Setuid $(<) ; }\n",
+"rule SHELL { Shell $(<) : $(>) ; }\n",
+"rule UNDEFINES { Undefines $(<) : $(>) ; }\n",
+"rule INSTALLBIN { InstallBin $(BINDIR) : $(<) ; }\n",
+"rule INSTALLLIB { InstallLib $(LIBDIR) : $(<) ; }\n",
+"rule INSTALLMAN { InstallMan $(MANDIR) : $(<) ; }\n",
+"rule addDirName { $(<) += [ FDirName $(>) ] ; }\n",
+"rule makeDirName { $(<) = [ FDirName $(>) ] ; }\n",
+"rule makeGristedName { $(<) = [ FGristSourceFiles $(>) ] ; }\n",
+"rule makeRelPath { $(<[1]) = [ FRelPath $(<[2-]) : $(>) ] ; }\n",
+"rule makeSuffixed { $(<[1]) = [ FAppendSuffix $(>) : $(<[2]) ] ; }\n",
+"{\n",
+"if $(JAMFILE) { include $(JAMFILE) ; }\n",
+"}\n",
+"}\n",
+0 };
diff --git a/jam-files/engine/jambase.h b/jam-files/engine/jambase.h
new file mode 100644
index 00000000..c05ec792
--- /dev/null
+++ b/jam-files/engine/jambase.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * jambase.h - declaration for the internal jambase
+ *
+ * The file Jambase is turned into a C array of strings in jambase.c
+ * so that it can be built in to the executable. This is the
+ * declaration for that array.
+ */
+
+extern char *jambase[];
diff --git a/jam-files/engine/jamgram.c b/jam-files/engine/jamgram.c
new file mode 100644
index 00000000..b1fa0835
--- /dev/null
+++ b/jam-files/engine/jamgram.c
@@ -0,0 +1,1830 @@
+/* A Bison parser, made by GNU Bison 1.875. */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+/* As a special exception, when this file is copied by Bison into a
+ Bison output file, you may use that output file without restriction.
+ This special exception was added by the Free Software Foundation
+ in version 1.24 of Bison. */
+
+/* Written by Richard Stallman by simplifying the original so called
+ ``semantic'' parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 0
+
+/* Using locations. */
+#define YYLSP_NEEDED 0
+
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ _BANG_t = 258,
+ _BANG_EQUALS_t = 259,
+ _AMPER_t = 260,
+ _AMPERAMPER_t = 261,
+ _LPAREN_t = 262,
+ _RPAREN_t = 263,
+ _PLUS_EQUALS_t = 264,
+ _COLON_t = 265,
+ _SEMIC_t = 266,
+ _LANGLE_t = 267,
+ _LANGLE_EQUALS_t = 268,
+ _EQUALS_t = 269,
+ _RANGLE_t = 270,
+ _RANGLE_EQUALS_t = 271,
+ _QUESTION_EQUALS_t = 272,
+ _LBRACKET_t = 273,
+ _RBRACKET_t = 274,
+ ACTIONS_t = 275,
+ BIND_t = 276,
+ CASE_t = 277,
+ CLASS_t = 278,
+ DEFAULT_t = 279,
+ ELSE_t = 280,
+ EXISTING_t = 281,
+ FOR_t = 282,
+ IF_t = 283,
+ IGNORE_t = 284,
+ IN_t = 285,
+ INCLUDE_t = 286,
+ LOCAL_t = 287,
+ MODULE_t = 288,
+ ON_t = 289,
+ PIECEMEAL_t = 290,
+ QUIETLY_t = 291,
+ RETURN_t = 292,
+ RULE_t = 293,
+ SWITCH_t = 294,
+ TOGETHER_t = 295,
+ UPDATED_t = 296,
+ WHILE_t = 297,
+ _LBRACE_t = 298,
+ _BAR_t = 299,
+ _BARBAR_t = 300,
+ _RBRACE_t = 301,
+ ARG = 302,
+ STRING = 303
+ };
+#endif
+#define _BANG_t 258
+#define _BANG_EQUALS_t 259
+#define _AMPER_t 260
+#define _AMPERAMPER_t 261
+#define _LPAREN_t 262
+#define _RPAREN_t 263
+#define _PLUS_EQUALS_t 264
+#define _COLON_t 265
+#define _SEMIC_t 266
+#define _LANGLE_t 267
+#define _LANGLE_EQUALS_t 268
+#define _EQUALS_t 269
+#define _RANGLE_t 270
+#define _RANGLE_EQUALS_t 271
+#define _QUESTION_EQUALS_t 272
+#define _LBRACKET_t 273
+#define _RBRACKET_t 274
+#define ACTIONS_t 275
+#define BIND_t 276
+#define CASE_t 277
+#define CLASS_t 278
+#define DEFAULT_t 279
+#define ELSE_t 280
+#define EXISTING_t 281
+#define FOR_t 282
+#define IF_t 283
+#define IGNORE_t 284
+#define IN_t 285
+#define INCLUDE_t 286
+#define LOCAL_t 287
+#define MODULE_t 288
+#define ON_t 289
+#define PIECEMEAL_t 290
+#define QUIETLY_t 291
+#define RETURN_t 292
+#define RULE_t 293
+#define SWITCH_t 294
+#define TOGETHER_t 295
+#define UPDATED_t 296
+#define WHILE_t 297
+#define _LBRACE_t 298
+#define _BAR_t 299
+#define _BARBAR_t 300
+#define _RBRACE_t 301
+#define ARG 302
+#define STRING 303
+
+
+
+
+/* Copy the first part of user declarations. */
+#line 96 "jamgram.y"
+
+#include "jam.h"
+
+#include "lists.h"
+#include "parse.h"
+#include "scan.h"
+#include "compile.h"
+#include "newstr.h"
+#include "rules.h"
+
+# define YYMAXDEPTH 10000 /* for OSF and other less endowed yaccs */
+
+# define F0 (LIST *(*)(PARSE *, FRAME *))0
+# define P0 (PARSE *)0
+# define S0 (char *)0
+
+# define pappend( l,r ) parse_make( compile_append,l,r,P0,S0,S0,0 )
+# define peval( c,l,r ) parse_make( compile_eval,l,r,P0,S0,S0,c )
+# define pfor( s,l,r,x ) parse_make( compile_foreach,l,r,P0,s,S0,x )
+# define pif( l,r,t ) parse_make( compile_if,l,r,t,S0,S0,0 )
+# define pincl( l ) parse_make( compile_include,l,P0,P0,S0,S0,0 )
+# define plist( s ) parse_make( compile_list,P0,P0,P0,s,S0,0 )
+# define plocal( l,r,t ) parse_make( compile_local,l,r,t,S0,S0,0 )
+# define pmodule( l,r ) parse_make( compile_module,l,r,P0,S0,S0,0 )
+# define pclass( l,r ) parse_make( compile_class,l,r,P0,S0,S0,0 )
+# define pnull() parse_make( compile_null,P0,P0,P0,S0,S0,0 )
+# define pon( l,r ) parse_make( compile_on,l,r,P0,S0,S0,0 )
+# define prule( s,p ) parse_make( compile_rule,p,P0,P0,s,S0,0 )
+# define prules( l,r ) parse_make( compile_rules,l,r,P0,S0,S0,0 )
+# define pset( l,r,a ) parse_make( compile_set,l,r,P0,S0,S0,a )
+# define pset1( l,r,t,a ) parse_make( compile_settings,l,r,t,S0,S0,a )
+# define psetc( s,p,a,l ) parse_make( compile_setcomp,p,a,P0,s,S0,l )
+# define psete( s,l,s1,f ) parse_make( compile_setexec,l,P0,P0,s,s1,f )
+# define pswitch( l,r ) parse_make( compile_switch,l,r,P0,S0,S0,0 )
+# define pwhile( l,r ) parse_make( compile_while,l,r,P0,S0,S0,0 )
+
+# define pnode( l,r ) parse_make( F0,l,r,P0,S0,S0,0 )
+# define psnode( s,l ) parse_make( F0,l,P0,P0,s,S0,0 )
+
+
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED)
+typedef int YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+/* Copy the second part of user declarations. */
+
+
+/* Line 214 of yacc.c. */
+#line 223 "y.tab.c"
+
+#if ! defined (yyoverflow) || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# if YYSTACK_USE_ALLOCA
+# define YYSTACK_ALLOC alloca
+# else
+# ifndef YYSTACK_USE_ALLOCA
+# if defined (alloca) || defined (_ALLOCA_H)
+# define YYSTACK_ALLOC alloca
+# else
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+# else
+# if defined (__STDC__) || defined (__cplusplus)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# endif
+# define YYSTACK_ALLOC malloc
+# define YYSTACK_FREE free
+# endif
+#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */
+
+
+#if (! defined (yyoverflow) \
+ && (! defined (__cplusplus) \
+ || (YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ short yyss;
+ YYSTYPE yyvs;
+ };
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (short) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ register YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (0)
+# endif
+# endif
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack, Stack, yysize); \
+ Stack = &yyptr->Stack; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (0)
+
+#endif
+
+#if defined (__STDC__) || defined (__cplusplus)
+ typedef signed char yysigned_char;
+#else
+ typedef short yysigned_char;
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 43
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 261
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 49
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 24
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 75
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 159
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 303
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const unsigned char yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
+ 45, 46, 47, 48
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const unsigned char yyprhs[] =
+{
+ 0, 0, 3, 4, 6, 8, 10, 12, 15, 21,
+ 22, 25, 27, 31, 32, 34, 35, 39, 43, 47,
+ 52, 59, 63, 72, 78, 84, 90, 96, 102, 110,
+ 116, 120, 121, 122, 132, 134, 136, 138, 141, 143,
+ 147, 151, 155, 159, 163, 167, 171, 175, 179, 183,
+ 187, 190, 194, 195, 198, 203, 205, 209, 211, 212,
+ 215, 217, 218, 223, 226, 231, 236, 237, 240, 242,
+ 244, 246, 248, 250, 252, 253
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yysigned_char yyrhs[] =
+{
+ 50, 0, -1, -1, 52, -1, 53, -1, 52, -1,
+ 57, -1, 57, 52, -1, 32, 65, 54, 11, 51,
+ -1, -1, 14, 65, -1, 53, -1, 7, 64, 8,
+ -1, -1, 32, -1, -1, 43, 51, 46, -1, 31,
+ 65, 11, -1, 47, 64, 11, -1, 67, 60, 65,
+ 11, -1, 67, 34, 65, 60, 65, 11, -1, 37,
+ 65, 11, -1, 27, 56, 47, 30, 65, 43, 51,
+ 46, -1, 39, 65, 43, 62, 46, -1, 28, 61,
+ 43, 51, 46, -1, 33, 65, 43, 51, 46, -1,
+ 23, 64, 43, 51, 46, -1, 42, 61, 43, 51,
+ 46, -1, 28, 61, 43, 51, 46, 25, 57, -1,
+ 56, 38, 47, 55, 57, -1, 34, 67, 57, -1,
+ -1, -1, 20, 70, 47, 72, 43, 58, 48, 59,
+ 46, -1, 14, -1, 9, -1, 17, -1, 24, 14,
+ -1, 67, -1, 61, 14, 61, -1, 61, 4, 61,
+ -1, 61, 12, 61, -1, 61, 13, 61, -1, 61,
+ 15, 61, -1, 61, 16, 61, -1, 61, 5, 61,
+ -1, 61, 6, 61, -1, 61, 44, 61, -1, 61,
+ 45, 61, -1, 67, 30, 65, -1, 3, 61, -1,
+ 7, 61, 8, -1, -1, 63, 62, -1, 22, 47,
+ 10, 51, -1, 65, -1, 65, 10, 64, -1, 66,
+ -1, -1, 66, 67, -1, 47, -1, -1, 18, 68,
+ 69, 19, -1, 67, 64, -1, 34, 67, 67, 64,
+ -1, 34, 67, 37, 65, -1, -1, 70, 71, -1,
+ 41, -1, 40, -1, 29, -1, 36, -1, 35, -1,
+ 26, -1, -1, 21, 65, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const unsigned short yyrline[] =
+{
+ 0, 139, 139, 141, 152, 154, 158, 160, 162, 167,
+ 170, 172, 176, 179, 182, 185, 188, 190, 192, 194,
+ 196, 198, 200, 202, 204, 206, 208, 210, 212, 214,
+ 216, 219, 221, 218, 230, 232, 234, 236, 243, 245,
+ 247, 249, 251, 253, 255, 257, 259, 261, 263, 265,
+ 267, 269, 281, 282, 286, 295, 297, 307, 312, 313,
+ 317, 319, 319, 328, 330, 332, 343, 344, 348, 350,
+ 352, 354, 356, 358, 368, 369
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE
+/* YYTNME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "_BANG_t", "_BANG_EQUALS_t", "_AMPER_t",
+ "_AMPERAMPER_t", "_LPAREN_t", "_RPAREN_t", "_PLUS_EQUALS_t", "_COLON_t",
+ "_SEMIC_t", "_LANGLE_t", "_LANGLE_EQUALS_t", "_EQUALS_t", "_RANGLE_t",
+ "_RANGLE_EQUALS_t", "_QUESTION_EQUALS_t", "_LBRACKET_t", "_RBRACKET_t",
+ "ACTIONS_t", "BIND_t", "CASE_t", "CLASS_t", "DEFAULT_t", "ELSE_t",
+ "EXISTING_t", "FOR_t", "IF_t", "IGNORE_t", "IN_t", "INCLUDE_t",
+ "LOCAL_t", "MODULE_t", "ON_t", "PIECEMEAL_t", "QUIETLY_t", "RETURN_t",
+ "RULE_t", "SWITCH_t", "TOGETHER_t", "UPDATED_t", "WHILE_t", "_LBRACE_t",
+ "_BAR_t", "_BARBAR_t", "_RBRACE_t", "ARG", "STRING", "$accept", "run",
+ "block", "rules", "null", "assign_list_opt", "arglist_opt", "local_opt",
+ "rule", "@1", "@2", "assign", "expr", "cases", "case", "lol", "list",
+ "listp", "arg", "@3", "func", "eflags", "eflag", "bindlist", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const unsigned short yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, 301, 302, 303
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const unsigned char yyr1[] =
+{
+ 0, 49, 50, 50, 51, 51, 52, 52, 52, 53,
+ 54, 54, 55, 55, 56, 56, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 58, 59, 57, 60, 60, 60, 60, 61, 61,
+ 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
+ 61, 61, 62, 62, 63, 64, 64, 65, 66, 66,
+ 67, 68, 67, 69, 69, 69, 70, 70, 71, 71,
+ 71, 71, 71, 71, 72, 72
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const unsigned char yyr2[] =
+{
+ 0, 2, 0, 1, 1, 1, 1, 2, 5, 0,
+ 2, 1, 3, 0, 1, 0, 3, 3, 3, 4,
+ 6, 3, 8, 5, 5, 5, 5, 5, 7, 5,
+ 3, 0, 0, 9, 1, 1, 1, 2, 1, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 2, 3, 0, 2, 4, 1, 3, 1, 0, 2,
+ 1, 0, 4, 2, 4, 4, 0, 2, 1, 1,
+ 1, 1, 1, 1, 0, 2
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+ STATE-NUM when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const unsigned char yydefact[] =
+{
+ 2, 61, 66, 58, 15, 0, 58, 58, 58, 0,
+ 58, 58, 0, 9, 60, 0, 3, 0, 6, 0,
+ 0, 0, 0, 55, 57, 14, 0, 0, 0, 60,
+ 0, 38, 0, 9, 0, 15, 0, 0, 0, 0,
+ 5, 4, 0, 1, 0, 7, 35, 34, 36, 0,
+ 58, 58, 0, 58, 0, 73, 70, 72, 71, 69,
+ 68, 74, 67, 9, 58, 59, 0, 50, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 9, 0, 0,
+ 58, 17, 58, 11, 0, 9, 30, 21, 52, 9,
+ 16, 18, 13, 37, 0, 0, 0, 63, 62, 58,
+ 0, 0, 56, 58, 51, 40, 45, 46, 41, 42,
+ 39, 43, 44, 0, 47, 48, 49, 10, 9, 0,
+ 0, 0, 52, 0, 58, 15, 58, 19, 58, 58,
+ 75, 31, 26, 0, 24, 8, 25, 0, 23, 53,
+ 27, 0, 29, 0, 65, 64, 0, 9, 15, 9,
+ 12, 20, 32, 0, 28, 54, 0, 22, 33
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const short yydefgoto[] =
+{
+ -1, 15, 39, 40, 41, 84, 125, 17, 18, 146,
+ 156, 51, 30, 121, 122, 22, 23, 24, 31, 20,
+ 54, 21, 62, 100
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -48
+static const short yypact[] =
+{
+ 179, -48, -48, -48, -15, 7, -48, -16, -48, 3,
+ -48, -48, 7, 179, 1, 27, -48, -9, 179, 19,
+ -3, 33, -11, 24, 3, -48, -10, 7, 7, -48,
+ 138, 9, 30, 35, 13, 205, 53, 22, 151, 20,
+ -48, -48, 56, -48, 23, -48, -48, -48, -48, 61,
+ -48, -48, 3, -48, 62, -48, -48, -48, -48, -48,
+ -48, 58, -48, 179, -48, -48, 52, -48, 164, 7,
+ 7, 7, 7, 7, 7, 7, 7, 179, 7, 7,
+ -48, -48, -48, -48, 72, 179, -48, -48, 68, 179,
+ -48, -48, 85, -48, 77, 73, 8, -48, -48, -48,
+ 50, 57, -48, -48, -48, 45, 93, 93, -48, -48,
+ 45, -48, -48, 64, 245, 245, -48, -48, 179, 66,
+ 67, 69, 68, 71, -48, 205, -48, -48, -48, -48,
+ -48, -48, -48, 70, 79, -48, -48, 109, -48, -48,
+ -48, 112, -48, 115, -48, -48, 75, 179, 205, 179,
+ -48, -48, -48, 81, -48, -48, 82, -48, -48
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const short yypgoto[] =
+{
+ -48, -48, -47, 5, 104, -48, -48, 136, -27, -48,
+ -48, 47, 60, 36, -48, -13, -4, -48, 0, -48,
+ -48, -48, -48, -48
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If zero, do what YYDEFACT says.
+ If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -59
+static const short yytable[] =
+{
+ 19, 42, 32, 33, 34, 16, 36, 37, 86, 35,
+ 27, -58, -58, 19, 28, 1, 101, 25, 19, -58,
+ 53, 1, -14, 45, 65, 1, 1, 43, 46, 44,
+ 113, 52, 63, 47, 64, 19, 48, 66, 119, 80,
+ 97, 81, 123, 49, 29, 128, 94, 95, -58, 82,
+ 29, 102, 96, 50, 29, 29, 85, 72, 73, 55,
+ 75, 76, 56, 19, 87, 88, 90, 91, 57, 58,
+ 92, 135, 38, 59, 60, 93, 116, 19, 117, 99,
+ 61, 98, 103, 118, 127, 19, 46, 67, 68, 19,
+ 120, 47, 124, 131, 48, 130, 129, 69, 142, 133,
+ 153, 49, 155, 132, 148, 72, 73, 74, 75, 76,
+ 134, 141, 136, 147, 137, 138, 145, 140, 19, 149,
+ 150, 154, 143, 152, 144, 19, 151, 157, 158, 105,
+ 106, 107, 108, 109, 110, 111, 112, 83, 114, 115,
+ 26, 126, 69, 70, 71, 0, 0, 19, 19, 19,
+ 72, 73, 74, 75, 76, 69, 70, 71, 139, 0,
+ 0, 0, 0, 72, 73, 74, 75, 76, 69, 70,
+ 71, 0, 104, 0, 0, 0, 72, 73, 74, 75,
+ 76, 77, 78, 79, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 89, 78, 79, 1, 0, 2,
+ 0, 0, 3, 0, 0, 0, 4, 5, 78, 79,
+ 6, 7, 8, 9, 0, 0, 10, -15, 11, 0,
+ 0, 12, 13, 1, 0, 2, 14, 0, 3, 0,
+ 0, 0, 4, 5, 0, 0, 6, 25, 8, 9,
+ 0, 0, 10, 0, 11, 0, 0, 12, 13, 69,
+ 70, 71, 14, 0, 0, 0, 0, 72, 73, 74,
+ 75, 76
+};
+
+static const short yycheck[] =
+{
+ 0, 14, 6, 7, 8, 0, 10, 11, 35, 9,
+ 3, 10, 11, 13, 7, 18, 63, 32, 18, 18,
+ 20, 18, 38, 18, 24, 18, 18, 0, 9, 38,
+ 77, 34, 43, 14, 10, 35, 17, 47, 85, 30,
+ 53, 11, 89, 24, 47, 37, 50, 51, 47, 14,
+ 47, 64, 52, 34, 47, 47, 43, 12, 13, 26,
+ 15, 16, 29, 63, 11, 43, 46, 11, 35, 36,
+ 47, 118, 12, 40, 41, 14, 80, 77, 82, 21,
+ 47, 19, 30, 11, 11, 85, 9, 27, 28, 89,
+ 22, 14, 7, 43, 17, 99, 96, 4, 125, 103,
+ 147, 24, 149, 46, 25, 12, 13, 14, 15, 16,
+ 46, 124, 46, 43, 47, 46, 129, 46, 118, 10,
+ 8, 148, 126, 48, 128, 125, 11, 46, 46, 69,
+ 70, 71, 72, 73, 74, 75, 76, 33, 78, 79,
+ 4, 94, 4, 5, 6, -1, -1, 147, 148, 149,
+ 12, 13, 14, 15, 16, 4, 5, 6, 122, -1,
+ -1, -1, -1, 12, 13, 14, 15, 16, 4, 5,
+ 6, -1, 8, -1, -1, -1, 12, 13, 14, 15,
+ 16, 43, 44, 45, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 43, 44, 45, 18, -1, 20,
+ -1, -1, 23, -1, -1, -1, 27, 28, 44, 45,
+ 31, 32, 33, 34, -1, -1, 37, 38, 39, -1,
+ -1, 42, 43, 18, -1, 20, 47, -1, 23, -1,
+ -1, -1, 27, 28, -1, -1, 31, 32, 33, 34,
+ -1, -1, 37, -1, 39, -1, -1, 42, 43, 4,
+ 5, 6, 47, -1, -1, -1, -1, 12, 13, 14,
+ 15, 16
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const unsigned char yystos[] =
+{
+ 0, 18, 20, 23, 27, 28, 31, 32, 33, 34,
+ 37, 39, 42, 43, 47, 50, 52, 56, 57, 67,
+ 68, 70, 64, 65, 66, 32, 56, 3, 7, 47,
+ 61, 67, 65, 65, 65, 67, 65, 65, 61, 51,
+ 52, 53, 64, 0, 38, 52, 9, 14, 17, 24,
+ 34, 60, 34, 67, 69, 26, 29, 35, 36, 40,
+ 41, 47, 71, 43, 10, 67, 47, 61, 61, 4,
+ 5, 6, 12, 13, 14, 15, 16, 43, 44, 45,
+ 30, 11, 14, 53, 54, 43, 57, 11, 43, 43,
+ 46, 11, 47, 14, 65, 65, 67, 64, 19, 21,
+ 72, 51, 64, 30, 8, 61, 61, 61, 61, 61,
+ 61, 61, 61, 51, 61, 61, 65, 65, 11, 51,
+ 22, 62, 63, 51, 7, 55, 60, 11, 37, 67,
+ 65, 43, 46, 65, 46, 51, 46, 47, 46, 62,
+ 46, 64, 57, 65, 65, 64, 58, 43, 25, 10,
+ 8, 11, 48, 51, 57, 51, 59, 46, 46
+};
+
+#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__)
+# define YYSIZE_T __SIZE_TYPE__
+#endif
+#if ! defined (YYSIZE_T) && defined (size_t)
+# define YYSIZE_T size_t
+#endif
+#if ! defined (YYSIZE_T)
+# if defined (__STDC__) || defined (__cplusplus)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# endif
+#endif
+#if ! defined (YYSIZE_T)
+# define YYSIZE_T unsigned int
+#endif
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrlab1
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+
+#define YYFAIL goto yyerrlab
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ yytoken = YYTRANSLATE (yychar); \
+ YYPOPSTACK; \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror ("syntax error: cannot back up");\
+ YYERROR; \
+ } \
+while (0)
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+/* YYLLOC_DEFAULT -- Compute the default location (before the actions
+ are run). */
+
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ Current.first_line = Rhs[1].first_line; \
+ Current.first_column = Rhs[1].first_column; \
+ Current.last_line = Rhs[N].last_line; \
+ Current.last_column = Rhs[N].last_column;
+#endif
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (0)
+
+# define YYDSYMPRINT(Args) \
+do { \
+ if (yydebug) \
+ yysymprint Args; \
+} while (0)
+
+# define YYDSYMPRINTF(Title, Token, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yysymprint (stderr, \
+ Token, Value); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (0)
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (cinluded). |
+`------------------------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_stack_print (short *bottom, short *top)
+#else
+static void
+yy_stack_print (bottom, top)
+ short *bottom;
+ short *top;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (/* Nothing. */; bottom <= top; ++bottom)
+ YYFPRINTF (stderr, " %d", *bottom);
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (0)
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_reduce_print (int yyrule)
+#else
+static void
+yy_reduce_print (yyrule)
+ int yyrule;
+#endif
+{
+ int yyi;
+ unsigned int yylineno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ",
+ yyrule - 1, yylineno);
+ /* Print the symbols being reduced, and their result. */
+ for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++)
+ YYFPRINTF (stderr, "%s ", yytname [yyrhs[yyi]]);
+ YYFPRINTF (stderr, "-> %s\n", yytname [yyr1[yyrule]]);
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (Rule); \
+} while (0)
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YYDSYMPRINT(Args)
+# define YYDSYMPRINTF(Title, Token, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#if YYMAXDEPTH == 0
+# undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined (__GLIBC__) && defined (_STRING_H)
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+static YYSIZE_T
+# if defined (__STDC__) || defined (__cplusplus)
+yystrlen (const char *yystr)
+# else
+yystrlen (yystr)
+ const char *yystr;
+# endif
+{
+ register const char *yys = yystr;
+
+ while (*yys++ != '\0')
+ continue;
+
+ return yys - yystr - 1;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE)
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+static char *
+# if defined (__STDC__) || defined (__cplusplus)
+yystpcpy (char *yydest, const char *yysrc)
+# else
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+# endif
+{
+ register char *yyd = yydest;
+ register const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+#endif /* !YYERROR_VERBOSE */
+
+
+
+#if YYDEBUG
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yysymprint (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ /* Pacify ``unused variable'' warnings. */
+ (void) yyvaluep;
+
+ if (yytype < YYNTOKENS)
+ {
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+# ifdef YYPRINT
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# endif
+ }
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+ YYFPRINTF (yyoutput, ")");
+}
+
+#endif /* ! YYDEBUG */
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yydestruct (int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yytype, yyvaluep)
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ /* Pacify ``unused variable'' warnings. */
+ (void) yyvaluep;
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes. */
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM);
+# else
+int yyparse ();
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+/* The lookahead symbol. */
+int yychar;
+
+/* The semantic value of the lookahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM)
+# else
+int yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+
+ register int yystate;
+ register int yyn;
+ int yyresult;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+ /* Lookahead token as an internal (translated) token number. */
+ int yytoken = 0;
+
+ /* Three stacks and their tools:
+ `yyss': related to states,
+ `yyvs': related to semantic values,
+ `yyls': related to locations.
+
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ short yyssa[YYINITDEPTH];
+ short *yyss = yyssa;
+ register short *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs = yyvsa;
+ register YYSTYPE *yyvsp;
+
+
+
+#define YYPOPSTACK (yyvsp--, yyssp--)
+
+ YYSIZE_T yystacksize = YYINITDEPTH;
+
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+
+ /* When reducing, the number of symbols on the RHS of the reduced
+ rule. */
+ int yylen;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. so pushing a state here evens the stacks.
+ */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ short *yyss1 = yyss;
+
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow ("parser stack overflow",
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyoverflowlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyoverflowlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ short *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyoverflowlab;
+ YYSTACK_RELOCATE (yyss);
+ YYSTACK_RELOCATE (yyvs);
+
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+/* Do appropriate processing given the current state. */
+/* Read a lookahead token if we need one and don't already have one. */
+/* yyresume: */
+
+ /* First try to decide what to do without reference to lookahead token. */
+
+ yyn = yypact[yystate];
+ if (yyn == YYPACT_NINF)
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YYDSYMPRINTF ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yyn == 0 || yyn == YYTABLE_NINF)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Shift the lookahead token. */
+ YYDPRINTF ((stderr, "Shifting token %s, ", yytname[yytoken]));
+
+ /* Discard the token being shifted unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ *++yyvsp = yylval;
+
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 3:
+#line 142 "jamgram.y"
+ { parse_save( yyvsp[0].parse ); }
+ break;
+
+ case 4:
+#line 153 "jamgram.y"
+ { yyval.parse = yyvsp[0].parse; }
+ break;
+
+ case 5:
+#line 155 "jamgram.y"
+ { yyval.parse = yyvsp[0].parse; }
+ break;
+
+ case 6:
+#line 159 "jamgram.y"
+ { yyval.parse = yyvsp[0].parse; }
+ break;
+
+ case 7:
+#line 161 "jamgram.y"
+ { yyval.parse = prules( yyvsp[-1].parse, yyvsp[0].parse ); }
+ break;
+
+ case 8:
+#line 163 "jamgram.y"
+ { yyval.parse = plocal( yyvsp[-3].parse, yyvsp[-2].parse, yyvsp[0].parse ); }
+ break;
+
+ case 9:
+#line 167 "jamgram.y"
+ { yyval.parse = pnull(); }
+ break;
+
+ case 10:
+#line 171 "jamgram.y"
+ { yyval.parse = yyvsp[0].parse; yyval.number = ASSIGN_SET; }
+ break;
+
+ case 11:
+#line 173 "jamgram.y"
+ { yyval.parse = yyvsp[0].parse; yyval.number = ASSIGN_APPEND; }
+ break;
+
+ case 12:
+#line 177 "jamgram.y"
+ { yyval.parse = yyvsp[-1].parse; }
+ break;
+
+ case 13:
+#line 179 "jamgram.y"
+ { yyval.parse = P0; }
+ break;
+
+ case 14:
+#line 183 "jamgram.y"
+ { yyval.number = 1; }
+ break;
+
+ case 15:
+#line 185 "jamgram.y"
+ { yyval.number = 0; }
+ break;
+
+ case 16:
+#line 189 "jamgram.y"
+ { yyval.parse = yyvsp[-1].parse; }
+ break;
+
+ case 17:
+#line 191 "jamgram.y"
+ { yyval.parse = pincl( yyvsp[-1].parse ); }
+ break;
+
+ case 18:
+#line 193 "jamgram.y"
+ { yyval.parse = prule( yyvsp[-2].string, yyvsp[-1].parse ); }
+ break;
+
+ case 19:
+#line 195 "jamgram.y"
+ { yyval.parse = pset( yyvsp[-3].parse, yyvsp[-1].parse, yyvsp[-2].number ); }
+ break;
+
+ case 20:
+#line 197 "jamgram.y"
+ { yyval.parse = pset1( yyvsp[-5].parse, yyvsp[-3].parse, yyvsp[-1].parse, yyvsp[-2].number ); }
+ break;
+
+ case 21:
+#line 199 "jamgram.y"
+ { yyval.parse = yyvsp[-1].parse; }
+ break;
+
+ case 22:
+#line 201 "jamgram.y"
+ { yyval.parse = pfor( yyvsp[-5].string, yyvsp[-3].parse, yyvsp[-1].parse, yyvsp[-6].number ); }
+ break;
+
+ case 23:
+#line 203 "jamgram.y"
+ { yyval.parse = pswitch( yyvsp[-3].parse, yyvsp[-1].parse ); }
+ break;
+
+ case 24:
+#line 205 "jamgram.y"
+ { yyval.parse = pif( yyvsp[-3].parse, yyvsp[-1].parse, pnull() ); }
+ break;
+
+ case 25:
+#line 207 "jamgram.y"
+ { yyval.parse = pmodule( yyvsp[-3].parse, yyvsp[-1].parse ); }
+ break;
+
+ case 26:
+#line 209 "jamgram.y"
+ { yyval.parse = pclass( yyvsp[-3].parse, yyvsp[-1].parse ); }
+ break;
+
+ case 27:
+#line 211 "jamgram.y"
+ { yyval.parse = pwhile( yyvsp[-3].parse, yyvsp[-1].parse ); }
+ break;
+
+ case 28:
+#line 213 "jamgram.y"
+ { yyval.parse = pif( yyvsp[-5].parse, yyvsp[-3].parse, yyvsp[0].parse ); }
+ break;
+
+ case 29:
+#line 215 "jamgram.y"
+ { yyval.parse = psetc( yyvsp[-2].string, yyvsp[0].parse, yyvsp[-1].parse, yyvsp[-4].number ); }
+ break;
+
+ case 30:
+#line 217 "jamgram.y"
+ { yyval.parse = pon( yyvsp[-1].parse, yyvsp[0].parse ); }
+ break;
+
+ case 31:
+#line 219 "jamgram.y"
+ { yymode( SCAN_STRING ); }
+ break;
+
+ case 32:
+#line 221 "jamgram.y"
+ { yymode( SCAN_NORMAL ); }
+ break;
+
+ case 33:
+#line 223 "jamgram.y"
+ { yyval.parse = psete( yyvsp[-6].string,yyvsp[-5].parse,yyvsp[-2].string,yyvsp[-7].number ); }
+ break;
+
+ case 34:
+#line 231 "jamgram.y"
+ { yyval.number = ASSIGN_SET; }
+ break;
+
+ case 35:
+#line 233 "jamgram.y"
+ { yyval.number = ASSIGN_APPEND; }
+ break;
+
+ case 36:
+#line 235 "jamgram.y"
+ { yyval.number = ASSIGN_DEFAULT; }
+ break;
+
+ case 37:
+#line 237 "jamgram.y"
+ { yyval.number = ASSIGN_DEFAULT; }
+ break;
+
+ case 38:
+#line 244 "jamgram.y"
+ { yyval.parse = peval( EXPR_EXISTS, yyvsp[0].parse, pnull() ); }
+ break;
+
+ case 39:
+#line 246 "jamgram.y"
+ { yyval.parse = peval( EXPR_EQUALS, yyvsp[-2].parse, yyvsp[0].parse ); }
+ break;
+
+ case 40:
+#line 248 "jamgram.y"
+ { yyval.parse = peval( EXPR_NOTEQ, yyvsp[-2].parse, yyvsp[0].parse ); }
+ break;
+
+ case 41:
+#line 250 "jamgram.y"
+ { yyval.parse = peval( EXPR_LESS, yyvsp[-2].parse, yyvsp[0].parse ); }
+ break;
+
+ case 42:
+#line 252 "jamgram.y"
+ { yyval.parse = peval( EXPR_LESSEQ, yyvsp[-2].parse, yyvsp[0].parse ); }
+ break;
+
+ case 43:
+#line 254 "jamgram.y"
+ { yyval.parse = peval( EXPR_MORE, yyvsp[-2].parse, yyvsp[0].parse ); }
+ break;
+
+ case 44:
+#line 256 "jamgram.y"
+ { yyval.parse = peval( EXPR_MOREEQ, yyvsp[-2].parse, yyvsp[0].parse ); }
+ break;
+
+ case 45:
+#line 258 "jamgram.y"
+ { yyval.parse = peval( EXPR_AND, yyvsp[-2].parse, yyvsp[0].parse ); }
+ break;
+
+ case 46:
+#line 260 "jamgram.y"
+ { yyval.parse = peval( EXPR_AND, yyvsp[-2].parse, yyvsp[0].parse ); }
+ break;
+
+ case 47:
+#line 262 "jamgram.y"
+ { yyval.parse = peval( EXPR_OR, yyvsp[-2].parse, yyvsp[0].parse ); }
+ break;
+
+ case 48:
+#line 264 "jamgram.y"
+ { yyval.parse = peval( EXPR_OR, yyvsp[-2].parse, yyvsp[0].parse ); }
+ break;
+
+ case 49:
+#line 266 "jamgram.y"
+ { yyval.parse = peval( EXPR_IN, yyvsp[-2].parse, yyvsp[0].parse ); }
+ break;
+
+ case 50:
+#line 268 "jamgram.y"
+ { yyval.parse = peval( EXPR_NOT, yyvsp[0].parse, pnull() ); }
+ break;
+
+ case 51:
+#line 270 "jamgram.y"
+ { yyval.parse = yyvsp[-1].parse; }
+ break;
+
+ case 52:
+#line 281 "jamgram.y"
+ { yyval.parse = P0; }
+ break;
+
+ case 53:
+#line 283 "jamgram.y"
+ { yyval.parse = pnode( yyvsp[-1].parse, yyvsp[0].parse ); }
+ break;
+
+ case 54:
+#line 287 "jamgram.y"
+ { yyval.parse = psnode( yyvsp[-2].string, yyvsp[0].parse ); }
+ break;
+
+ case 55:
+#line 296 "jamgram.y"
+ { yyval.parse = pnode( P0, yyvsp[0].parse ); }
+ break;
+
+ case 56:
+#line 298 "jamgram.y"
+ { yyval.parse = pnode( yyvsp[0].parse, yyvsp[-2].parse ); }
+ break;
+
+ case 57:
+#line 308 "jamgram.y"
+ { yyval.parse = yyvsp[0].parse; yymode( SCAN_NORMAL ); }
+ break;
+
+ case 58:
+#line 312 "jamgram.y"
+ { yyval.parse = pnull(); yymode( SCAN_PUNCT ); }
+ break;
+
+ case 59:
+#line 314 "jamgram.y"
+ { yyval.parse = pappend( yyvsp[-1].parse, yyvsp[0].parse ); }
+ break;
+
+ case 60:
+#line 318 "jamgram.y"
+ { yyval.parse = plist( yyvsp[0].string ); }
+ break;
+
+ case 61:
+#line 319 "jamgram.y"
+ { yymode( SCAN_NORMAL ); }
+ break;
+
+ case 62:
+#line 320 "jamgram.y"
+ { yyval.parse = yyvsp[-1].parse; }
+ break;
+
+ case 63:
+#line 329 "jamgram.y"
+ { yyval.parse = prule( yyvsp[-1].string, yyvsp[0].parse ); }
+ break;
+
+ case 64:
+#line 331 "jamgram.y"
+ { yyval.parse = pon( yyvsp[-2].parse, prule( yyvsp[-1].string, yyvsp[0].parse ) ); }
+ break;
+
+ case 65:
+#line 333 "jamgram.y"
+ { yyval.parse = pon( yyvsp[-2].parse, yyvsp[0].parse ); }
+ break;
+
+ case 66:
+#line 343 "jamgram.y"
+ { yyval.number = 0; }
+ break;
+
+ case 67:
+#line 345 "jamgram.y"
+ { yyval.number = yyvsp[-1].number | yyvsp[0].number; }
+ break;
+
+ case 68:
+#line 349 "jamgram.y"
+ { yyval.number = EXEC_UPDATED; }
+ break;
+
+ case 69:
+#line 351 "jamgram.y"
+ { yyval.number = EXEC_TOGETHER; }
+ break;
+
+ case 70:
+#line 353 "jamgram.y"
+ { yyval.number = EXEC_IGNORE; }
+ break;
+
+ case 71:
+#line 355 "jamgram.y"
+ { yyval.number = EXEC_QUIETLY; }
+ break;
+
+ case 72:
+#line 357 "jamgram.y"
+ { yyval.number = EXEC_PIECEMEAL; }
+ break;
+
+ case 73:
+#line 359 "jamgram.y"
+ { yyval.number = EXEC_EXISTING; }
+ break;
+
+ case 74:
+#line 368 "jamgram.y"
+ { yyval.parse = pnull(); }
+ break;
+
+ case 75:
+#line 370 "jamgram.y"
+ { yyval.parse = yyvsp[0].parse; }
+ break;
+
+
+ }
+
+/* Line 991 of yacc.c. */
+#line 1621 "y.tab.c"
+
+ yyvsp -= yylen;
+ yyssp -= yylen;
+
+
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if YYERROR_VERBOSE
+ yyn = yypact[yystate];
+
+ if (YYPACT_NINF < yyn && yyn < YYLAST)
+ {
+ YYSIZE_T yysize = 0;
+ int yytype = YYTRANSLATE (yychar);
+ char *yymsg;
+ int yyx, yycount;
+
+ yycount = 0;
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. */
+ for (yyx = yyn < 0 ? -yyn : 0;
+ yyx < (int) (sizeof (yytname) / sizeof (char *)); yyx++)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ yysize += yystrlen (yytname[yyx]) + 15, yycount++;
+ yysize += yystrlen ("syntax error, unexpected ") + 1;
+ yysize += yystrlen (yytname[yytype]);
+ yymsg = (char *) YYSTACK_ALLOC (yysize);
+ if (yymsg != 0)
+ {
+ char *yyp = yystpcpy (yymsg, "syntax error, unexpected ");
+ yyp = yystpcpy (yyp, yytname[yytype]);
+
+ if (yycount < 5)
+ {
+ yycount = 0;
+ for (yyx = yyn < 0 ? -yyn : 0;
+ yyx < (int) (sizeof (yytname) / sizeof (char *));
+ yyx++)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ const char *yyq = ! yycount ? ", expecting " : " or ";
+ yyp = yystpcpy (yyp, yyq);
+ yyp = yystpcpy (yyp, yytname[yyx]);
+ yycount++;
+ }
+ }
+ yyerror (yymsg);
+ YYSTACK_FREE (yymsg);
+ }
+ else
+ yyerror ("syntax error; also virtual memory exhausted");
+ }
+ else
+#endif /* YYERROR_VERBOSE */
+ yyerror ("syntax error");
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse lookahead token after an
+ error, discard it. */
+
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ {
+ /* Pop the error token. */
+ YYPOPSTACK;
+ /* Pop the rest of the stack. */
+ while (yyss < yyssp)
+ {
+ YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp);
+ yydestruct (yystos[*yyssp], yyvsp);
+ YYPOPSTACK;
+ }
+ YYABORT;
+ }
+
+ YYDSYMPRINTF ("Error: discarding", yytoken, &yylval, &yylloc);
+ yydestruct (yytoken, &yylval);
+ yychar = YYEMPTY;
+
+ }
+
+ /* Else will try to reuse lookahead token after shifting the error
+ token. */
+ goto yyerrlab2;
+
+
+/*----------------------------------------------------.
+| yyerrlab1 -- error raised explicitly by an action. |
+`----------------------------------------------------*/
+yyerrlab1:
+
+ /* Suppress GCC warning that yyerrlab1 is unused when no action
+ invokes YYERROR. */
+#if defined (__GNUC_MINOR__) && 2093 <= (__GNUC__ * 1000 + __GNUC_MINOR__)
+ __attribute__ ((__unused__))
+#endif
+
+
+ goto yyerrlab2;
+
+
+/*---------------------------------------------------------------.
+| yyerrlab2 -- pop states until the error token can be shifted. |
+`---------------------------------------------------------------*/
+yyerrlab2:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (yyn != YYPACT_NINF)
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+ YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp);
+ yydestruct (yystos[yystate], yyvsp);
+ yyvsp--;
+ yystate = *--yyssp;
+
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ YYDPRINTF ((stderr, "Shifting error token, "));
+
+ *++yyvsp = yylval;
+
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#ifndef yyoverflow
+/*----------------------------------------------.
+| yyoverflowlab -- parser overflow comes here. |
+`----------------------------------------------*/
+yyoverflowlab:
+ yyerror ("parser stack overflow");
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+ return yyresult;
+}
+
+
+
diff --git a/jam-files/engine/jamgram.h b/jam-files/engine/jamgram.h
new file mode 100644
index 00000000..3cb76564
--- /dev/null
+++ b/jam-files/engine/jamgram.h
@@ -0,0 +1,140 @@
+/* A Bison parser, made by GNU Bison 1.875. */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+/* As a special exception, when this file is copied by Bison into a
+ Bison output file, you may use that output file without restriction.
+ This special exception was added by the Free Software Foundation
+ in version 1.24 of Bison. */
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ _BANG_t = 258,
+ _BANG_EQUALS_t = 259,
+ _AMPER_t = 260,
+ _AMPERAMPER_t = 261,
+ _LPAREN_t = 262,
+ _RPAREN_t = 263,
+ _PLUS_EQUALS_t = 264,
+ _COLON_t = 265,
+ _SEMIC_t = 266,
+ _LANGLE_t = 267,
+ _LANGLE_EQUALS_t = 268,
+ _EQUALS_t = 269,
+ _RANGLE_t = 270,
+ _RANGLE_EQUALS_t = 271,
+ _QUESTION_EQUALS_t = 272,
+ _LBRACKET_t = 273,
+ _RBRACKET_t = 274,
+ ACTIONS_t = 275,
+ BIND_t = 276,
+ CASE_t = 277,
+ CLASS_t = 278,
+ DEFAULT_t = 279,
+ ELSE_t = 280,
+ EXISTING_t = 281,
+ FOR_t = 282,
+ IF_t = 283,
+ IGNORE_t = 284,
+ IN_t = 285,
+ INCLUDE_t = 286,
+ LOCAL_t = 287,
+ MODULE_t = 288,
+ ON_t = 289,
+ PIECEMEAL_t = 290,
+ QUIETLY_t = 291,
+ RETURN_t = 292,
+ RULE_t = 293,
+ SWITCH_t = 294,
+ TOGETHER_t = 295,
+ UPDATED_t = 296,
+ WHILE_t = 297,
+ _LBRACE_t = 298,
+ _BAR_t = 299,
+ _BARBAR_t = 300,
+ _RBRACE_t = 301,
+ ARG = 302,
+ STRING = 303
+ };
+#endif
+#define _BANG_t 258
+#define _BANG_EQUALS_t 259
+#define _AMPER_t 260
+#define _AMPERAMPER_t 261
+#define _LPAREN_t 262
+#define _RPAREN_t 263
+#define _PLUS_EQUALS_t 264
+#define _COLON_t 265
+#define _SEMIC_t 266
+#define _LANGLE_t 267
+#define _LANGLE_EQUALS_t 268
+#define _EQUALS_t 269
+#define _RANGLE_t 270
+#define _RANGLE_EQUALS_t 271
+#define _QUESTION_EQUALS_t 272
+#define _LBRACKET_t 273
+#define _RBRACKET_t 274
+#define ACTIONS_t 275
+#define BIND_t 276
+#define CASE_t 277
+#define CLASS_t 278
+#define DEFAULT_t 279
+#define ELSE_t 280
+#define EXISTING_t 281
+#define FOR_t 282
+#define IF_t 283
+#define IGNORE_t 284
+#define IN_t 285
+#define INCLUDE_t 286
+#define LOCAL_t 287
+#define MODULE_t 288
+#define ON_t 289
+#define PIECEMEAL_t 290
+#define QUIETLY_t 291
+#define RETURN_t 292
+#define RULE_t 293
+#define SWITCH_t 294
+#define TOGETHER_t 295
+#define UPDATED_t 296
+#define WHILE_t 297
+#define _LBRACE_t 298
+#define _BAR_t 299
+#define _BARBAR_t 300
+#define _RBRACE_t 301
+#define ARG 302
+#define STRING 303
+
+
+
+
+#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED)
+typedef int YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+extern YYSTYPE yylval;
+
+
+
diff --git a/jam-files/engine/jamgram.y b/jam-files/engine/jamgram.y
new file mode 100644
index 00000000..c26b1e1b
--- /dev/null
+++ b/jam-files/engine/jamgram.y
@@ -0,0 +1,371 @@
+%token _BANG_t
+%token _BANG_EQUALS_t
+%token _AMPER_t
+%token _AMPERAMPER_t
+%token _LPAREN_t
+%token _RPAREN_t
+%token _PLUS_EQUALS_t
+%token _COLON_t
+%token _SEMIC_t
+%token _LANGLE_t
+%token _LANGLE_EQUALS_t
+%token _EQUALS_t
+%token _RANGLE_t
+%token _RANGLE_EQUALS_t
+%token _QUESTION_EQUALS_t
+%token _LBRACKET_t
+%token _RBRACKET_t
+%token ACTIONS_t
+%token BIND_t
+%token CASE_t
+%token CLASS_t
+%token DEFAULT_t
+%token ELSE_t
+%token EXISTING_t
+%token FOR_t
+%token IF_t
+%token IGNORE_t
+%token IN_t
+%token INCLUDE_t
+%token LOCAL_t
+%token MODULE_t
+%token ON_t
+%token PIECEMEAL_t
+%token QUIETLY_t
+%token RETURN_t
+%token RULE_t
+%token SWITCH_t
+%token TOGETHER_t
+%token UPDATED_t
+%token WHILE_t
+%token _LBRACE_t
+%token _BAR_t
+%token _BARBAR_t
+%token _RBRACE_t
+/*
+ * Copyright 1993, 2000 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/* This file is ALSO:
+ * Copyright 2001-2004 David Abrahams.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+/*
+ * jamgram.yy - jam grammar
+ *
+ * 04/13/94 (seiwald) - added shorthand L0 for null list pointer
+ * 06/01/94 (seiwald) - new 'actions existing' does existing sources
+ * 08/23/94 (seiwald) - Support for '+=' (append to variable)
+ * 08/31/94 (seiwald) - Allow ?= as alias for "default =".
+ * 09/15/94 (seiwald) - if conditionals take only single arguments, so
+ * that 'if foo == bar' gives syntax error (use =).
+ * 02/11/95 (seiwald) - when scanning arguments to rules, only treat
+ * punctuation keywords as keywords. All arg lists
+ * are terminated with punctuation keywords.
+ *
+ * 09/11/00 (seiwald) - Support for function calls:
+ *
+ * Rules now return lists (LIST *), rather than void.
+ *
+ * New "[ rule ]" syntax evals rule into a LIST.
+ *
+ * Lists are now generated by compile_list() and
+ * compile_append(), and any other rule that indirectly
+ * makes a list, rather than being built directly here,
+ * so that lists values can contain rule evaluations.
+ *
+ * New 'return' rule sets the return value, though
+ * other statements also may have return values.
+ *
+ * 'run' production split from 'block' production so
+ * that empty blocks can be handled separately.
+ */
+
+%token ARG STRING
+
+%left _BARBAR_t _BAR_t
+%left _AMPERAMPER_t _AMPER_t
+%left _EQUALS_t _BANG_EQUALS_t IN_t
+%left _LANGLE_t _LANGLE_EQUALS_t _RANGLE_t _RANGLE_EQUALS_t
+%left _BANG_t
+
+%{
+#include "jam.h"
+
+#include "lists.h"
+#include "parse.h"
+#include "scan.h"
+#include "compile.h"
+#include "newstr.h"
+#include "rules.h"
+
+# define YYMAXDEPTH 10000 /* for OSF and other less endowed yaccs */
+
+# define F0 (LIST *(*)(PARSE *, FRAME *))0
+# define P0 (PARSE *)0
+# define S0 (char *)0
+
+# define pappend( l,r ) parse_make( compile_append,l,r,P0,S0,S0,0 )
+# define peval( c,l,r ) parse_make( compile_eval,l,r,P0,S0,S0,c )
+# define pfor( s,l,r,x ) parse_make( compile_foreach,l,r,P0,s,S0,x )
+# define pif( l,r,t ) parse_make( compile_if,l,r,t,S0,S0,0 )
+# define pincl( l ) parse_make( compile_include,l,P0,P0,S0,S0,0 )
+# define plist( s ) parse_make( compile_list,P0,P0,P0,s,S0,0 )
+# define plocal( l,r,t ) parse_make( compile_local,l,r,t,S0,S0,0 )
+# define pmodule( l,r ) parse_make( compile_module,l,r,P0,S0,S0,0 )
+# define pclass( l,r ) parse_make( compile_class,l,r,P0,S0,S0,0 )
+# define pnull() parse_make( compile_null,P0,P0,P0,S0,S0,0 )
+# define pon( l,r ) parse_make( compile_on,l,r,P0,S0,S0,0 )
+# define prule( s,p ) parse_make( compile_rule,p,P0,P0,s,S0,0 )
+# define prules( l,r ) parse_make( compile_rules,l,r,P0,S0,S0,0 )
+# define pset( l,r,a ) parse_make( compile_set,l,r,P0,S0,S0,a )
+# define pset1( l,r,t,a ) parse_make( compile_settings,l,r,t,S0,S0,a )
+# define psetc( s,p,a,l ) parse_make( compile_setcomp,p,a,P0,s,S0,l )
+# define psete( s,l,s1,f ) parse_make( compile_setexec,l,P0,P0,s,s1,f )
+# define pswitch( l,r ) parse_make( compile_switch,l,r,P0,S0,S0,0 )
+# define pwhile( l,r ) parse_make( compile_while,l,r,P0,S0,S0,0 )
+
+# define pnode( l,r ) parse_make( F0,l,r,P0,S0,S0,0 )
+# define psnode( s,l ) parse_make( F0,l,P0,P0,s,S0,0 )
+
+%}
+
+%%
+
+run : /* empty */
+ /* do nothing */
+ | rules
+ { parse_save( $1.parse ); }
+ ;
+
+/*
+ * block - zero or more rules
+ * rules - one or more rules
+ * rule - any one of jam's rules
+ * right-recursive so rules execute in order.
+ */
+
+block : null
+ { $$.parse = $1.parse; }
+ | rules
+ { $$.parse = $1.parse; }
+ ;
+
+rules : rule
+ { $$.parse = $1.parse; }
+ | rule rules
+ { $$.parse = prules( $1.parse, $2.parse ); }
+ | LOCAL_t list assign_list_opt _SEMIC_t block
+ { $$.parse = plocal( $2.parse, $3.parse, $5.parse ); }
+ ;
+
+null : /* empty */
+ { $$.parse = pnull(); }
+ ;
+
+assign_list_opt : _EQUALS_t list
+ { $$.parse = $2.parse; $$.number = ASSIGN_SET; }
+ | null
+ { $$.parse = $1.parse; $$.number = ASSIGN_APPEND; }
+ ;
+
+arglist_opt : _LPAREN_t lol _RPAREN_t
+ { $$.parse = $2.parse; }
+ |
+ { $$.parse = P0; }
+ ;
+
+local_opt : LOCAL_t
+ { $$.number = 1; }
+ | /* empty */
+ { $$.number = 0; }
+ ;
+
+rule : _LBRACE_t block _RBRACE_t
+ { $$.parse = $2.parse; }
+ | INCLUDE_t list _SEMIC_t
+ { $$.parse = pincl( $2.parse ); }
+ | ARG lol _SEMIC_t
+ { $$.parse = prule( $1.string, $2.parse ); }
+ | arg assign list _SEMIC_t
+ { $$.parse = pset( $1.parse, $3.parse, $2.number ); }
+ | arg ON_t list assign list _SEMIC_t
+ { $$.parse = pset1( $1.parse, $3.parse, $5.parse, $4.number ); }
+ | RETURN_t list _SEMIC_t
+ { $$.parse = $2.parse; }
+ | FOR_t local_opt ARG IN_t list _LBRACE_t block _RBRACE_t
+ { $$.parse = pfor( $3.string, $5.parse, $7.parse, $2.number ); }
+ | SWITCH_t list _LBRACE_t cases _RBRACE_t
+ { $$.parse = pswitch( $2.parse, $4.parse ); }
+ | IF_t expr _LBRACE_t block _RBRACE_t
+ { $$.parse = pif( $2.parse, $4.parse, pnull() ); }
+ | MODULE_t list _LBRACE_t block _RBRACE_t
+ { $$.parse = pmodule( $2.parse, $4.parse ); }
+ | CLASS_t lol _LBRACE_t block _RBRACE_t
+ { $$.parse = pclass( $2.parse, $4.parse ); }
+ | WHILE_t expr _LBRACE_t block _RBRACE_t
+ { $$.parse = pwhile( $2.parse, $4.parse ); }
+ | IF_t expr _LBRACE_t block _RBRACE_t ELSE_t rule
+ { $$.parse = pif( $2.parse, $4.parse, $7.parse ); }
+ | local_opt RULE_t ARG arglist_opt rule
+ { $$.parse = psetc( $3.string, $5.parse, $4.parse, $1.number ); }
+ | ON_t arg rule
+ { $$.parse = pon( $2.parse, $3.parse ); }
+ | ACTIONS_t eflags ARG bindlist _LBRACE_t
+ { yymode( SCAN_STRING ); }
+ STRING
+ { yymode( SCAN_NORMAL ); }
+ _RBRACE_t
+ { $$.parse = psete( $3.string,$4.parse,$7.string,$2.number ); }
+ ;
+
+/*
+ * assign - = or +=
+ */
+
+assign : _EQUALS_t
+ { $$.number = ASSIGN_SET; }
+ | _PLUS_EQUALS_t
+ { $$.number = ASSIGN_APPEND; }
+ | _QUESTION_EQUALS_t
+ { $$.number = ASSIGN_DEFAULT; }
+ | DEFAULT_t _EQUALS_t
+ { $$.number = ASSIGN_DEFAULT; }
+ ;
+
+/*
+ * expr - an expression for if
+ */
+expr : arg
+ { $$.parse = peval( EXPR_EXISTS, $1.parse, pnull() ); }
+ | expr _EQUALS_t expr
+ { $$.parse = peval( EXPR_EQUALS, $1.parse, $3.parse ); }
+ | expr _BANG_EQUALS_t expr
+ { $$.parse = peval( EXPR_NOTEQ, $1.parse, $3.parse ); }
+ | expr _LANGLE_t expr
+ { $$.parse = peval( EXPR_LESS, $1.parse, $3.parse ); }
+ | expr _LANGLE_EQUALS_t expr
+ { $$.parse = peval( EXPR_LESSEQ, $1.parse, $3.parse ); }
+ | expr _RANGLE_t expr
+ { $$.parse = peval( EXPR_MORE, $1.parse, $3.parse ); }
+ | expr _RANGLE_EQUALS_t expr
+ { $$.parse = peval( EXPR_MOREEQ, $1.parse, $3.parse ); }
+ | expr _AMPER_t expr
+ { $$.parse = peval( EXPR_AND, $1.parse, $3.parse ); }
+ | expr _AMPERAMPER_t expr
+ { $$.parse = peval( EXPR_AND, $1.parse, $3.parse ); }
+ | expr _BAR_t expr
+ { $$.parse = peval( EXPR_OR, $1.parse, $3.parse ); }
+ | expr _BARBAR_t expr
+ { $$.parse = peval( EXPR_OR, $1.parse, $3.parse ); }
+ | arg IN_t list
+ { $$.parse = peval( EXPR_IN, $1.parse, $3.parse ); }
+ | _BANG_t expr
+ { $$.parse = peval( EXPR_NOT, $2.parse, pnull() ); }
+ | _LPAREN_t expr _RPAREN_t
+ { $$.parse = $2.parse; }
+ ;
+
+
+/*
+ * cases - action elements inside a 'switch'
+ * case - a single action element inside a 'switch'
+ * right-recursive rule so cases can be examined in order.
+ */
+
+cases : /* empty */
+ { $$.parse = P0; }
+ | case cases
+ { $$.parse = pnode( $1.parse, $2.parse ); }
+ ;
+
+case : CASE_t ARG _COLON_t block
+ { $$.parse = psnode( $2.string, $4.parse ); }
+ ;
+
+/*
+ * lol - list of lists
+ * right-recursive rule so that lists can be added in order.
+ */
+
+lol : list
+ { $$.parse = pnode( P0, $1.parse ); }
+ | list _COLON_t lol
+ { $$.parse = pnode( $3.parse, $1.parse ); }
+ ;
+
+/*
+ * list - zero or more args in a LIST
+ * listp - list (in puncutation only mode)
+ * arg - one ARG or function call
+ */
+
+list : listp
+ { $$.parse = $1.parse; yymode( SCAN_NORMAL ); }
+ ;
+
+listp : /* empty */
+ { $$.parse = pnull(); yymode( SCAN_PUNCT ); }
+ | listp arg
+ { $$.parse = pappend( $1.parse, $2.parse ); }
+ ;
+
+arg : ARG
+ { $$.parse = plist( $1.string ); }
+ | _LBRACKET_t { yymode( SCAN_NORMAL ); } func _RBRACKET_t
+ { $$.parse = $3.parse; }
+ ;
+
+/*
+ * func - a function call (inside [])
+ * This needs to be split cleanly out of 'rule'
+ */
+
+func : arg lol
+ { $$.parse = prule( $1.string, $2.parse ); }
+ | ON_t arg arg lol
+ { $$.parse = pon( $2.parse, prule( $3.string, $4.parse ) ); }
+ | ON_t arg RETURN_t list
+ { $$.parse = pon( $2.parse, $4.parse ); }
+ ;
+
+
+/*
+ * eflags - zero or more modifiers to 'executes'
+ * eflag - a single modifier to 'executes'
+ */
+
+eflags : /* empty */
+ { $$.number = 0; }
+ | eflags eflag
+ { $$.number = $1.number | $2.number; }
+ ;
+
+eflag : UPDATED_t
+ { $$.number = EXEC_UPDATED; }
+ | TOGETHER_t
+ { $$.number = EXEC_TOGETHER; }
+ | IGNORE_t
+ { $$.number = EXEC_IGNORE; }
+ | QUIETLY_t
+ { $$.number = EXEC_QUIETLY; }
+ | PIECEMEAL_t
+ { $$.number = EXEC_PIECEMEAL; }
+ | EXISTING_t
+ { $$.number = EXEC_EXISTING; }
+ ;
+
+
+/*
+ * bindlist - list of variable to bind for an action
+ */
+
+bindlist : /* empty */
+ { $$.parse = pnull(); }
+ | BIND_t list
+ { $$.parse = $2.parse; }
+ ;
diff --git a/jam-files/engine/jamgram.yy b/jam-files/engine/jamgram.yy
new file mode 100644
index 00000000..15243487
--- /dev/null
+++ b/jam-files/engine/jamgram.yy
@@ -0,0 +1,329 @@
+/*
+ * Copyright 1993, 2000 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/* This file is ALSO:
+ * Copyright 2001-2004 David Abrahams.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+/*
+ * jamgram.yy - jam grammar
+ *
+ * 04/13/94 (seiwald) - added shorthand L0 for null list pointer
+ * 06/01/94 (seiwald) - new 'actions existing' does existing sources
+ * 08/23/94 (seiwald) - Support for '+=' (append to variable)
+ * 08/31/94 (seiwald) - Allow ?= as alias for "default =".
+ * 09/15/94 (seiwald) - if conditionals take only single arguments, so
+ * that 'if foo == bar' gives syntax error (use =).
+ * 02/11/95 (seiwald) - when scanning arguments to rules, only treat
+ * punctuation keywords as keywords. All arg lists
+ * are terminated with punctuation keywords.
+ *
+ * 09/11/00 (seiwald) - Support for function calls:
+ *
+ * Rules now return lists (LIST *), rather than void.
+ *
+ * New "[ rule ]" syntax evals rule into a LIST.
+ *
+ * Lists are now generated by compile_list() and
+ * compile_append(), and any other rule that indirectly
+ * makes a list, rather than being built directly here,
+ * so that lists values can contain rule evaluations.
+ *
+ * New 'return' rule sets the return value, though
+ * other statements also may have return values.
+ *
+ * 'run' production split from 'block' production so
+ * that empty blocks can be handled separately.
+ */
+
+%token ARG STRING
+
+%left `||` `|`
+%left `&&` `&`
+%left `=` `!=` `in`
+%left `<` `<=` `>` `>=`
+%left `!`
+
+%{
+#include "jam.h"
+
+#include "lists.h"
+#include "parse.h"
+#include "scan.h"
+#include "compile.h"
+#include "newstr.h"
+#include "rules.h"
+
+# define YYMAXDEPTH 10000 /* for OSF and other less endowed yaccs */
+
+# define F0 (LIST *(*)(PARSE *, FRAME *))0
+# define P0 (PARSE *)0
+# define S0 (char *)0
+
+# define pappend( l,r ) parse_make( compile_append,l,r,P0,S0,S0,0 )
+# define peval( c,l,r ) parse_make( compile_eval,l,r,P0,S0,S0,c )
+# define pfor( s,l,r,x ) parse_make( compile_foreach,l,r,P0,s,S0,x )
+# define pif( l,r,t ) parse_make( compile_if,l,r,t,S0,S0,0 )
+# define pincl( l ) parse_make( compile_include,l,P0,P0,S0,S0,0 )
+# define plist( s ) parse_make( compile_list,P0,P0,P0,s,S0,0 )
+# define plocal( l,r,t ) parse_make( compile_local,l,r,t,S0,S0,0 )
+# define pmodule( l,r ) parse_make( compile_module,l,r,P0,S0,S0,0 )
+# define pclass( l,r ) parse_make( compile_class,l,r,P0,S0,S0,0 )
+# define pnull() parse_make( compile_null,P0,P0,P0,S0,S0,0 )
+# define pon( l,r ) parse_make( compile_on,l,r,P0,S0,S0,0 )
+# define prule( s,p ) parse_make( compile_rule,p,P0,P0,s,S0,0 )
+# define prules( l,r ) parse_make( compile_rules,l,r,P0,S0,S0,0 )
+# define pset( l,r,a ) parse_make( compile_set,l,r,P0,S0,S0,a )
+# define pset1( l,r,t,a ) parse_make( compile_settings,l,r,t,S0,S0,a )
+# define psetc( s,p,a,l ) parse_make( compile_setcomp,p,a,P0,s,S0,l )
+# define psete( s,l,s1,f ) parse_make( compile_setexec,l,P0,P0,s,s1,f )
+# define pswitch( l,r ) parse_make( compile_switch,l,r,P0,S0,S0,0 )
+# define pwhile( l,r ) parse_make( compile_while,l,r,P0,S0,S0,0 )
+
+# define pnode( l,r ) parse_make( F0,l,r,P0,S0,S0,0 )
+# define psnode( s,l ) parse_make( F0,l,P0,P0,s,S0,0 )
+
+%}
+
+%%
+
+run : /* empty */
+ /* do nothing */
+ | rules
+ { parse_save( $1.parse ); }
+ ;
+
+/*
+ * block - zero or more rules
+ * rules - one or more rules
+ * rule - any one of jam's rules
+ * right-recursive so rules execute in order.
+ */
+
+block : null
+ { $$.parse = $1.parse; }
+ | rules
+ { $$.parse = $1.parse; }
+ ;
+
+rules : rule
+ { $$.parse = $1.parse; }
+ | rule rules
+ { $$.parse = prules( $1.parse, $2.parse ); }
+ | `local` list assign_list_opt `;` block
+ { $$.parse = plocal( $2.parse, $3.parse, $5.parse ); }
+ ;
+
+null : /* empty */
+ { $$.parse = pnull(); }
+ ;
+
+assign_list_opt : `=` list
+ { $$.parse = $2.parse; $$.number = ASSIGN_SET; }
+ | null
+ { $$.parse = $1.parse; $$.number = ASSIGN_APPEND; }
+ ;
+
+arglist_opt : `(` lol `)`
+ { $$.parse = $2.parse; }
+ |
+ { $$.parse = P0; }
+ ;
+
+local_opt : `local`
+ { $$.number = 1; }
+ | /* empty */
+ { $$.number = 0; }
+ ;
+
+rule : `{` block `}`
+ { $$.parse = $2.parse; }
+ | `include` list `;`
+ { $$.parse = pincl( $2.parse ); }
+ | ARG lol `;`
+ { $$.parse = prule( $1.string, $2.parse ); }
+ | arg assign list `;`
+ { $$.parse = pset( $1.parse, $3.parse, $2.number ); }
+ | arg `on` list assign list `;`
+ { $$.parse = pset1( $1.parse, $3.parse, $5.parse, $4.number ); }
+ | `return` list `;`
+ { $$.parse = $2.parse; }
+ | `for` local_opt ARG `in` list `{` block `}`
+ { $$.parse = pfor( $3.string, $5.parse, $7.parse, $2.number ); }
+ | `switch` list `{` cases `}`
+ { $$.parse = pswitch( $2.parse, $4.parse ); }
+ | `if` expr `{` block `}`
+ { $$.parse = pif( $2.parse, $4.parse, pnull() ); }
+ | `module` list `{` block `}`
+ { $$.parse = pmodule( $2.parse, $4.parse ); }
+ | `class` lol `{` block `}`
+ { $$.parse = pclass( $2.parse, $4.parse ); }
+ | `while` expr `{` block `}`
+ { $$.parse = pwhile( $2.parse, $4.parse ); }
+ | `if` expr `{` block `}` `else` rule
+ { $$.parse = pif( $2.parse, $4.parse, $7.parse ); }
+ | local_opt `rule` ARG arglist_opt rule
+ { $$.parse = psetc( $3.string, $5.parse, $4.parse, $1.number ); }
+ | `on` arg rule
+ { $$.parse = pon( $2.parse, $3.parse ); }
+ | `actions` eflags ARG bindlist `{`
+ { yymode( SCAN_STRING ); }
+ STRING
+ { yymode( SCAN_NORMAL ); }
+ `}`
+ { $$.parse = psete( $3.string,$4.parse,$7.string,$2.number ); }
+ ;
+
+/*
+ * assign - = or +=
+ */
+
+assign : `=`
+ { $$.number = ASSIGN_SET; }
+ | `+=`
+ { $$.number = ASSIGN_APPEND; }
+ | `?=`
+ { $$.number = ASSIGN_DEFAULT; }
+ | `default` `=`
+ { $$.number = ASSIGN_DEFAULT; }
+ ;
+
+/*
+ * expr - an expression for if
+ */
+expr : arg
+ { $$.parse = peval( EXPR_EXISTS, $1.parse, pnull() ); }
+ | expr `=` expr
+ { $$.parse = peval( EXPR_EQUALS, $1.parse, $3.parse ); }
+ | expr `!=` expr
+ { $$.parse = peval( EXPR_NOTEQ, $1.parse, $3.parse ); }
+ | expr `<` expr
+ { $$.parse = peval( EXPR_LESS, $1.parse, $3.parse ); }
+ | expr `<=` expr
+ { $$.parse = peval( EXPR_LESSEQ, $1.parse, $3.parse ); }
+ | expr `>` expr
+ { $$.parse = peval( EXPR_MORE, $1.parse, $3.parse ); }
+ | expr `>=` expr
+ { $$.parse = peval( EXPR_MOREEQ, $1.parse, $3.parse ); }
+ | expr `&` expr
+ { $$.parse = peval( EXPR_AND, $1.parse, $3.parse ); }
+ | expr `&&` expr
+ { $$.parse = peval( EXPR_AND, $1.parse, $3.parse ); }
+ | expr `|` expr
+ { $$.parse = peval( EXPR_OR, $1.parse, $3.parse ); }
+ | expr `||` expr
+ { $$.parse = peval( EXPR_OR, $1.parse, $3.parse ); }
+ | arg `in` list
+ { $$.parse = peval( EXPR_IN, $1.parse, $3.parse ); }
+ | `!` expr
+ { $$.parse = peval( EXPR_NOT, $2.parse, pnull() ); }
+ | `(` expr `)`
+ { $$.parse = $2.parse; }
+ ;
+
+
+/*
+ * cases - action elements inside a 'switch'
+ * case - a single action element inside a 'switch'
+ * right-recursive rule so cases can be examined in order.
+ */
+
+cases : /* empty */
+ { $$.parse = P0; }
+ | case cases
+ { $$.parse = pnode( $1.parse, $2.parse ); }
+ ;
+
+case : `case` ARG `:` block
+ { $$.parse = psnode( $2.string, $4.parse ); }
+ ;
+
+/*
+ * lol - list of lists
+ * right-recursive rule so that lists can be added in order.
+ */
+
+lol : list
+ { $$.parse = pnode( P0, $1.parse ); }
+ | list `:` lol
+ { $$.parse = pnode( $3.parse, $1.parse ); }
+ ;
+
+/*
+ * list - zero or more args in a LIST
+ * listp - list (in puncutation only mode)
+ * arg - one ARG or function call
+ */
+
+list : listp
+ { $$.parse = $1.parse; yymode( SCAN_NORMAL ); }
+ ;
+
+listp : /* empty */
+ { $$.parse = pnull(); yymode( SCAN_PUNCT ); }
+ | listp arg
+ { $$.parse = pappend( $1.parse, $2.parse ); }
+ ;
+
+arg : ARG
+ { $$.parse = plist( $1.string ); }
+ | `[` { yymode( SCAN_NORMAL ); } func `]`
+ { $$.parse = $3.parse; }
+ ;
+
+/*
+ * func - a function call (inside [])
+ * This needs to be split cleanly out of 'rule'
+ */
+
+func : arg lol
+ { $$.parse = prule( $1.string, $2.parse ); }
+ | `on` arg arg lol
+ { $$.parse = pon( $2.parse, prule( $3.string, $4.parse ) ); }
+ | `on` arg `return` list
+ { $$.parse = pon( $2.parse, $4.parse ); }
+ ;
+
+
+/*
+ * eflags - zero or more modifiers to 'executes'
+ * eflag - a single modifier to 'executes'
+ */
+
+eflags : /* empty */
+ { $$.number = 0; }
+ | eflags eflag
+ { $$.number = $1.number | $2.number; }
+ ;
+
+eflag : `updated`
+ { $$.number = EXEC_UPDATED; }
+ | `together`
+ { $$.number = EXEC_TOGETHER; }
+ | `ignore`
+ { $$.number = EXEC_IGNORE; }
+ | `quietly`
+ { $$.number = EXEC_QUIETLY; }
+ | `piecemeal`
+ { $$.number = EXEC_PIECEMEAL; }
+ | `existing`
+ { $$.number = EXEC_EXISTING; }
+ ;
+
+
+/*
+ * bindlist - list of variable to bind for an action
+ */
+
+bindlist : /* empty */
+ { $$.parse = pnull(); }
+ | `bind` list
+ { $$.parse = $2.parse; }
+ ;
+
+
diff --git a/jam-files/engine/jamgramtab.h b/jam-files/engine/jamgramtab.h
new file mode 100644
index 00000000..a0fd43f6
--- /dev/null
+++ b/jam-files/engine/jamgramtab.h
@@ -0,0 +1,44 @@
+ { "!", _BANG_t },
+ { "!=", _BANG_EQUALS_t },
+ { "&", _AMPER_t },
+ { "&&", _AMPERAMPER_t },
+ { "(", _LPAREN_t },
+ { ")", _RPAREN_t },
+ { "+=", _PLUS_EQUALS_t },
+ { ":", _COLON_t },
+ { ";", _SEMIC_t },
+ { "<", _LANGLE_t },
+ { "<=", _LANGLE_EQUALS_t },
+ { "=", _EQUALS_t },
+ { ">", _RANGLE_t },
+ { ">=", _RANGLE_EQUALS_t },
+ { "?=", _QUESTION_EQUALS_t },
+ { "[", _LBRACKET_t },
+ { "]", _RBRACKET_t },
+ { "actions", ACTIONS_t },
+ { "bind", BIND_t },
+ { "case", CASE_t },
+ { "class", CLASS_t },
+ { "default", DEFAULT_t },
+ { "else", ELSE_t },
+ { "existing", EXISTING_t },
+ { "for", FOR_t },
+ { "if", IF_t },
+ { "ignore", IGNORE_t },
+ { "in", IN_t },
+ { "include", INCLUDE_t },
+ { "local", LOCAL_t },
+ { "module", MODULE_t },
+ { "on", ON_t },
+ { "piecemeal", PIECEMEAL_t },
+ { "quietly", QUIETLY_t },
+ { "return", RETURN_t },
+ { "rule", RULE_t },
+ { "switch", SWITCH_t },
+ { "together", TOGETHER_t },
+ { "updated", UPDATED_t },
+ { "while", WHILE_t },
+ { "{", _LBRACE_t },
+ { "|", _BAR_t },
+ { "||", _BARBAR_t },
+ { "}", _RBRACE_t },
diff --git a/jam-files/engine/lists.c b/jam-files/engine/lists.c
new file mode 100644
index 00000000..ebabb63e
--- /dev/null
+++ b/jam-files/engine/lists.c
@@ -0,0 +1,339 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+# include "jam.h"
+# include "newstr.h"
+# include "lists.h"
+
+/*
+ * lists.c - maintain lists of strings
+ *
+ * This implementation essentially uses a singly linked list, but
+ * guarantees that the head element of every list has a valid pointer
+ * to the tail of the list, so the new elements can efficiently and
+ * properly be appended to the end of a list.
+ *
+ * To avoid massive allocation, list_free() just tacks the whole freed
+ * chain onto freelist and list_new() looks on freelist first for an
+ * available list struct. list_free() does not free the strings in the
+ * chain: it lazily lets list_new() do so.
+ *
+ * 08/23/94 (seiwald) - new list_append()
+ * 09/07/00 (seiwald) - documented lol_*() functions
+ */
+
+static LIST *freelist = 0; /* junkpile for list_free() */
+
+/*
+ * list_append() - append a list onto another one, returning total
+ */
+
+LIST * list_append( LIST * l, LIST * nl )
+{
+ if ( !nl )
+ {
+ /* Just return l */
+ }
+ else if ( !l )
+ {
+ l = nl;
+ }
+ else
+ {
+ /* Graft two non-empty lists. */
+ l->tail->next = nl;
+ l->tail = nl->tail;
+ }
+
+ return l;
+}
+
+/*
+ * list_new() - tack a string onto the end of a list of strings
+ */
+
+LIST * list_new( LIST * head, char * string )
+{
+ LIST * l;
+
+ if ( DEBUG_LISTS )
+ printf( "list > %s <\n", string );
+
+ /* Get list struct from freelist, if one available. */
+ /* Otherwise allocate. */
+ /* If from freelist, must free string first */
+
+ if ( freelist )
+ {
+ l = freelist;
+ freestr( l->string );
+ freelist = freelist->next;
+ }
+ else
+ {
+ l = (LIST *)BJAM_MALLOC( sizeof( LIST ) );
+ }
+
+ /* If first on chain, head points here. */
+ /* If adding to chain, tack us on. */
+ /* Tail must point to this new, last element. */
+
+ if ( !head ) head = l;
+ else head->tail->next = l;
+ head->tail = l;
+ l->next = 0;
+
+ l->string = string;
+
+ return head;
+}
+
+
+/*
+ * list_copy() - copy a whole list of strings (nl) onto end of another (l).
+ */
+
+LIST * list_copy( LIST * l, LIST * nl )
+{
+ for ( ; nl; nl = list_next( nl ) )
+ l = list_new( l, copystr( nl->string ) );
+ return l;
+}
+
+
+/*
+ * list_sublist() - copy a subset of a list of strings.
+ */
+
+LIST * list_sublist( LIST * l, int start, int count )
+{
+ LIST * nl = 0;
+ for ( ; l && start--; l = list_next( l ) );
+ for ( ; l && count--; l = list_next( l ) )
+ nl = list_new( nl, copystr( l->string ) );
+ return nl;
+}
+
+
+static int str_ptr_compare( void const * va, void const * vb )
+{
+ char * a = *( (char * *)va );
+ char * b = *( (char * *)vb );
+ return strcmp(a, b);
+}
+
+
+LIST * list_sort( LIST * l )
+{
+ int len;
+ int ii;
+ char * * strings;
+ LIST * listp;
+ LIST * result = 0;
+
+ if ( !l )
+ return L0;
+
+ len = list_length( l );
+ strings = (char * *)BJAM_MALLOC( len * sizeof(char*) );
+
+ listp = l;
+ for ( ii = 0; ii < len; ++ii )
+ {
+ strings[ ii ] = listp->string;
+ listp = listp->next;
+ }
+
+ qsort( strings, len, sizeof( char * ), str_ptr_compare );
+
+ for ( ii = 0; ii < len; ++ii )
+ result = list_append( result, list_new( 0, strings[ ii ] ) );
+
+ BJAM_FREE( strings );
+
+ return result;
+}
+
+
+/*
+ * list_free() - free a list of strings
+ */
+
+void list_free( LIST * head )
+{
+ /* Just tack onto freelist. */
+ if ( head )
+ {
+ head->tail->next = freelist;
+ freelist = head;
+ }
+}
+
+
+/*
+ * list_pop_front() - remove the front element from a list of strings
+ */
+
+LIST * list_pop_front( LIST * l )
+{
+ LIST * result = l->next;
+ if ( result )
+ {
+ result->tail = l->tail;
+ l->next = L0;
+ l->tail = l;
+ }
+ list_free( l );
+ return result;
+}
+
+
+/*
+ * list_print() - print a list of strings to stdout
+ */
+
+void list_print( LIST * l )
+{
+ LIST * p = 0;
+ for ( ; l; p = l, l = list_next( l ) )
+ if ( p )
+ printf( "%s ", p->string );
+ if ( p )
+ printf( "%s", p->string );
+}
+
+
+/*
+ * list_length() - return the number of items in the list
+ */
+
+int list_length( LIST * l )
+{
+ int n = 0;
+ for ( ; l; l = list_next( l ), ++n );
+ return n;
+}
+
+
+int list_in( LIST * l, char * value )
+{
+ for ( ; l; l = l->next )
+ if ( strcmp( l->string, value ) == 0 )
+ return 1;
+ return 0;
+}
+
+
+LIST * list_unique( LIST * sorted_list )
+{
+ LIST * result = 0;
+ LIST * last_added = 0;
+
+ for ( ; sorted_list; sorted_list = sorted_list->next )
+ {
+ if ( !last_added || strcmp( sorted_list->string, last_added->string ) != 0 )
+ {
+ result = list_new( result, sorted_list->string );
+ last_added = sorted_list;
+ }
+ }
+ return result;
+}
+
+
+/*
+ * lol_init() - initialize a LOL (list of lists).
+ */
+
+void lol_init( LOL * lol )
+{
+ lol->count = 0;
+}
+
+
+/*
+ * lol_add() - append a LIST onto an LOL.
+ */
+
+void lol_add( LOL * lol, LIST * l )
+{
+ if ( lol->count < LOL_MAX )
+ lol->list[ lol->count++ ] = l;
+}
+
+
+/*
+ * lol_free() - free the LOL and its LISTs.
+ */
+
+void lol_free( LOL * lol )
+{
+ int i;
+ for ( i = 0; i < lol->count; ++i )
+ list_free( lol->list[ i ] );
+ lol->count = 0;
+}
+
+
+/*
+ * lol_get() - return one of the LISTs in the LOL.
+ */
+
+LIST * lol_get( LOL * lol, int i )
+{
+ return i < lol->count ? lol->list[ i ] : 0;
+}
+
+
+/*
+ * lol_print() - debug print LISTS separated by ":".
+ */
+
+void lol_print( LOL * lol )
+{
+ int i;
+
+ for ( i = 0; i < lol->count; ++i )
+ {
+ if ( i )
+ printf( " : " );
+ list_print( lol->list[ i ] );
+ }
+}
+
+#ifdef HAVE_PYTHON
+
+PyObject *list_to_python(LIST *l)
+{
+ PyObject *result = PyList_New(0);
+
+ for (; l; l = l->next)
+ {
+ PyObject* s = PyString_FromString(l->string);
+ PyList_Append(result, s);
+ Py_DECREF(s);
+ }
+
+ return result;
+}
+
+LIST *list_from_python(PyObject *l)
+{
+ LIST * result = 0;
+
+ Py_ssize_t i, n;
+ n = PySequence_Size(l);
+ for (i = 0; i < n; ++i)
+ {
+ PyObject *v = PySequence_GetItem(l, i);
+ result = list_new (result, newstr (PyString_AsString(v)));
+ Py_DECREF(v);
+ }
+
+ return result;
+}
+
+#endif
diff --git a/jam-files/engine/lists.h b/jam-files/engine/lists.h
new file mode 100644
index 00000000..1dc59827
--- /dev/null
+++ b/jam-files/engine/lists.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/* This file is ALSO:
+ * Copyright 2001-2004 David Abrahams.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+/*
+ * lists.h - the LIST structure and routines to manipulate them
+ *
+ * The whole of jam relies on lists of strings as a datatype. This
+ * module, in conjunction with newstr.c, handles these relatively
+ * efficiently.
+ *
+ * Structures defined:
+ *
+ * LIST - list of strings
+ * LOL - list of LISTs
+ *
+ * External routines:
+ *
+ * list_append() - append a list onto another one, returning total
+ * list_new() - tack a string onto the end of a list of strings
+ * list_copy() - copy a whole list of strings
+ * list_sublist() - copy a subset of a list of strings
+ * list_free() - free a list of strings
+ * list_print() - print a list of strings to stdout
+ * list_length() - return the number of items in the list
+ *
+ * lol_init() - initialize a LOL (list of lists)
+ * lol_add() - append a LIST onto an LOL
+ * lol_free() - free the LOL and its LISTs
+ * lol_get() - return one of the LISTs in the LOL
+ * lol_print() - debug print LISTS separated by ":"
+ *
+ * 04/13/94 (seiwald) - added shorthand L0 for null list pointer
+ * 08/23/94 (seiwald) - new list_append()
+ */
+
+#ifndef LISTS_DWA20011022_H
+# define LISTS_DWA20011022_H
+
+#ifdef HAVE_PYTHON
+#include <Python.h>
+#endif
+
+/*
+ * LIST - list of strings
+ */
+
+typedef struct _list LIST;
+
+struct _list {
+ LIST *next;
+ LIST *tail; /* only valid in head node */
+ char *string; /* private copy */
+};
+
+/*
+ * LOL - list of LISTs
+ */
+
+typedef struct _lol LOL;
+
+# define LOL_MAX 19
+
+struct _lol {
+ int count;
+ LIST *list[ LOL_MAX ];
+};
+
+LIST * list_append( LIST *l, LIST *nl );
+LIST * list_copy( LIST *l, LIST *nl );
+void list_free( LIST *head );
+LIST * list_new( LIST *head, char *string );
+void list_print( LIST *l );
+int list_length( LIST *l );
+LIST * list_sublist( LIST *l, int start, int count );
+LIST * list_pop_front( LIST *l );
+LIST * list_sort( LIST *l);
+LIST * list_unique( LIST *sorted_list);
+int list_in(LIST* l, char* value);
+
+# define list_next( l ) ((l)->next)
+
+# define L0 ((LIST *)0)
+
+void lol_add( LOL *lol, LIST *l );
+void lol_init( LOL *lol );
+void lol_free( LOL *lol );
+LIST * lol_get( LOL *lol, int i );
+void lol_print( LOL *lol );
+void lol_build( LOL* lol, char** elements );
+
+#ifdef HAVE_PYTHON
+
+PyObject *list_to_python(LIST *l);
+LIST *list_from_python(PyObject *l);
+
+#endif
+
+#endif
+
diff --git a/jam-files/engine/make.c b/jam-files/engine/make.c
new file mode 100644
index 00000000..c871f0be
--- /dev/null
+++ b/jam-files/engine/make.c
@@ -0,0 +1,814 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/* This file is ALSO:
+ * Copyright 2001-2004 David Abrahams.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+/*
+ * make.c - bring a target up to date, once rules are in place.
+ *
+ * This modules controls the execution of rules to bring a target and its
+ * dependencies up to date. It is invoked after the targets, rules, et. al.
+ * described in rules.h are created by the interpreting jam files.
+ *
+ * This file contains the main make() entry point and the first pass make0().
+ * The second pass, make1(), which actually does the command execution, is in
+ * make1.c.
+ *
+ * External routines:
+ * make() - make a target, given its name
+ *
+ * Internal routines:
+ * make0() - bind and scan everything to make a TARGET
+ * make0sort() - reorder TARGETS chain by their time (newest to oldest)
+ *
+ * 12/26/93 (seiwald) - allow NOTIME targets to be expanded via $(<), $(>).
+ * 01/04/94 (seiwald) - print all targets, bounded, when tracing commands.
+ * 04/08/94 (seiwald) - progress report now reflects only targets with actions.
+ * 04/11/94 (seiwald) - Combined deps & headers into deps[2] in TARGET.
+ * 12/20/94 (seiwald) - NOTIME renamed NOTFILE.
+ * 12/20/94 (seiwald) - make0() headers after determining fate of target, so
+ * that headers are not seen as being dependent on
+ * themselves.
+ * 01/19/95 (seiwald) - distinguish between CANTFIND/CANTMAKE targets.
+ * 02/02/95 (seiwald) - propagate leaf source time for new LEAVES rule.
+ * 02/14/95 (seiwald) - NOUPDATE rule means don't update existing target.
+ * 08/22/95 (seiwald) - NOUPDATE targets immune to anyhow (-a) flag.
+ * 09/06/00 (seiwald) - NOCARE affects targets with sources/actions.
+ * 03/02/01 (seiwald) - reverse NOCARE change.
+ * 03/14/02 (seiwald) - TEMPORARY targets no longer take on parents age.
+ * 03/16/02 (seiwald) - support for -g (reorder builds by source time).
+ */
+
+#include "jam.h"
+
+#include "lists.h"
+#include "parse.h"
+#include "variable.h"
+#include "rules.h"
+
+#ifdef OPT_HEADER_CACHE_EXT
+ #include "hcache.h"
+#endif
+
+#include "search.h"
+#include "newstr.h"
+#include "make.h"
+#include "headers.h"
+#include "command.h"
+#include <assert.h>
+
+#ifndef max
+ #define max( a,b ) ((a)>(b)?(a):(b))
+#endif
+
+static TARGETS * make0sort( TARGETS * c );
+
+#ifdef OPT_GRAPH_DEBUG_EXT
+ static void dependGraphOutput( TARGET * t, int depth );
+#endif
+
+static const char * target_fate[] =
+{
+ "init", /* T_FATE_INIT */
+ "making", /* T_FATE_MAKING */
+ "stable", /* T_FATE_STABLE */
+ "newer", /* T_FATE_NEWER */
+ "temp", /* T_FATE_ISTMP */
+ "touched", /* T_FATE_TOUCHED */
+ "rebuild", /* T_FATE_REBUILD */
+ "missing", /* T_FATE_MISSING */
+ "needtmp", /* T_FATE_NEEDTMP */
+ "old", /* T_FATE_OUTDATED */
+ "update", /* T_FATE_UPDATE */
+ "nofind", /* T_FATE_CANTFIND */
+ "nomake" /* T_FATE_CANTMAKE */
+};
+
+static const char * target_bind[] =
+{
+ "unbound",
+ "missing",
+ "parents",
+ "exists",
+};
+
+# define spaces(x) ( " " + ( x > 20 ? 0 : 20-x ) )
+
+
+/*
+ * make() - make a target, given its name.
+ */
+
+int make( int n_targets, char const * * targets, int anyhow )
+{
+ int i;
+ COUNTS counts[ 1 ];
+ int status = 0; /* 1 if anything fails */
+
+#ifdef OPT_HEADER_CACHE_EXT
+ hcache_init();
+#endif
+
+ memset( (char *)counts, 0, sizeof( *counts ) );
+
+ /* First bind all targets with LOCATE_TARGET setting. This is needed to
+ * correctly handle dependencies to generated headers.
+ */
+ bind_explicitly_located_targets();
+
+ {
+ PROFILE_ENTER( MAKE_MAKE0 );
+ for ( i = 0; i < n_targets; ++i )
+ make0( bindtarget( targets[ i ] ), 0, 0, counts, anyhow );
+ PROFILE_EXIT( MAKE_MAKE0 );
+ }
+
+#ifdef OPT_GRAPH_DEBUG_EXT
+ if ( DEBUG_GRAPH )
+ for ( i = 0; i < n_targets; ++i )
+ dependGraphOutput( bindtarget( targets[ i ] ), 0 );
+#endif
+
+ if ( DEBUG_MAKE )
+ {
+ if ( counts->targets )
+ printf( "...found %d target%s...\n", counts->targets,
+ counts->targets > 1 ? "s" : "" );
+ if ( counts->temp )
+ printf( "...using %d temp target%s...\n", counts->temp,
+ counts->temp > 1 ? "s" : "" );
+ if ( counts->updating )
+ printf( "...updating %d target%s...\n", counts->updating,
+ counts->updating > 1 ? "s" : "" );
+ if ( counts->cantfind )
+ printf( "...can't find %d target%s...\n", counts->cantfind,
+ counts->cantfind > 1 ? "s" : "" );
+ if ( counts->cantmake )
+ printf( "...can't make %d target%s...\n", counts->cantmake,
+ counts->cantmake > 1 ? "s" : "" );
+ }
+
+#ifdef OPT_HEADER_CACHE_EXT
+ hcache_done();
+#endif
+
+ status = counts->cantfind || counts->cantmake;
+
+ {
+ PROFILE_ENTER( MAKE_MAKE1 );
+ for ( i = 0; i < n_targets; ++i )
+ status |= make1( bindtarget( targets[ i ] ) );
+ PROFILE_EXIT( MAKE_MAKE1 );
+ }
+
+ return status;
+}
+
+
+/* Force any dependants of t that have already at least begun being visited by
+ * make0() to be updated.
+ */
+
+static void update_dependants( TARGET * t )
+{
+ TARGETS * q;
+
+ for ( q = t->dependants; q; q = q->next )
+ {
+ TARGET * p = q->target;
+ char fate0 = p->fate;
+
+ /* If we have already at least begun visiting it and we are not already
+ * rebuilding it for other reasons.
+ */
+ if ( ( fate0 != T_FATE_INIT ) && ( fate0 < T_FATE_BUILD ) )
+ {
+ p->fate = T_FATE_UPDATE;
+
+ if ( DEBUG_FATE )
+ {
+ printf( "fate change %s from %s to %s (as dependant of %s)\n",
+ p->name, target_fate[ (int) fate0 ], target_fate[ (int) p->fate ], t->name );
+ }
+
+ /* If we are done visiting it, go back and make sure its dependants
+ * get rebuilt.
+ */
+ if ( fate0 > T_FATE_MAKING )
+ update_dependants( p );
+ }
+ }
+}
+
+
+/*
+ * Make sure that all of t's rebuilds get rebuilt.
+ */
+
+static void force_rebuilds( TARGET * t )
+{
+ TARGETS * d;
+ for ( d = t->rebuilds; d; d = d->next )
+ {
+ TARGET * r = d->target;
+
+ /* If it is not already being rebuilt for other reasons. */
+ if ( r->fate < T_FATE_BUILD )
+ {
+ if ( DEBUG_FATE )
+ printf( "fate change %s from %s to %s (by rebuild)\n",
+ r->name, target_fate[ (int) r->fate ], target_fate[ T_FATE_REBUILD ] );
+
+ /* Force rebuild it. */
+ r->fate = T_FATE_REBUILD;
+
+ /* And make sure its dependants are updated too. */
+ update_dependants( r );
+ }
+ }
+}
+
+
+/*
+ * make0() - bind and scan everything to make a TARGET.
+ *
+ * Recursively binds a target, searches for #included headers, calls itself on
+ * those headers and any dependencies.
+ */
+
+void make0
+(
+ TARGET * t,
+ TARGET * p, /* parent */
+ int depth, /* for display purposes */
+ COUNTS * counts, /* for reporting */
+ int anyhow
+) /* forcibly touch all (real) targets */
+{
+ TARGETS * c;
+ TARGET * ptime = t;
+ time_t last;
+ time_t leaf;
+ time_t hlast;
+ int fate;
+ char const * flag = "";
+ SETTINGS * s;
+
+#ifdef OPT_GRAPH_DEBUG_EXT
+ int savedFate, oldTimeStamp;
+#endif
+
+ if ( DEBUG_MAKEPROG )
+ printf( "make\t--\t%s%s\n", spaces( depth ), t->name );
+
+ /*
+ * Step 1: initialize
+ */
+
+ if ( DEBUG_MAKEPROG )
+ printf( "make\t--\t%s%s\n", spaces( depth ), t->name );
+
+ t->fate = T_FATE_MAKING;
+
+ /*
+ * Step 2: under the influence of "on target" variables,
+ * bind the target and search for headers.
+ */
+
+ /* Step 2a: set "on target" variables. */
+ s = copysettings( t->settings );
+ pushsettings( s );
+
+ /* Step 2b: find and timestamp the target file (if it is a file). */
+ if ( ( t->binding == T_BIND_UNBOUND ) && !( t->flags & T_FLAG_NOTFILE ) )
+ {
+ char * another_target;
+ t->boundname = search( t->name, &t->time, &another_target,
+ t->flags & T_FLAG_ISFILE );
+ /* If it was detected that this target refers to an already existing and
+ * bound one, we add an include dependency, so that every target
+ * depending on us will depend on that other target as well.
+ */
+ if ( another_target )
+ target_include( t, bindtarget( another_target ) );
+
+ t->binding = t->time ? T_BIND_EXISTS : T_BIND_MISSING;
+ }
+
+ /* INTERNAL, NOTFILE header nodes have the time of their parents. */
+ if ( p && ( t->flags & T_FLAG_INTERNAL ) )
+ ptime = p;
+
+ /* If temp file does not exist but parent does, use parent. */
+ if ( p && ( t->flags & T_FLAG_TEMP ) &&
+ ( t->binding == T_BIND_MISSING ) &&
+ ( p->binding != T_BIND_MISSING ) )
+ {
+ t->binding = T_BIND_PARENTS;
+ ptime = p;
+ }
+
+#ifdef OPT_SEMAPHORE
+ {
+ LIST * var = var_get( "JAM_SEMAPHORE" );
+ if ( var )
+ {
+ TARGET * semaphore = bindtarget( var->string );
+ semaphore->progress = T_MAKE_SEMAPHORE;
+ t->semaphore = semaphore;
+ }
+ }
+#endif
+
+ /* Step 2c: If its a file, search for headers. */
+ if ( t->binding == T_BIND_EXISTS )
+ headers( t );
+
+ /* Step 2d: reset "on target" variables. */
+ popsettings( s );
+ freesettings( s );
+
+ /*
+ * Pause for a little progress reporting .
+ */
+
+ if ( DEBUG_BIND )
+ {
+ if ( strcmp( t->name, t->boundname ) )
+ printf( "bind\t--\t%s%s: %s\n",
+ spaces( depth ), t->name, t->boundname );
+
+ switch ( t->binding )
+ {
+ case T_BIND_UNBOUND:
+ case T_BIND_MISSING:
+ case T_BIND_PARENTS:
+ printf( "time\t--\t%s%s: %s\n",
+ spaces( depth ), t->name, target_bind[ (int) t->binding ] );
+ break;
+
+ case T_BIND_EXISTS:
+ printf( "time\t--\t%s%s: %s",
+ spaces( depth ), t->name, ctime( &t->time ) );
+ break;
+ }
+ }
+
+ /*
+ * Step 3: recursively make0() dependencies & headers.
+ */
+
+ /* Step 3a: recursively make0() dependencies. */
+ for ( c = t->depends; c; c = c->next )
+ {
+ int internal = t->flags & T_FLAG_INTERNAL;
+
+ /* Warn about circular deps, except for includes, which include each
+ * other alot.
+ */
+ if ( c->target->fate == T_FATE_INIT )
+ make0( c->target, ptime, depth + 1, counts, anyhow );
+ else if ( c->target->fate == T_FATE_MAKING && !internal )
+ printf( "warning: %s depends on itself\n", c->target->name );
+ }
+
+ /* Step 3b: recursively make0() internal includes node. */
+ if ( t->includes )
+ make0( t->includes, p, depth + 1, counts, anyhow );
+
+ /* Step 3c: add dependencies' includes to our direct dependencies. */
+ {
+ TARGETS * incs = 0;
+ for ( c = t->depends; c; c = c->next )
+ if ( c->target->includes )
+ incs = targetentry( incs, c->target->includes );
+ t->depends = targetchain( t->depends, incs );
+ }
+
+ /*
+ * Step 4: compute time & fate
+ */
+
+ /* Step 4a: pick up dependencies' time and fate */
+ last = 0;
+ leaf = 0;
+ fate = T_FATE_STABLE;
+ for ( c = t->depends; c; c = c->next )
+ {
+ /* If LEAVES has been applied, we only heed the timestamps of the leaf
+ * source nodes.
+ */
+ leaf = max( leaf, c->target->leaf );
+
+ if ( t->flags & T_FLAG_LEAVES )
+ {
+ last = leaf;
+ continue;
+ }
+
+ last = max( last, c->target->time );
+ fate = max( fate, c->target->fate );
+
+#ifdef OPT_GRAPH_DEBUG_EXT
+ if ( DEBUG_FATE )
+ if ( fate < c->target->fate )
+ printf( "fate change %s from %s to %s by dependency %s\n",
+ t->name, target_fate[(int) fate], target_fate[(int) c->target->fate],
+ c->target->name );
+#endif
+ }
+
+ /* Step 4b: pick up included headers time */
+
+ /*
+ * If a header is newer than a temp source that includes it,
+ * the temp source will need building.
+ */
+
+ hlast = t->includes ? t->includes->time : 0;
+
+ /* Step 4c: handle NOUPDATE oddity.
+ *
+ * If a NOUPDATE file exists, mark it as having eternally old dependencies.
+ * Do not inherit our fate from our dependencies. Decide fate based only on
+ * other flags and our binding (done later).
+ */
+ if ( t->flags & T_FLAG_NOUPDATE )
+ {
+#ifdef OPT_GRAPH_DEBUG_EXT
+ if ( DEBUG_FATE )
+ if ( fate != T_FATE_STABLE )
+ printf( "fate change %s back to stable, NOUPDATE.\n", t->name
+ );
+#endif
+
+ last = 0;
+ t->time = 0;
+
+ /* Do not inherit our fate from our dependencies. Decide fate based only
+ * upon other flags and our binding (done later).
+ */
+ fate = T_FATE_STABLE;
+ }
+
+ /* Step 4d: determine fate: rebuild target or what? */
+
+ /*
+ In English:
+ If can not find or make child, can not make target.
+ If children changed, make target.
+ If target missing, make it.
+ If children newer, make target.
+ If temp's children newer than parent, make temp.
+ If temp's headers newer than parent, make temp.
+ If deliberately touched, make it.
+ If up-to-date temp file present, use it.
+ If target newer than non-notfile parent, mark target newer.
+ Otherwise, stable!
+
+ Note this block runs from least to most stable:
+ as we make it further down the list, the target's
+ fate is getting stabler.
+ */
+
+#ifdef OPT_GRAPH_DEBUG_EXT
+ savedFate = fate;
+ oldTimeStamp = 0;
+#endif
+
+ if ( fate >= T_FATE_BROKEN )
+ {
+ fate = T_FATE_CANTMAKE;
+ }
+ else if ( fate >= T_FATE_SPOIL )
+ {
+ fate = T_FATE_UPDATE;
+ }
+ else if ( t->binding == T_BIND_MISSING )
+ {
+ fate = T_FATE_MISSING;
+ }
+ else if ( ( t->binding == T_BIND_EXISTS ) && ( last > t->time ) )
+ {
+#ifdef OPT_GRAPH_DEBUG_EXT
+ oldTimeStamp = 1;
+#endif
+ fate = T_FATE_OUTDATED;
+ }
+ else if ( ( t->binding == T_BIND_PARENTS ) && ( last > p->time ) )
+ {
+#ifdef OPT_GRAPH_DEBUG_EXT
+ oldTimeStamp = 1;
+#endif
+ fate = T_FATE_NEEDTMP;
+ }
+ else if ( ( t->binding == T_BIND_PARENTS ) && ( hlast > p->time ) )
+ {
+ fate = T_FATE_NEEDTMP;
+ }
+ else if ( t->flags & T_FLAG_TOUCHED )
+ {
+ fate = T_FATE_TOUCHED;
+ }
+ else if ( anyhow && !( t->flags & T_FLAG_NOUPDATE ) )
+ {
+ fate = T_FATE_TOUCHED;
+ }
+ else if ( ( t->binding == T_BIND_EXISTS ) && ( t->flags & T_FLAG_TEMP ) )
+ {
+ fate = T_FATE_ISTMP;
+ }
+ else if ( ( t->binding == T_BIND_EXISTS ) && p &&
+ ( p->binding != T_BIND_UNBOUND ) && ( t->time > p->time ) )
+ {
+#ifdef OPT_GRAPH_DEBUG_EXT
+ oldTimeStamp = 1;
+#endif
+ fate = T_FATE_NEWER;
+ }
+ else
+ {
+ fate = T_FATE_STABLE;
+ }
+#ifdef OPT_GRAPH_DEBUG_EXT
+ if ( DEBUG_FATE && ( fate != savedFate ) )
+ {
+ if ( savedFate == T_FATE_STABLE )
+ printf( "fate change %s set to %s%s\n", t->name,
+ target_fate[ fate ], oldTimeStamp ? " (by timestamp)" : "" );
+ else
+ printf( "fate change %s from %s to %s%s\n", t->name,
+ target_fate[ savedFate ], target_fate[ fate ],
+ oldTimeStamp ? " (by timestamp)" : "" );
+ }
+#endif
+
+ /* Step 4e: handle missing files */
+ /* If it is missing and there are no actions to create it, boom. */
+ /* If we can not make a target we do not care about it, okay. */
+ /* We could insist that there are updating actions for all missing */
+ /* files, but if they have dependencies we just pretend it is a NOTFILE. */
+
+ if ( ( fate == T_FATE_MISSING ) && !t->actions && !t->depends )
+ {
+ if ( t->flags & T_FLAG_NOCARE )
+ {
+#ifdef OPT_GRAPH_DEBUG_EXT
+ if ( DEBUG_FATE )
+ printf( "fate change %s to STABLE from %s, "
+ "no actions, no dependencies and do not care\n",
+ t->name, target_fate[ fate ] );
+#endif
+ fate = T_FATE_STABLE;
+ }
+ else
+ {
+ printf( "don't know how to make %s\n", t->name );
+ fate = T_FATE_CANTFIND;
+ }
+ }
+
+ /* Step 4f: propagate dependencies' time & fate. */
+ /* Set leaf time to be our time only if this is a leaf. */
+
+ t->time = max( t->time, last );
+ t->leaf = leaf ? leaf : t->time ;
+ /* This target's fate may have been updated by virtue of following some
+ * target's rebuilds list, so only allow it to be increased to the fate we
+ * have calculated. Otherwise, grab its new fate.
+ */
+ if ( fate > t->fate )
+ t->fate = fate;
+ else
+ fate = t->fate;
+
+ /* Step 4g: if this target needs to be built, force rebuild everything in
+ * this target's rebuilds list.
+ */
+ if ( ( fate >= T_FATE_BUILD ) && ( fate < T_FATE_BROKEN ) )
+ force_rebuilds( t );
+
+ /*
+ * Step 5: sort dependencies by their update time.
+ */
+
+ if ( globs.newestfirst )
+ t->depends = make0sort( t->depends );
+
+ /*
+ * Step 6: a little harmless tabulating for tracing purposes
+ */
+
+ /* Do not count or report interal includes nodes. */
+ if ( t->flags & T_FLAG_INTERNAL )
+ return;
+
+ if ( counts )
+ {
+#ifdef OPT_IMPROVED_PATIENCE_EXT
+ ++counts->targets;
+#else
+ if ( !( ++counts->targets % 1000 ) && DEBUG_MAKE )
+ printf( "...patience...\n" );
+#endif
+
+ if ( fate == T_FATE_ISTMP )
+ ++counts->temp;
+ else if ( fate == T_FATE_CANTFIND )
+ ++counts->cantfind;
+ else if ( ( fate == T_FATE_CANTMAKE ) && t->actions )
+ ++counts->cantmake;
+ else if ( ( fate >= T_FATE_BUILD ) && ( fate < T_FATE_BROKEN ) &&
+ t->actions )
+ ++counts->updating;
+ }
+
+ if ( !( t->flags & T_FLAG_NOTFILE ) && ( fate >= T_FATE_SPOIL ) )
+ flag = "+";
+ else if ( ( t->binding == T_BIND_EXISTS ) && p && ( t->time > p->time ) )
+ flag = "*";
+
+ if ( DEBUG_MAKEPROG )
+ printf( "made%s\t%s\t%s%s\n", flag, target_fate[ (int) t->fate ],
+ spaces( depth ), t->name );
+}
+
+
+#ifdef OPT_GRAPH_DEBUG_EXT
+
+static const char * target_name( TARGET * t )
+{
+ static char buf[ 1000 ];
+ if ( t->flags & T_FLAG_INTERNAL )
+ {
+ sprintf( buf, "%s (internal node)", t->name );
+ return buf;
+ }
+ return t->name;
+}
+
+
+/*
+ * dependGraphOutput() - output the DG after make0 has run.
+ */
+
+static void dependGraphOutput( TARGET * t, int depth )
+{
+ TARGETS * c;
+
+ if ( ( t->flags & T_FLAG_VISITED ) || !t->name || !t->boundname )
+ return;
+
+ t->flags |= T_FLAG_VISITED;
+
+ switch ( t->fate )
+ {
+ case T_FATE_TOUCHED:
+ case T_FATE_MISSING:
+ case T_FATE_OUTDATED:
+ case T_FATE_UPDATE:
+ printf( "->%s%2d Name: %s\n", spaces( depth ), depth, target_name( t ) );
+ break;
+ default:
+ printf( " %s%2d Name: %s\n", spaces( depth ), depth, target_name( t ) );
+ break;
+ }
+
+ if ( strcmp( t->name, t->boundname ) )
+ printf( " %s Loc: %s\n", spaces( depth ), t->boundname );
+
+ switch ( t->fate )
+ {
+ case T_FATE_STABLE:
+ printf( " %s : Stable\n", spaces( depth ) );
+ break;
+ case T_FATE_NEWER:
+ printf( " %s : Newer\n", spaces( depth ) );
+ break;
+ case T_FATE_ISTMP:
+ printf( " %s : Up to date temp file\n", spaces( depth ) );
+ break;
+ case T_FATE_NEEDTMP:
+ printf( " %s : Temporary file, to be updated\n", spaces( depth ) );
+ break;
+ case T_FATE_TOUCHED:
+ printf( " %s : Been touched, updating it\n", spaces( depth ) );
+ break;
+ case T_FATE_MISSING:
+ printf( " %s : Missing, creating it\n", spaces( depth ) );
+ break;
+ case T_FATE_OUTDATED:
+ printf( " %s : Outdated, updating it\n", spaces( depth ) );
+ break;
+ case T_FATE_REBUILD:
+ printf( " %s : Rebuild, updating it\n", spaces( depth ) );
+ break;
+ case T_FATE_UPDATE:
+ printf( " %s : Updating it\n", spaces( depth ) );
+ break;
+ case T_FATE_CANTFIND:
+ printf( " %s : Can not find it\n", spaces( depth ) );
+ break;
+ case T_FATE_CANTMAKE:
+ printf( " %s : Can make it\n", spaces( depth ) );
+ break;
+ }
+
+ if ( t->flags & ~T_FLAG_VISITED )
+ {
+ printf( " %s : ", spaces( depth ) );
+ if ( t->flags & T_FLAG_TEMP ) printf( "TEMPORARY " );
+ if ( t->flags & T_FLAG_NOCARE ) printf( "NOCARE " );
+ if ( t->flags & T_FLAG_NOTFILE ) printf( "NOTFILE " );
+ if ( t->flags & T_FLAG_TOUCHED ) printf( "TOUCHED " );
+ if ( t->flags & T_FLAG_LEAVES ) printf( "LEAVES " );
+ if ( t->flags & T_FLAG_NOUPDATE ) printf( "NOUPDATE " );
+ printf( "\n" );
+ }
+
+ for ( c = t->depends; c; c = c->next )
+ {
+ printf( " %s : Depends on %s (%s)", spaces( depth ),
+ target_name( c->target ), target_fate[ (int) c->target->fate ] );
+ if ( c->target->time == t->time )
+ printf( " (max time)");
+ printf( "\n" );
+ }
+
+ for ( c = t->depends; c; c = c->next )
+ dependGraphOutput( c->target, depth + 1 );
+}
+#endif
+
+
+/*
+ * make0sort() - reorder TARGETS chain by their time (newest to oldest).
+ *
+ * We walk chain, taking each item and inserting it on the sorted result, with
+ * newest items at the front. This involves updating each of the TARGETS'
+ * c->next and c->tail. Note that we make c->tail a valid prev pointer for every
+ * entry. Normally, it is only valid at the head, where prev == tail. Note also
+ * that while tail is a loop, next ends at the end of the chain.
+ */
+
+static TARGETS * make0sort( TARGETS * chain )
+{
+ PROFILE_ENTER( MAKE_MAKE0SORT );
+
+ TARGETS * result = 0;
+
+ /* Walk the current target list. */
+ while ( chain )
+ {
+ TARGETS * c = chain;
+ TARGETS * s = result;
+
+ chain = chain->next;
+
+ /* Find point s in result for c. */
+ while ( s && ( s->target->time > c->target->time ) )
+ s = s->next;
+
+ /* Insert c in front of s (might be 0). Do not even think of deciphering
+ * this.
+ */
+ c->next = s; /* good even if s = 0 */
+ if ( result == s ) result = c; /* new head of chain? */
+ if ( !s ) s = result; /* wrap to ensure a next */
+ if ( result != c ) s->tail->next = c; /* not head? be prev's next */
+ c->tail = s->tail; /* take on next's prev */
+ s->tail = c; /* make next's prev us */
+ }
+
+ PROFILE_EXIT( MAKE_MAKE0SORT );
+ return result;
+}
+
+
+static LIST * targets_to_update_ = 0;
+
+
+void mark_target_for_updating( char * target )
+{
+ targets_to_update_ = list_new( targets_to_update_, target );
+}
+
+
+LIST * targets_to_update()
+{
+ return targets_to_update_;
+}
+
+
+void clear_targets_to_update()
+{
+ list_free( targets_to_update_ );
+ targets_to_update_ = 0;
+}
diff --git a/jam-files/engine/make.h b/jam-files/engine/make.h
new file mode 100644
index 00000000..b372263e
--- /dev/null
+++ b/jam-files/engine/make.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * make.h - bring a target up to date, once rules are in place
+ */
+
+#include "lists.h"
+
+int make( int n_targets, const char **targets, int anyhow );
+int make1( TARGET *t );
+
+typedef struct {
+ int temp;
+ int updating;
+ int cantfind;
+ int cantmake;
+ int targets;
+ int made;
+} COUNTS ;
+
+
+void make0( TARGET *t, TARGET *p, int depth,
+ COUNTS *counts, int anyhow );
+
+
+/*
+ * Specifies that the target should be updated.
+ */
+void mark_target_for_updating(char *target);
+/*
+ * Returns the list of all the target previously passed to 'mark_target_for_updating'.
+ */
+LIST *targets_to_update();
+/*
+ * Cleasr/unmarks all targets that are currently marked for update.
+ */
+void clear_targets_to_update();
diff --git a/jam-files/engine/make1.c b/jam-files/engine/make1.c
new file mode 100644
index 00000000..8001f333
--- /dev/null
+++ b/jam-files/engine/make1.c
@@ -0,0 +1,1145 @@
+/*
+ * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/* This file is ALSO:
+ * Copyright 2001-2004 David Abrahams.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+/*
+ * make1.c - execute command to bring targets up to date
+ *
+ * This module contains make1(), the entry point called by make() to
+ * recursively decend the dependency graph executing update actions as
+ * marked by make0().
+ *
+ * External routines:
+ *
+ * make1() - execute commands to update a TARGET and all of its dependencies.
+ *
+ * Internal routines, the recursive/asynchronous command executors:
+ *
+ * make1a() - recursively traverse dependency target tree, calling make1b().
+ * make1atail() - started processing all dependencies so go on to make1b().
+ * make1b() - when dependencies are up to date, build target with make1c().
+ * make1c() - launch target's next command, call parents' make1b() if none.
+ * make1d() - handle command execution completion and call back make1c().
+ *
+ * Internal support routines:
+ *
+ * make1cmds() - turn ACTIONS into CMDs, grouping, splitting, etc.
+ * make1list() - turn a list of targets into a LIST, for $(<) and $(>).
+ * make1settings() - for vars that get bound values, build up replacement lists.
+ * make1bind() - bind targets that weren't bound in dependency analysis.
+ *
+ * 04/16/94 (seiwald) - Split from make.c.
+ * 04/21/94 (seiwald) - Handle empty "updated" actions.
+ * 05/04/94 (seiwald) - async multiprocess (-j) support.
+ * 06/01/94 (seiwald) - new 'actions existing' does existing sources.
+ * 12/20/94 (seiwald) - NOTIME renamed NOTFILE.
+ * 01/19/95 (seiwald) - distinguish between CANTFIND/CANTMAKE targets.
+ * 01/22/94 (seiwald) - pass per-target JAMSHELL down to exec_cmd().
+ * 02/28/95 (seiwald) - Handle empty "existing" actions.
+ * 03/10/95 (seiwald) - Fancy counts.
+ */
+
+#include "jam.h"
+
+#include "lists.h"
+#include "parse.h"
+#include "assert.h"
+#include "variable.h"
+#include "rules.h"
+#include "headers.h"
+
+#include "search.h"
+#include "newstr.h"
+#include "make.h"
+#include "command.h"
+#include "execcmd.h"
+#include "compile.h"
+#include "output.h"
+
+#include <stdlib.h>
+
+#if ! defined(NT) || defined(__GNUC__)
+ #include <unistd.h> /* for unlink */
+#endif
+
+static CMD * make1cmds ( TARGET * );
+static LIST * make1list ( LIST *, TARGETS *, int flags );
+static SETTINGS * make1settings( LIST * vars );
+static void make1bind ( TARGET * );
+
+/* Ugly static - it is too hard to carry it through the callbacks. */
+
+static struct
+{
+ int failed;
+ int skipped;
+ int total;
+ int made;
+} counts[ 1 ] ;
+
+/* Target state - remove recursive calls by just keeping track of state target
+ * is in.
+ */
+typedef struct _state
+{
+ struct _state * prev; /* previous state on stack */
+ TARGET * t; /* current target */
+ TARGET * parent; /* parent argument necessary for make1a() */
+#define T_STATE_MAKE1A 0 /* make1a() should be called */
+#define T_STATE_MAKE1ATAIL 1 /* make1atail() should be called */
+#define T_STATE_MAKE1B 2 /* make1b() should be called */
+#define T_STATE_MAKE1C 3 /* make1c() should be called */
+#define T_STATE_MAKE1D 4 /* make1d() should be called */
+ int curstate; /* current state */
+ int status;
+} state;
+
+static void make1a ( state * );
+static void make1atail ( state * );
+static void make1b ( state * );
+static void make1c ( state * );
+static void make1d ( state * );
+static void make_closure( void * closure, int status, timing_info *, char *, char * );
+
+typedef struct _stack
+{
+ state * stack;
+} stack;
+
+static stack state_stack = { NULL };
+
+static state * state_freelist = NULL;
+
+
+static state * alloc_state()
+{
+ if ( state_freelist != NULL )
+ {
+ state * pState = state_freelist;
+ state_freelist = pState->prev;
+ memset( pState, 0, sizeof( state ) );
+ return pState;
+ }
+
+ return (state *)BJAM_MALLOC( sizeof( state ) );
+}
+
+
+static void free_state( state * pState )
+{
+ pState->prev = state_freelist;
+ state_freelist = pState;
+}
+
+
+static void clear_state_freelist()
+{
+ while ( state_freelist != NULL )
+ {
+ state * pState = state_freelist;
+ state_freelist = state_freelist->prev;
+ BJAM_FREE( pState );
+ }
+}
+
+
+static state * current_state( stack * pStack )
+{
+ return pStack->stack;
+}
+
+
+static void pop_state( stack * pStack )
+{
+ if ( pStack->stack != NULL )
+ {
+ state * pState = pStack->stack->prev;
+ free_state( pStack->stack );
+ pStack->stack = pState;
+ }
+}
+
+
+static state * push_state( stack * pStack, TARGET * t, TARGET * parent, int curstate )
+{
+ state * pState = alloc_state();
+
+ pState->t = t;
+ pState->parent = parent;
+ pState->prev = pStack->stack;
+ pState->curstate = curstate;
+
+ pStack->stack = pState;
+
+ return pStack->stack;
+}
+
+
+/*
+ * Pushes a stack onto another stack, effectively reversing the order.
+ */
+
+static void push_stack_on_stack( stack * pDest, stack * pSrc )
+{
+ while ( pSrc->stack != NULL )
+ {
+ state * pState = pSrc->stack;
+ pSrc->stack = pSrc->stack->prev;
+ pState->prev = pDest->stack;
+ pDest->stack = pState;
+ }
+}
+
+
+/*
+ * make1() - execute commands to update a TARGET and all of its dependencies.
+ */
+
+static int intr = 0;
+
+int make1( TARGET * t )
+{
+ state * pState;
+
+ memset( (char *)counts, 0, sizeof( *counts ) );
+
+ /* Recursively make the target and its dependencies. */
+ push_state( &state_stack, t, NULL, T_STATE_MAKE1A );
+
+ do
+ {
+ while ( ( pState = current_state( &state_stack ) ) != NULL )
+ {
+ if ( intr )
+ pop_state( &state_stack );
+
+ switch ( pState->curstate )
+ {
+ case T_STATE_MAKE1A : make1a ( pState ); break;
+ case T_STATE_MAKE1ATAIL: make1atail( pState ); break;
+ case T_STATE_MAKE1B : make1b ( pState ); break;
+ case T_STATE_MAKE1C : make1c ( pState ); break;
+ case T_STATE_MAKE1D : make1d ( pState ); break;
+ }
+ }
+ }
+ /* Wait for any outstanding commands to finish running. */
+ while ( exec_wait() );
+
+ clear_state_freelist();
+
+ /* Talk about it. */
+ if ( counts->failed )
+ printf( "...failed updating %d target%s...\n", counts->failed,
+ counts->failed > 1 ? "s" : "" );
+ if ( DEBUG_MAKE && counts->skipped )
+ printf( "...skipped %d target%s...\n", counts->skipped,
+ counts->skipped > 1 ? "s" : "" );
+ if ( DEBUG_MAKE && counts->made )
+ printf( "...updated %d target%s...\n", counts->made,
+ counts->made > 1 ? "s" : "" );
+
+ return counts->total != counts->made;
+}
+
+
+/*
+ * make1a() - recursively traverse target tree, calling make1b().
+ *
+ * Called to start processing a specified target. Does nothing if the target is
+ * already being processed or otherwise starts processing all of its
+ * dependencies. Once all of its dependencies have started being processed goes
+ * on and calls make1b() (actually does that indirectly via a helper
+ * make1atail() state).
+ */
+
+static void make1a( state * pState )
+{
+ TARGET * t = pState->t;
+ TARGETS * c;
+
+ /* If the parent is the first to try to build this target or this target is
+ * in the make1c() quagmire, arrange for the parent to be notified when this
+ * target is built.
+ */
+ if ( pState->parent )
+ switch ( pState->t->progress )
+ {
+ case T_MAKE_INIT:
+ case T_MAKE_ACTIVE:
+ case T_MAKE_RUNNING:
+ pState->t->parents = targetentry( pState->t->parents,
+ pState->parent );
+ ++pState->parent->asynccnt;
+ }
+
+ /* If this target is already being processed then do nothing. There is no
+ * need to start processing the same target all over again.
+ */
+ if ( pState->t->progress != T_MAKE_INIT )
+ {
+ pop_state( &state_stack );
+ return;
+ }
+
+ /* Asynccnt counts the dependencies preventing this target from proceeding
+ * to make1b() for actual building. We start off with a count of 1 to
+ * prevent anything from happening until we can notify all dependencies that
+ * they are needed. This 1 is accounted for when we call make1b() ourselves,
+ * below. Without this if a a dependency gets built before we finish
+ * processing all of our other dependencies our build might be triggerred
+ * prematurely.
+ */
+ pState->t->asynccnt = 1;
+
+ /* Add header nodes created during the building process. */
+ {
+ TARGETS * inc = 0;
+ for ( c = t->depends; c; c = c->next )
+ if ( c->target->rescanned && c->target->includes )
+ inc = targetentry( inc, c->target->includes );
+ t->depends = targetchain( t->depends, inc );
+ }
+
+ /* Guard against circular dependencies. */
+ pState->t->progress = T_MAKE_ONSTACK;
+
+ {
+ stack temp_stack = { NULL };
+ for ( c = t->depends; c && !intr; c = c->next )
+ push_state( &temp_stack, c->target, pState->t, T_STATE_MAKE1A );
+
+ /* Using stacks reverses the order of execution. Reverse it back. */
+ push_stack_on_stack( &state_stack, &temp_stack );
+ }
+
+ pState->curstate = T_STATE_MAKE1ATAIL;
+}
+
+
+/*
+ * make1atail() - started processing all dependencies so go on to make1b().
+ */
+
+static void make1atail( state * pState )
+{
+ pState->t->progress = T_MAKE_ACTIVE;
+ /* Now that all of our dependencies have bumped up our asynccnt we can
+ * remove our own internal bump added to prevent this target from being
+ * built before all of its dependencies start getting processed.
+ */
+ pState->curstate = T_STATE_MAKE1B;
+}
+
+
+/*
+ * make1b() - when dependencies are up to date, build target with make1c().
+ *
+ * Called after all dependencies have started being processed and after each of
+ * them finishes its processing. The target actually goes on to getting built in
+ * make1c() only after all of its dependencies have finished their processing.
+ */
+
+static void make1b( state * pState )
+{
+ TARGET * t = pState->t;
+ TARGETS * c;
+ TARGET * failed = 0;
+ char * failed_name = "dependencies";
+
+ /* If any dependencies are still outstanding, wait until they call make1b()
+ * to signal their completion.
+ */
+ if ( --pState->t->asynccnt )
+ {
+ pop_state( &state_stack );
+ return;
+ }
+
+ /* Try to aquire a semaphore. If it is locked, wait until the target that
+ * locked it is built and signal completition.
+ */
+#ifdef OPT_SEMAPHORE
+ if ( t->semaphore && t->semaphore->asynccnt )
+ {
+ /* Append 't' to the list of targets waiting on semaphore. */
+ t->semaphore->parents = targetentry( t->semaphore->parents, t );
+ t->asynccnt++;
+
+ if ( DEBUG_EXECCMD )
+ printf( "SEM: %s is busy, delaying launch of %s\n",
+ t->semaphore->name, t->name );
+ pop_state( &state_stack );
+ return;
+ }
+#endif
+
+ /* Now ready to build target 't', if dependencies built OK. */
+
+ /* Collect status from dependencies. */
+ for ( c = t->depends; c; c = c->next )
+ if ( c->target->status > t->status && !( c->target->flags & T_FLAG_NOCARE ) )
+ {
+ failed = c->target;
+ pState->t->status = c->target->status;
+ }
+ /* If an internal header node failed to build, we want to output the target
+ * that it failed on.
+ */
+ if ( failed )
+ {
+ failed_name = failed->flags & T_FLAG_INTERNAL
+ ? failed->failed
+ : failed->name;
+ }
+ t->failed = failed_name;
+
+ /* If actions for building any of the dependencies have failed, bail.
+ * Otherwise, execute all actions to make the current target.
+ */
+ if ( ( pState->t->status == EXEC_CMD_FAIL ) && pState->t->actions )
+ {
+ ++counts->skipped;
+ if ( ( pState->t->flags & ( T_FLAG_RMOLD | T_FLAG_NOTFILE ) ) == T_FLAG_RMOLD )
+ {
+ if ( !unlink( pState->t->boundname ) )
+ printf( "...removing outdated %s\n", pState->t->boundname );
+ }
+ else
+ printf( "...skipped %s for lack of %s...\n", pState->t->name, failed_name );
+ }
+
+ if ( pState->t->status == EXEC_CMD_OK )
+ switch ( pState->t->fate )
+ {
+ /* These are handled by the default case below now
+ case T_FATE_INIT:
+ case T_FATE_MAKING:
+ */
+
+ case T_FATE_STABLE:
+ case T_FATE_NEWER:
+ break;
+
+ case T_FATE_CANTFIND:
+ case T_FATE_CANTMAKE:
+ pState->t->status = EXEC_CMD_FAIL;
+ break;
+
+ case T_FATE_ISTMP:
+ if ( DEBUG_MAKE )
+ printf( "...using %s...\n", pState->t->name );
+ break;
+
+ case T_FATE_TOUCHED:
+ case T_FATE_MISSING:
+ case T_FATE_NEEDTMP:
+ case T_FATE_OUTDATED:
+ case T_FATE_UPDATE:
+ case T_FATE_REBUILD:
+ /* Prepare commands for executing actions scheduled for this target
+ * and then schedule transfer to make1c() state to proceed with
+ * executing the prepared commands. Commands have their embedded
+ * variables automatically expanded, including making use of any "on
+ * target" variables.
+ */
+ if ( pState->t->actions )
+ {
+ ++counts->total;
+ if ( DEBUG_MAKE && !( counts->total % 100 ) )
+ printf( "...on %dth target...\n", counts->total );
+
+ pState->t->cmds = (char *)make1cmds( pState->t );
+ /* Set the target's "progress" so that make1c() counts it among
+ * its successes/failures.
+ */
+ pState->t->progress = T_MAKE_RUNNING;
+ }
+ break;
+
+ /* All possible fates should have been accounted for by now. */
+ default:
+ printf( "ERROR: %s has bad fate %d", pState->t->name,
+ pState->t->fate );
+ abort();
+ }
+
+ /* Call make1c() to begin the execution of the chain of commands needed to
+ * build the target. If we are not going to build the target (due of
+ * dependency failures or no commands needing to be run) the chain will be
+ * empty and make1c() will directly signal the target's completion.
+ */
+
+#ifdef OPT_SEMAPHORE
+ /* If there is a semaphore, indicate that it is in use. */
+ if ( pState->t->semaphore )
+ {
+ ++pState->t->semaphore->asynccnt;
+ if ( DEBUG_EXECCMD )
+ printf( "SEM: %s now used by %s\n", pState->t->semaphore->name,
+ pState->t->name );
+ }
+#endif
+
+ pState->curstate = T_STATE_MAKE1C;
+}
+
+
+/*
+ * make1c() - launch target's next command, call parents' make1b() if none.
+ *
+ * If there are (more) commands to run to build this target (and we have not hit
+ * an error running earlier comands) we launch the command using exec_cmd(). If
+ * there are no more commands to run, we collect the status from all the actions
+ * and report our completion to all the parents.
+ */
+
+static void make1c( state * pState )
+{
+ CMD * cmd = (CMD *)pState->t->cmds;
+
+ if ( cmd && ( pState->t->status == EXEC_CMD_OK ) )
+ {
+ char * rule_name = 0;
+ char * target = 0;
+
+ if ( DEBUG_MAKEQ ||
+ ( !( cmd->rule->actions->flags & RULE_QUIETLY ) && DEBUG_MAKE ) )
+ {
+ rule_name = cmd->rule->name;
+ target = lol_get( &cmd->args, 0 )->string;
+ if ( globs.noexec )
+ out_action( rule_name, target, cmd->buf, "", "", EXIT_OK );
+ }
+
+ if ( globs.noexec )
+ {
+ pState->curstate = T_STATE_MAKE1D;
+ pState->status = EXEC_CMD_OK;
+ }
+ else
+ {
+ /* Pop state first because exec_cmd() could push state. */
+ pop_state( &state_stack );
+ exec_cmd( cmd->buf, make_closure, pState->t, cmd->shell, rule_name,
+ target );
+ }
+ }
+ else
+ {
+ TARGETS * c;
+ ACTIONS * actions;
+
+ /* Collect status from actions, and distribute it as well. */
+ for ( actions = pState->t->actions; actions; actions = actions->next )
+ if ( actions->action->status > pState->t->status )
+ pState->t->status = actions->action->status;
+ for ( actions = pState->t->actions; actions; actions = actions->next )
+ if ( pState->t->status > actions->action->status )
+ actions->action->status = pState->t->status;
+
+ /* Tally success/failure for those we tried to update. */
+ if ( pState->t->progress == T_MAKE_RUNNING )
+ switch ( pState->t->status )
+ {
+ case EXEC_CMD_OK : ++counts->made ; break;
+ case EXEC_CMD_FAIL: ++counts->failed; break;
+ }
+
+ /* Tell parents their dependency has been built. */
+ {
+ stack temp_stack = { NULL };
+ TARGET * t = pState->t;
+ TARGET * additional_includes = NULL;
+
+ t->progress = T_MAKE_DONE;
+
+ /* Target has been updated so rescan it for dependencies. */
+ if ( ( t->fate >= T_FATE_MISSING ) &&
+ ( t->status == EXEC_CMD_OK ) &&
+ !t->rescanned )
+ {
+ TARGET * target_to_rescan = t;
+ SETTINGS * s;
+
+ target_to_rescan->rescanned = 1;
+
+ if ( target_to_rescan->flags & T_FLAG_INTERNAL )
+ target_to_rescan = t->original_target;
+
+ /* Clean current includes. */
+ target_to_rescan->includes = 0;
+
+ s = copysettings( target_to_rescan->settings );
+ pushsettings( s );
+ headers( target_to_rescan );
+ popsettings( s );
+ freesettings( s );
+
+ if ( target_to_rescan->includes )
+ {
+ target_to_rescan->includes->rescanned = 1;
+ /* Tricky. The parents have already been processed, but they
+ * have not seen the internal node, because it was just
+ * created. We need to make the calls to make1a() that would
+ * have been made by the parents here, and also make sure
+ * all unprocessed parents will pick up the includes. We
+ * must make sure processing of the additional make1a()
+ * invocations is done before make1b() which means this
+ * target is built, otherwise the parent would be considered
+ * built before this make1a() processing has even started.
+ */
+ make0( target_to_rescan->includes, target_to_rescan->parents->target, 0, 0, 0 );
+ for ( c = target_to_rescan->parents; c; c = c->next )
+ c->target->depends = targetentry( c->target->depends,
+ target_to_rescan->includes );
+ /* Will be processed below. */
+ additional_includes = target_to_rescan->includes;
+ }
+ }
+
+ if ( additional_includes )
+ for ( c = t->parents; c; c = c->next )
+ push_state( &temp_stack, additional_includes, c->target, T_STATE_MAKE1A );
+
+ for ( c = t->parents; c; c = c->next )
+ push_state( &temp_stack, c->target, NULL, T_STATE_MAKE1B );
+
+#ifdef OPT_SEMAPHORE
+ /* If there is a semaphore, it is now free. */
+ if ( t->semaphore )
+ {
+ assert( t->semaphore->asynccnt == 1 );
+ --t->semaphore->asynccnt;
+
+ if ( DEBUG_EXECCMD )
+ printf( "SEM: %s is now free\n", t->semaphore->name );
+
+ /* If anything is waiting, notify the next target. There is no
+ * point in notifying waiting targets, since they will be
+ * notified again.
+ */
+ if ( t->semaphore->parents )
+ {
+ TARGETS * first = t->semaphore->parents;
+ if ( first->next )
+ first->next->tail = first->tail;
+ t->semaphore->parents = first->next;
+
+ if ( DEBUG_EXECCMD )
+ printf( "SEM: placing %s on stack\n", first->target->name );
+ push_state( &temp_stack, first->target, NULL, T_STATE_MAKE1B );
+ BJAM_FREE( first );
+ }
+ }
+#endif
+
+ /* Must pop state before pushing any more. */
+ pop_state( &state_stack );
+
+ /* Using stacks reverses the order of execution. Reverse it back. */
+ push_stack_on_stack( &state_stack, &temp_stack );
+ }
+ }
+}
+
+
+/*
+ * call_timing_rule() - Look up the __TIMING_RULE__ variable on the given
+ * target, and if non-empty, invoke the rule it names, passing the given
+ * timing_info.
+ */
+
+static void call_timing_rule( TARGET * target, timing_info * time )
+{
+ LIST * timing_rule;
+
+ pushsettings( target->settings );
+ timing_rule = var_get( "__TIMING_RULE__" );
+ popsettings( target->settings );
+
+ if ( timing_rule )
+ {
+ /* rule timing-rule ( args * : target : start end user system ) */
+
+ /* Prepare the argument list. */
+ FRAME frame[ 1 ];
+ frame_init( frame );
+
+ /* args * :: $(__TIMING_RULE__[2-]) */
+ lol_add( frame->args, list_copy( L0, timing_rule->next ) );
+
+ /* target :: the name of the target */
+ lol_add( frame->args, list_new( L0, target->name ) );
+
+ /* start end user system :: info about the action command */
+ lol_add( frame->args, list_new( list_new( list_new( list_new( L0,
+ outf_time ( time->start ) ),
+ outf_time ( time->end ) ),
+ outf_double( time->user ) ),
+ outf_double( time->system ) ) );
+
+ /* Call the rule. */
+ evaluate_rule( timing_rule->string, frame );
+
+ /* Clean up. */
+ frame_free( frame );
+ }
+}
+
+
+/*
+ * call_action_rule() - Look up the __ACTION_RULE__ variable on the given
+ * target, and if non-empty, invoke the rule it names, passing the given info,
+ * timing_info, executed command and command output.
+ */
+
+static void call_action_rule
+(
+ TARGET * target,
+ int status,
+ timing_info * time,
+ char * executed_command,
+ char * command_output
+)
+{
+ LIST * action_rule;
+
+ pushsettings( target->settings );
+ action_rule = var_get( "__ACTION_RULE__" );
+ popsettings( target->settings );
+
+ if ( action_rule )
+ {
+ /* rule action-rule (
+ args * :
+ target :
+ command status start end user system :
+ output ? ) */
+
+ /* Prepare the argument list. */
+ FRAME frame[ 1 ];
+ frame_init( frame );
+
+ /* args * :: $(__ACTION_RULE__[2-]) */
+ lol_add( frame->args, list_copy( L0, action_rule->next ) );
+
+ /* target :: the name of the target */
+ lol_add( frame->args, list_new( L0, target->name ) );
+
+ /* command status start end user system :: info about the action command */
+ lol_add( frame->args,
+ list_new( list_new( list_new( list_new( list_new( list_new( L0,
+ newstr( executed_command ) ),
+ outf_int( status ) ),
+ outf_time( time->start ) ),
+ outf_time( time->end ) ),
+ outf_double( time->user ) ),
+ outf_double( time->system ) ) );
+
+ /* output ? :: the output of the action command */
+ if ( command_output )
+ lol_add( frame->args, list_new( L0, newstr( command_output ) ) );
+ else
+ lol_add( frame->args, L0 );
+
+ /* Call the rule. */
+ evaluate_rule( action_rule->string, frame );
+
+ /* Clean up. */
+ frame_free( frame );
+ }
+}
+
+
+/*
+ * make_closure() - internal function passed as a notification callback for when
+ * commands finish getting executed by the OS.
+ */
+
+static void make_closure
+(
+ void * closure,
+ int status,
+ timing_info * time,
+ char * executed_command,
+ char * command_output
+)
+{
+ TARGET * built = (TARGET *)closure;
+
+ call_timing_rule( built, time );
+ if ( DEBUG_EXECCMD )
+ printf( "%f sec system; %f sec user\n", time->system, time->user );
+
+ call_action_rule( built, status, time, executed_command, command_output );
+
+ push_state( &state_stack, built, NULL, T_STATE_MAKE1D )->status = status;
+}
+
+
+/*
+ * make1d() - handle command execution completion and call back make1c().
+ *
+ * exec_cmd() has completed and now all we need to do is fiddle with the status
+ * and call back to make1c() so it can run the next command scheduled for
+ * building this target or close up the target's build process in case there are
+ * no more commands scheduled for it. On interrupts, we bail heavily.
+ */
+
+static void make1d( state * pState )
+{
+ TARGET * t = pState->t;
+ CMD * cmd = (CMD *)t->cmds;
+ int status = pState->status;
+
+ if ( t->flags & T_FLAG_FAIL_EXPECTED )
+ {
+ /* Invert execution result when FAIL_EXPECTED has been applied. */
+ switch ( status )
+ {
+ case EXEC_CMD_FAIL: status = EXEC_CMD_OK ; break;
+ case EXEC_CMD_OK: status = EXEC_CMD_FAIL; break;
+ }
+ }
+
+ if ( ( status == EXEC_CMD_FAIL ) &&
+ ( cmd->rule->actions->flags & RULE_IGNORE ) )
+ status = EXEC_CMD_OK;
+
+ /* On interrupt, set intr so _everything_ fails. */
+ if ( status == EXEC_CMD_INTR )
+ ++intr;
+
+ /* Print command text on failure. */
+ if ( ( status == EXEC_CMD_FAIL ) && DEBUG_MAKE )
+ {
+ if ( !DEBUG_EXEC )
+ printf( "%s\n", cmd->buf );
+
+ printf( "...failed %s ", cmd->rule->name );
+ list_print( lol_get( &cmd->args, 0 ) );
+ printf( "...\n" );
+ }
+
+ /* Treat failed commands as interrupts in case we were asked to stop the
+ * build in case of any errors.
+ */
+ if ( ( status == EXEC_CMD_FAIL ) && globs.quitquick )
+ ++intr;
+
+ /* If the command was interrupted or failed and the target is not
+ * "precious", remove the targets.
+ */
+ if (status != EXEC_CMD_OK)
+ {
+ LIST * targets = lol_get( &cmd->args, 0 );
+ for ( ; targets; targets = list_next( targets ) )
+ {
+ int need_unlink = 1;
+ TARGET* t = bindtarget ( targets->string );
+ if (t->flags & T_FLAG_PRECIOUS)
+ {
+ need_unlink = 0;
+ }
+ if (need_unlink && !unlink( targets->string ) )
+ printf( "...removing %s\n", targets->string );
+ }
+ }
+
+ /* Free this command and call make1c() to move onto the next one scheduled
+ * for building this same target.
+ */
+ t->status = status;
+ t->cmds = (char *)cmd_next( cmd );
+ cmd_free( cmd );
+ pState->curstate = T_STATE_MAKE1C;
+}
+
+
+/*
+ * swap_settings() - replace the settings from the current module and target
+ * with those from the new module and target
+ */
+
+static void swap_settings
+(
+ module_t * * current_module,
+ TARGET * * current_target,
+ module_t * new_module,
+ TARGET * new_target
+)
+{
+ if ( new_module == root_module() )
+ new_module = 0;
+
+ if ( ( new_target == *current_target ) && ( new_module == *current_module ) )
+ return;
+
+ if ( *current_target )
+ popsettings( (*current_target)->settings );
+
+ if ( new_module != *current_module )
+ {
+ if ( *current_module )
+ exit_module( *current_module );
+
+ *current_module = new_module;
+
+ if ( new_module )
+ enter_module( new_module );
+ }
+
+ *current_target = new_target;
+ if ( new_target )
+ pushsettings( new_target->settings );
+}
+
+
+/*
+ * make1cmds() - turn ACTIONS into CMDs, grouping, splitting, etc.
+ *
+ * Essentially copies a chain of ACTIONs to a chain of CMDs, grouping
+ * RULE_TOGETHER actions, splitting RULE_PIECEMEAL actions, and handling
+ * RULE_NEWSRCS actions. The result is a chain of CMDs which can be expanded by
+ * var_string() and executed using exec_cmd().
+ */
+
+static CMD * make1cmds( TARGET * t )
+{
+ CMD * cmds = 0;
+ LIST * shell = 0;
+ module_t * settings_module = 0;
+ TARGET * settings_target = 0;
+ ACTIONS * a0;
+
+ /* Step through actions. Actions may be shared with other targets or grouped
+ * using RULE_TOGETHER, so actions already seen are skipped.
+ */
+ for ( a0 = t->actions ; a0; a0 = a0->next )
+ {
+ RULE * rule = a0->action->rule;
+ rule_actions * actions = rule->actions;
+ SETTINGS * boundvars;
+ LIST * nt;
+ LIST * ns;
+ ACTIONS * a1;
+ int start;
+ int chunk;
+ int length;
+
+ /* Only do rules with commands to execute. If this action has already
+ * been executed, use saved status.
+ */
+ if ( !actions || a0->action->running )
+ continue;
+
+ a0->action->running = 1;
+
+ /* Make LISTS of targets and sources. If `execute together` has been
+ * specified for this rule, tack on sources from each instance of this
+ * rule for this target.
+ */
+ nt = make1list( L0, a0->action->targets, 0 );
+ ns = make1list( L0, a0->action->sources, actions->flags );
+ if ( actions->flags & RULE_TOGETHER )
+ for ( a1 = a0->next; a1; a1 = a1->next )
+ if ( a1->action->rule == rule && !a1->action->running )
+ {
+ ns = make1list( ns, a1->action->sources, actions->flags );
+ a1->action->running = 1;
+ }
+
+ /* If doing only updated (or existing) sources, but none have been
+ * updated (or exist), skip this action.
+ */
+ if ( !ns && ( actions->flags & ( RULE_NEWSRCS | RULE_EXISTING ) ) )
+ {
+ list_free( nt );
+ continue;
+ }
+
+ swap_settings( &settings_module, &settings_target, rule->module, t );
+ if ( !shell )
+ shell = var_get( "JAMSHELL" ); /* shell is per-target */
+
+ /* If we had 'actions xxx bind vars' we bind the vars now. */
+ boundvars = make1settings( actions->bindlist );
+ pushsettings( boundvars );
+
+ /*
+ * Build command, starting with all source args.
+ *
+ * If cmd_new returns 0, it is because the resulting command length is
+ * > MAXLINE. In this case, we will slowly reduce the number of source
+ * arguments presented until it does fit. This only applies to actions
+ * that allow PIECEMEAL commands.
+ *
+ * While reducing slowly takes a bit of compute time to get things just
+ * right, it is worth it to get as close to MAXLINE as possible, because
+ * launching the commands we are executing is likely to be much more
+ * compute intensive.
+ *
+ * Note we loop through at least once, for sourceless actions.
+ */
+
+ start = 0;
+ chunk = length = list_length( ns );
+
+ do
+ {
+ /* Build cmd: cmd_new consumes its lists. */
+ CMD * cmd = cmd_new( rule,
+ list_copy( L0, nt ),
+ list_sublist( ns, start, chunk ),
+ list_copy( L0, shell ) );
+
+ if ( cmd )
+ {
+ /* It fit: chain it up. */
+ if ( !cmds ) cmds = cmd;
+ else cmds->tail->next = cmd;
+ cmds->tail = cmd;
+ start += chunk;
+ }
+ else if ( ( actions->flags & RULE_PIECEMEAL ) && ( chunk > 1 ) )
+ {
+ /* Reduce chunk size slowly. */
+ chunk = chunk * 9 / 10;
+ }
+ else
+ {
+ /* Too long and not splittable. */
+ printf( "%s actions too long (max %d):\n", rule->name, MAXLINE
+ );
+
+ /* Tell the user what didn't fit. */
+ cmd = cmd_new( rule, list_copy( L0, nt ),
+ list_sublist( ns, start, chunk ),
+ list_new( L0, newstr( "%" ) ) );
+ fputs( cmd->buf, stdout );
+ exit( EXITBAD );
+ }
+ }
+ while ( start < length );
+
+ /* These were always copied when used. */
+ list_free( nt );
+ list_free( ns );
+
+ /* Free the variables whose values were bound by 'actions xxx bind
+ * vars'.
+ */
+ popsettings( boundvars );
+ freesettings( boundvars );
+ }
+
+ swap_settings( &settings_module, &settings_target, 0, 0 );
+ return cmds;
+}
+
+
+/*
+ * make1list() - turn a list of targets into a LIST, for $(<) and $(>).
+ */
+
+static LIST * make1list( LIST * l, TARGETS * targets, int flags )
+{
+ for ( ; targets; targets = targets->next )
+ {
+ TARGET * t = targets->target;
+
+ if ( t->binding == T_BIND_UNBOUND )
+ make1bind( t );
+
+ if ( ( flags & RULE_EXISTING ) && ( flags & RULE_NEWSRCS ) )
+ {
+ if ( ( t->binding != T_BIND_EXISTS ) && ( t->fate <= T_FATE_STABLE ) )
+ continue;
+ }
+ else
+ {
+ if ( ( flags & RULE_EXISTING ) && ( t->binding != T_BIND_EXISTS ) )
+ continue;
+
+ if ( ( flags & RULE_NEWSRCS ) && ( t->fate <= T_FATE_STABLE ) )
+ continue;
+ }
+
+ /* Prohibit duplicates for RULE_TOGETHER. */
+ if ( flags & RULE_TOGETHER )
+ {
+ LIST * m;
+ for ( m = l; m; m = m->next )
+ if ( !strcmp( m->string, t->boundname ) )
+ break;
+ if ( m )
+ continue;
+ }
+
+ /* Build new list. */
+ l = list_new( l, copystr( t->boundname ) );
+ }
+
+ return l;
+}
+
+
+/*
+ * make1settings() - for vars that get bound values, build up replacement lists.
+ */
+
+static SETTINGS * make1settings( LIST * vars )
+{
+ SETTINGS * settings = 0;
+
+ for ( ; vars; vars = list_next( vars ) )
+ {
+ LIST * l = var_get( vars->string );
+ LIST * nl = 0;
+
+ for ( ; l; l = list_next( l ) )
+ {
+ TARGET * t = bindtarget( l->string );
+
+ /* Make sure the target is bound. */
+ if ( t->binding == T_BIND_UNBOUND )
+ make1bind( t );
+
+ /* Build a new list. */
+ nl = list_new( nl, copystr( t->boundname ) );
+ }
+
+ /* Add to settings chain. */
+ settings = addsettings( settings, VAR_SET, vars->string, nl );
+ }
+
+ return settings;
+}
+
+
+/*
+ * make1bind() - bind targets that were not bound during dependency analysis
+ *
+ * Spot the kludge! If a target is not in the dependency tree, it did not get
+ * bound by make0(), so we have to do it here. Ugly.
+ */
+
+static void make1bind( TARGET * t )
+{
+ if ( t->flags & T_FLAG_NOTFILE )
+ return;
+
+ pushsettings( t->settings );
+ t->boundname = search( t->name, &t->time, 0, ( t->flags & T_FLAG_ISFILE ) );
+ t->binding = t->time ? T_BIND_EXISTS : T_BIND_MISSING;
+ popsettings( t->settings );
+}
diff --git a/jam-files/engine/md5.c b/jam-files/engine/md5.c
new file mode 100644
index 00000000..c35d96c5
--- /dev/null
+++ b/jam-files/engine/md5.c
@@ -0,0 +1,381 @@
+/*
+ Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
+ text is available at
+ http://www.ietf.org/rfc/rfc1321.txt
+ The code is derived from the text of the RFC, including the test suite
+ (section A.5) but excluding the rest of Appendix A. It does not include
+ any code or documentation that is identified in the RFC as being
+ copyrighted.
+
+ The original and principal author of md5.c is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
+ either statically or dynamically; added missing #include <string.h>
+ in library.
+ 2002-03-11 lpd Corrected argument list for main(), and added int return
+ type, in test program and T value program.
+ 2002-02-21 lpd Added missing #include <stdio.h> in test program.
+ 2000-07-03 lpd Patched to eliminate warnings about "constant is
+ unsigned in ANSI C, signed in traditional"; made test program
+ self-checking.
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+ 1999-05-03 lpd Original version.
+ */
+
+#include "md5.h"
+#include <string.h>
+
+#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
+#ifdef ARCH_IS_BIG_ENDIAN
+# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
+#else
+# define BYTE_ORDER 0
+#endif
+
+#define T_MASK ((md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3 0x242070db
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6 0x4787c62a
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#define T9 0x698098d8
+#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
+#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
+#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
+#define T13 0x6b901122
+#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16 0x49b40821
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19 0x265e5a51
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22 0x02441453
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25 0x21e1cde6
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28 0x455a14ed
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#define T31 0x676f02d9
+#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
+#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
+#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
+#define T35 0x6d9d6122
+#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38 0x4bdecfa9
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41 0x289b7ec6
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44 0x04881d05
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47 0x1fa27cf8
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50 0x432aff97
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#define T53 0x655b59c3
+#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
+#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
+#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
+#define T57 0x6fa87e4f
+#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60 0x4e0811a1
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63 0x2ad7d2bb
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+ md5_word_t
+ a = pms->abcd[0], b = pms->abcd[1],
+ c = pms->abcd[2], d = pms->abcd[3];
+ md5_word_t t;
+#if BYTE_ORDER > 0
+ /* Define storage only for big-endian CPUs. */
+ md5_word_t X[16];
+#else
+ /* Define storage for little-endian or both types of CPUs. */
+ md5_word_t xbuf[16];
+ const md5_word_t *X;
+#endif
+
+ {
+#if BYTE_ORDER == 0
+ /*
+ * Determine dynamically whether this is a big-endian or
+ * little-endian machine, since we can use a more efficient
+ * algorithm on the latter.
+ */
+ static const int w = 1;
+
+ if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
+#endif
+#if BYTE_ORDER <= 0 /* little-endian */
+ {
+ /*
+ * On little-endian machines, we can process properly aligned
+ * data without copying it.
+ */
+ if (!((data - (const md5_byte_t *)0) & 3)) {
+ /* data are properly aligned */
+ X = (const md5_word_t *)data;
+ } else {
+ /* not aligned */
+ memcpy(xbuf, data, 64);
+ X = xbuf;
+ }
+ }
+#endif
+#if BYTE_ORDER == 0
+ else /* dynamic big-endian */
+#endif
+#if BYTE_ORDER >= 0 /* big-endian */
+ {
+ /*
+ * On big-endian machines, we must arrange the bytes in the
+ * right order.
+ */
+ const md5_byte_t *xp = data;
+ int i;
+
+# if BYTE_ORDER == 0
+ X = xbuf; /* (dynamic only) */
+# else
+# define xbuf X /* (static only) */
+# endif
+ for (i = 0; i < 16; ++i, xp += 4)
+ xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+ }
+#endif
+ }
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+ /* Round 1. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + F(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 7, T1);
+ SET(d, a, b, c, 1, 12, T2);
+ SET(c, d, a, b, 2, 17, T3);
+ SET(b, c, d, a, 3, 22, T4);
+ SET(a, b, c, d, 4, 7, T5);
+ SET(d, a, b, c, 5, 12, T6);
+ SET(c, d, a, b, 6, 17, T7);
+ SET(b, c, d, a, 7, 22, T8);
+ SET(a, b, c, d, 8, 7, T9);
+ SET(d, a, b, c, 9, 12, T10);
+ SET(c, d, a, b, 10, 17, T11);
+ SET(b, c, d, a, 11, 22, T12);
+ SET(a, b, c, d, 12, 7, T13);
+ SET(d, a, b, c, 13, 12, T14);
+ SET(c, d, a, b, 14, 17, T15);
+ SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+ /* Round 2. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + G(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 1, 5, T17);
+ SET(d, a, b, c, 6, 9, T18);
+ SET(c, d, a, b, 11, 14, T19);
+ SET(b, c, d, a, 0, 20, T20);
+ SET(a, b, c, d, 5, 5, T21);
+ SET(d, a, b, c, 10, 9, T22);
+ SET(c, d, a, b, 15, 14, T23);
+ SET(b, c, d, a, 4, 20, T24);
+ SET(a, b, c, d, 9, 5, T25);
+ SET(d, a, b, c, 14, 9, T26);
+ SET(c, d, a, b, 3, 14, T27);
+ SET(b, c, d, a, 8, 20, T28);
+ SET(a, b, c, d, 13, 5, T29);
+ SET(d, a, b, c, 2, 9, T30);
+ SET(c, d, a, b, 7, 14, T31);
+ SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+ /* Round 3. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + H(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 5, 4, T33);
+ SET(d, a, b, c, 8, 11, T34);
+ SET(c, d, a, b, 11, 16, T35);
+ SET(b, c, d, a, 14, 23, T36);
+ SET(a, b, c, d, 1, 4, T37);
+ SET(d, a, b, c, 4, 11, T38);
+ SET(c, d, a, b, 7, 16, T39);
+ SET(b, c, d, a, 10, 23, T40);
+ SET(a, b, c, d, 13, 4, T41);
+ SET(d, a, b, c, 0, 11, T42);
+ SET(c, d, a, b, 3, 16, T43);
+ SET(b, c, d, a, 6, 23, T44);
+ SET(a, b, c, d, 9, 4, T45);
+ SET(d, a, b, c, 12, 11, T46);
+ SET(c, d, a, b, 15, 16, T47);
+ SET(b, c, d, a, 2, 23, T48);
+#undef SET
+
+ /* Round 4. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + I(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 6, T49);
+ SET(d, a, b, c, 7, 10, T50);
+ SET(c, d, a, b, 14, 15, T51);
+ SET(b, c, d, a, 5, 21, T52);
+ SET(a, b, c, d, 12, 6, T53);
+ SET(d, a, b, c, 3, 10, T54);
+ SET(c, d, a, b, 10, 15, T55);
+ SET(b, c, d, a, 1, 21, T56);
+ SET(a, b, c, d, 8, 6, T57);
+ SET(d, a, b, c, 15, 10, T58);
+ SET(c, d, a, b, 6, 15, T59);
+ SET(b, c, d, a, 13, 21, T60);
+ SET(a, b, c, d, 4, 6, T61);
+ SET(d, a, b, c, 11, 10, T62);
+ SET(c, d, a, b, 2, 15, T63);
+ SET(b, c, d, a, 9, 21, T64);
+#undef SET
+
+ /* Then perform the following additions. (That is increment each
+ of the four registers by the value it had before this block
+ was started.) */
+ pms->abcd[0] += a;
+ pms->abcd[1] += b;
+ pms->abcd[2] += c;
+ pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+ pms->count[0] = pms->count[1] = 0;
+ pms->abcd[0] = 0x67452301;
+ pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
+ pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
+ pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+ const md5_byte_t *p = data;
+ int left = nbytes;
+ int offset = (pms->count[0] >> 3) & 63;
+ md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+ if (nbytes <= 0)
+ return;
+
+ /* Update the message length. */
+ pms->count[1] += nbytes >> 29;
+ pms->count[0] += nbits;
+ if (pms->count[0] < nbits)
+ pms->count[1]++;
+
+ /* Process an initial partial block. */
+ if (offset) {
+ int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+ memcpy(pms->buf + offset, p, copy);
+ if (offset + copy < 64)
+ return;
+ p += copy;
+ left -= copy;
+ md5_process(pms, pms->buf);
+ }
+
+ /* Process full blocks. */
+ for (; left >= 64; p += 64, left -= 64)
+ md5_process(pms, p);
+
+ /* Process a final partial block. */
+ if (left)
+ memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+ static const md5_byte_t pad[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ md5_byte_t data[8];
+ int i;
+
+ /* Save the length before padding. */
+ for (i = 0; i < 8; ++i)
+ data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+ /* Pad to 56 bytes mod 64. */
+ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+ /* Append the length. */
+ md5_append(pms, data, 8);
+ for (i = 0; i < 16; ++i)
+ digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
diff --git a/jam-files/engine/md5.h b/jam-files/engine/md5.h
new file mode 100644
index 00000000..698c995d
--- /dev/null
+++ b/jam-files/engine/md5.h
@@ -0,0 +1,91 @@
+/*
+ Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
+ text is available at
+ http://www.ietf.org/rfc/rfc1321.txt
+ The code is derived from the text of the RFC, including the test suite
+ (section A.5) but excluding the rest of Appendix A. It does not include
+ any code or documentation that is identified in the RFC as being
+ copyrighted.
+
+ The original and principal author of md5.h is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2002-04-13 lpd Removed support for non-ANSI compilers; removed
+ references to Ghostscript; clarified derivation from RFC 1321;
+ now handles byte order either statically or dynamically.
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+ added conditionalization for C++ compilation from Martin
+ Purschke <purschke@bnl.gov>.
+ 1999-05-03 lpd Original version.
+ */
+
+#ifndef md5_INCLUDED
+# define md5_INCLUDED
+
+/*
+ * This package supports both compile-time and run-time determination of CPU
+ * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
+ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
+ * defined as non-zero, the code will be compiled to run only on big-endian
+ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
+ * run on either big- or little-endian CPUs, but will run slightly less
+ * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
+ */
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned int md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+ md5_word_t count[2]; /* message length in bits, lsw first */
+ md5_word_t abcd[4]; /* digest buffer */
+ md5_byte_t buf[64]; /* accumulate block */
+} md5_state_t;
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* Initialize the algorithm. */
+void md5_init(md5_state_t *pms);
+
+/* Append a string to the message. */
+void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+
+/* Finish the message and return the digest. */
+void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
+
+#endif /* md5_INCLUDED */
diff --git a/jam-files/engine/mem.c b/jam-files/engine/mem.c
new file mode 100644
index 00000000..6a11fb38
--- /dev/null
+++ b/jam-files/engine/mem.c
@@ -0,0 +1,75 @@
+/*
+Copyright Rene Rivera 2006.
+Distributed under the Boost Software License, Version 1.0.
+(See accompanying file LICENSE_1_0.txt or copy at
+http://www.boost.org/LICENSE_1_0.txt)
+*/
+
+#include "jam.h"
+
+#ifdef OPT_BOEHM_GC
+
+ /* Compile the Boehm GC as one big chunk of code. It's much easier
+ this way, than trying to make radical changes to the bjam build
+ scripts. */
+
+ #define ATOMIC_UNCOLLECTABLE
+ #define NO_EXECUTE_PERMISSION
+ #define ALL_INTERIOR_POINTERS
+
+ #define LARGE_CONFIG
+ /*
+ #define NO_SIGNALS
+ #define SILENT
+ */
+ #ifndef GC_DEBUG
+ #define NO_DEBUGGING
+ #endif
+
+ #ifdef __GLIBC__
+ #define __USE_GNU
+ #endif
+
+ #include "boehm_gc/reclaim.c"
+ #include "boehm_gc/allchblk.c"
+ #include "boehm_gc/misc.c"
+ #include "boehm_gc/alloc.c"
+ #include "boehm_gc/mach_dep.c"
+ #include "boehm_gc/os_dep.c"
+ #include "boehm_gc/mark_rts.c"
+ #include "boehm_gc/headers.c"
+ #include "boehm_gc/mark.c"
+ #include "boehm_gc/obj_map.c"
+ #include "boehm_gc/pcr_interface.c"
+ #include "boehm_gc/blacklst.c"
+ #include "boehm_gc/new_hblk.c"
+ #include "boehm_gc/real_malloc.c"
+ #include "boehm_gc/dyn_load.c"
+ #include "boehm_gc/dbg_mlc.c"
+ #include "boehm_gc/malloc.c"
+ #include "boehm_gc/stubborn.c"
+ #include "boehm_gc/checksums.c"
+ #include "boehm_gc/pthread_support.c"
+ #include "boehm_gc/pthread_stop_world.c"
+ #include "boehm_gc/darwin_stop_world.c"
+ #include "boehm_gc/typd_mlc.c"
+ #include "boehm_gc/ptr_chck.c"
+ #include "boehm_gc/mallocx.c"
+ #include "boehm_gc/gcj_mlc.c"
+ #include "boehm_gc/specific.c"
+ #include "boehm_gc/gc_dlopen.c"
+ #include "boehm_gc/backgraph.c"
+ #include "boehm_gc/win32_threads.c"
+
+ /* Needs to be last. */
+ #include "boehm_gc/finalize.c"
+
+#elif defined(OPT_DUMA)
+
+ #ifdef OS_NT
+ #define WIN32
+ #endif
+ #include "duma/duma.c"
+ #include "duma/print.c"
+
+#endif
diff --git a/jam-files/engine/mem.h b/jam-files/engine/mem.h
new file mode 100644
index 00000000..71b2fb4b
--- /dev/null
+++ b/jam-files/engine/mem.h
@@ -0,0 +1,134 @@
+/*
+Copyright Rene Rivera 2006.
+Distributed under the Boost Software License, Version 1.0.
+(See accompanying file LICENSE_1_0.txt or copy at
+http://www.boost.org/LICENSE_1_0.txt)
+*/
+
+#ifndef BJAM_MEM_H
+#define BJAM_MEM_H
+
+
+#ifdef OPT_BOEHM_GC
+
+ /* Use Boehm GC memory allocator. */
+ #include <gc.h>
+ #define bjam_malloc_x(s) memset(GC_malloc(s),0,s)
+ #define bjam_malloc_atomic_x(s) memset(GC_malloc_atomic(s),0,s)
+ #define bjam_calloc_x(n,s) memset(GC_malloc((n)*(s)),0,(n)*(s))
+ #define bjam_calloc_atomic_x(n,s) memset(GC_malloc_atomic((n)*(s)),0,(n)*(s))
+ #define bjam_realloc_x(p,s) GC_realloc(p,s)
+ #define bjam_free_x(p) GC_free(p)
+ #define bjam_mem_init_x() GC_init(); GC_enable_incremental()
+
+ #define bjam_malloc_raw_x(s) malloc(s)
+ #define bjam_calloc_raw_x(n,s) calloc(n,s)
+ #define bjam_realloc_raw_x(p,s) realloc(p,s)
+ #define bjam_free_raw_x(p) free(p)
+
+ #ifndef BJAM_NEWSTR_NO_ALLOCATE
+ #define BJAM_NEWSTR_NO_ALLOCATE
+ #endif
+
+#elif defined(OPT_DUMA)
+
+ /* Use Duma memory debugging library. */
+ #include <stdlib.h>
+ #define _DUMA_CONFIG_H_
+ #define DUMA_NO_GLOBAL_MALLOC_FREE
+ #define DUMA_EXPLICIT_INIT
+ #define DUMA_NO_THREAD_SAFETY
+ #define DUMA_NO_CPP_SUPPORT
+ /* #define DUMA_NO_LEAKDETECTION */
+ /* #define DUMA_USE_FRAMENO */
+ /* #define DUMA_PREFER_ATEXIT */
+ /* #define DUMA_OLD_DEL_MACRO */
+ /* #define DUMA_NO_HANG_MSG */
+ #define DUMA_PAGE_SIZE 4096
+ #define DUMA_MIN_ALIGNMENT 1
+ /* #define DUMA_GNU_INIT_ATTR 0 */
+ typedef unsigned int DUMA_ADDR;
+ typedef unsigned int DUMA_SIZE;
+ #include <duma.h>
+ #define bjam_malloc_x(s) malloc(s)
+ #define bjam_calloc_x(n,s) calloc(n,s)
+ #define bjam_realloc_x(p,s) realloc(p,s)
+ #define bjam_free_x(p) free(p)
+
+ #ifndef BJAM_NEWSTR_NO_ALLOCATE
+ #define BJAM_NEWSTR_NO_ALLOCATE
+ #endif
+
+#else
+
+ /* Standard C memory allocation. */
+ #define bjam_malloc_x(s) malloc(s)
+ #define bjam_calloc_x(n,s) calloc(n,s)
+ #define bjam_realloc_x(p,s) realloc(p,s)
+ #define bjam_free_x(p) free(p)
+
+#endif
+
+#ifndef bjam_malloc_atomic_x
+ #define bjam_malloc_atomic_x(s) bjam_malloc_x(s)
+#endif
+#ifndef bjam_calloc_atomic_x
+ #define bjam_calloc_atomic_x(n,s) bjam_calloc_x(n,s)
+#endif
+#ifndef bjam_mem_init_x
+ #define bjam_mem_init_x()
+#endif
+#ifndef bjam_mem_close_x
+ #define bjam_mem_close_x()
+#endif
+#ifndef bjam_malloc_raw_x
+ #define bjam_malloc_raw_x(s) bjam_malloc_x(s)
+#endif
+#ifndef bjam_calloc_raw_x
+ #define bjam_calloc_raw_x(n,s) bjam_calloc_x(n,s)
+#endif
+#ifndef bjam_realloc_raw_x
+ #define bjam_realloc_raw_x(p,s) bjam_realloc_x(p,s)
+#endif
+#ifndef bjam_free_raw_x
+ #define bjam_free_raw_x(p) bjam_free_x(p)
+#endif
+
+#ifdef OPT_DEBUG_PROFILE
+
+ /* Profile tracing of memory allocations. */
+ #define BJAM_MALLOC(s) (profile_memory(s), bjam_malloc_x(s))
+ #define BJAM_MALLOC_ATOMIC(s) (profile_memory(s), bjam_malloc_atomic_x(s))
+ #define BJAM_CALLOC(n,s) (profile_memory(n*s), bjam_calloc_x(n,s))
+ #define BJAM_CALLOC_ATOMIC(n,s) (profile_memory(n*s), bjam_calloc_atomic_x(n,s))
+ #define BJAM_REALLOC(p,s) (profile_memory(s), bjam_realloc_x(p,s))
+ #define BJAM_FREE(p) bjam_free_x(p)
+ #define BJAM_MEM_INIT() bjam_mem_init_x()
+ #define BJAM_MEM_CLOSE() bjam_mem_close_x()
+
+ #define BJAM_MALLOC_RAW(s) (profile_memory(s), bjam_malloc_raw_x(s))
+ #define BJAM_CALLOC_RAW(n,s) (profile_memory(n*s), bjam_calloc_raw_x(n,s))
+ #define BJAM_REALLOC_RAW(p,s) (profile_memory(s), bjam_realloc_raw_x(p,s))
+ #define BJAM_FREE_RAW(p) bjam_free_raw_x(p)
+
+#else
+
+ /* No mem tracing. */
+ #define BJAM_MALLOC(s) bjam_malloc_x(s)
+ #define BJAM_MALLOC_ATOMIC(s) bjam_malloc_atomic_x(s)
+ #define BJAM_CALLOC(n,s) bjam_calloc_x(n,s)
+ #define BJAM_CALLOC_ATOMIC(n,s) bjam_calloc_atomic_x(n,s)
+ #define BJAM_REALLOC(p,s) bjam_realloc_x(p,s)
+ #define BJAM_FREE(p) bjam_free_x(p)
+ #define BJAM_MEM_INIT() bjam_mem_init_x()
+ #define BJAM_MEM_CLOSE() bjam_mem_close_x()
+
+ #define BJAM_MALLOC_RAW(s) bjam_malloc_raw_x(s)
+ #define BJAM_CALLOC_RAW(n,s) bjam_calloc_raw_x(n,s)
+ #define BJAM_REALLOC_RAW(p,s) bjam_realloc_raw_x(p,s)
+ #define BJAM_FREE_RAW(p) bjam_free_raw_x(p)
+
+#endif
+
+
+#endif
diff --git a/jam-files/engine/mkjambase.c b/jam-files/engine/mkjambase.c
new file mode 100644
index 00000000..cdf59982
--- /dev/null
+++ b/jam-files/engine/mkjambase.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/* This file is ALSO:
+ * Copyright 2001-2004 David Abrahams.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+/*
+ * mkjambase.c - turn Jambase into a big C structure
+ *
+ * Usage: mkjambase jambase.c Jambase ...
+ *
+ * Results look like this:
+ *
+ * char *jambase[] = {
+ * "...\n",
+ * ...
+ * 0 };
+ *
+ * Handles \'s and "'s specially; knows to delete blank and comment lines.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+
+int main( int argc, char * * argv, char * * envp )
+{
+ char buf[ 1024 ];
+ FILE * fin;
+ FILE * fout;
+ char * p;
+ int doDotC = 0;
+
+ if ( argc < 3 )
+ {
+ fprintf( stderr, "usage: %s jambase.c Jambase ...\n", argv[ 0 ] );
+ return -1;
+ }
+
+ if ( !( fout = fopen( argv[1], "w" ) ) )
+ {
+ perror( argv[ 1 ] );
+ return -1;
+ }
+
+ /* If the file ends in .c generate a C source file. */
+ if ( ( p = strrchr( argv[1], '.' ) ) && !strcmp( p, ".c" ) )
+ doDotC++;
+
+ /* Now process the files. */
+
+ argc -= 2;
+ argv += 2;
+
+ if ( doDotC )
+ {
+ fprintf( fout, "/* Generated by mkjambase from Jambase */\n" );
+ fprintf( fout, "char *jambase[] = {\n" );
+ }
+
+ for ( ; argc--; ++argv )
+ {
+ if ( !( fin = fopen( *argv, "r" ) ) )
+ {
+ perror( *argv );
+ return -1;
+ }
+
+ if ( doDotC )
+ fprintf( fout, "/* %s */\n", *argv );
+ else
+ fprintf( fout, "### %s ###\n", *argv );
+
+ while ( fgets( buf, sizeof( buf ), fin ) )
+ {
+ if ( doDotC )
+ {
+ char * p = buf;
+
+ /* Strip leading whitespace. */
+ while ( ( *p == ' ' ) || ( *p == '\t' ) || ( *p == '\n' ) )
+ ++p;
+
+ /* Drop comments and empty lines. */
+ if ( ( *p == '#' ) || !*p )
+ continue;
+
+ /* Copy. */
+ putc( '"', fout );
+ for ( ; *p && ( *p != '\n' ); ++p )
+ switch ( *p )
+ {
+ case '\\': putc( '\\', fout ); putc( '\\', fout ); break;
+ case '"' : putc( '\\', fout ); putc( '"' , fout ); break;
+ case '\r': break;
+ default: putc( *p, fout ); break;
+ }
+
+ fprintf( fout, "\\n\",\n" );
+ }
+ else
+ {
+ fprintf( fout, "%s", buf );
+ }
+ }
+
+ fclose( fin );
+ }
+
+ if ( doDotC )
+ fprintf( fout, "0 };\n" );
+
+ fclose( fout );
+
+ return 0;
+}
diff --git a/jam-files/engine/modules.c b/jam-files/engine/modules.c
new file mode 100644
index 00000000..72952594
--- /dev/null
+++ b/jam-files/engine/modules.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2001-2004 David Abrahams.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+#include "jam.h"
+
+#include "modules.h"
+#include "string.h"
+#include "hash.h"
+#include "newstr.h"
+#include "lists.h"
+#include "parse.h"
+#include "rules.h"
+#include "variable.h"
+#include "strings.h"
+#include <assert.h>
+
+static struct hash * module_hash = 0;
+
+
+static char * new_module_str( module_t * m, char * suffix )
+{
+ char * result;
+ string s;
+ string_copy( &s, m->name );
+ string_append( &s, suffix );
+ result = newstr( s.value );
+ string_free( &s );
+ return result;
+}
+
+
+module_t * bindmodule( char * name )
+{
+ PROFILE_ENTER( BINDMODULE );
+
+ string s;
+ module_t m_;
+ module_t * m = &m_;
+
+ if ( !module_hash )
+ module_hash = hashinit( sizeof( module_t ), "modules" );
+
+ string_new( &s );
+ if ( name )
+ {
+ string_append( &s, name );
+ string_push_back( &s, '.' );
+ }
+
+ m->name = s.value;
+
+ if ( hashenter( module_hash, (HASHDATA * *)&m ) )
+ {
+ m->name = newstr( m->name );
+ m->variables = 0;
+ m->rules = 0;
+ m->imported_modules = 0;
+ m->class_module = 0;
+ m->native_rules = 0;
+ m->user_module = 0;
+ }
+ string_free( &s );
+
+ PROFILE_EXIT( BINDMODULE );
+
+ return m;
+}
+
+/*
+ * demand_rules() - Get the module's "rules" hash on demand.
+ */
+struct hash * demand_rules( module_t * m )
+{
+ if ( !m->rules )
+ m->rules = hashinit( sizeof( RULE ), new_module_str( m, "rules" ) );
+ return m->rules;
+}
+
+
+/*
+ * delete_module() - wipe out the module's rules and variables.
+ */
+
+static void delete_rule_( void * xrule, void * data )
+{
+ rule_free( (RULE *)xrule );
+}
+
+
+void delete_module( module_t * m )
+{
+ /* Clear out all the rules. */
+ if ( m->rules )
+ {
+ hashenumerate( m->rules, delete_rule_, (void *)0 );
+ hashdone( m->rules );
+ m->rules = 0;
+ }
+
+ if ( m->variables )
+ {
+ var_hash_swap( &m->variables );
+ var_done();
+ var_hash_swap( &m->variables );
+ m->variables = 0;
+ }
+}
+
+
+module_t * root_module()
+{
+ static module_t * root = 0;
+ if ( !root )
+ root = bindmodule( 0 );
+ return root;
+}
+
+void enter_module( module_t * m )
+{
+ var_hash_swap( &m->variables );
+}
+
+
+void exit_module( module_t * m )
+{
+ var_hash_swap( &m->variables );
+}
+
+
+void import_module( LIST * module_names, module_t * target_module )
+{
+ PROFILE_ENTER( IMPORT_MODULE );
+
+ struct hash * h;
+
+ if ( !target_module->imported_modules )
+ target_module->imported_modules = hashinit( sizeof( char * ), "imported" );
+ h = target_module->imported_modules;
+
+ for ( ; module_names; module_names = module_names->next )
+ {
+ char * s = module_names->string;
+ char * * ss = &s;
+ hashenter( h, (HASHDATA * *)&ss );
+ }
+
+ PROFILE_EXIT( IMPORT_MODULE );
+}
+
+
+static void add_module_name( void * r_, void * result_ )
+{
+ char * * r = (char * *)r_;
+ LIST * * result = (LIST * *)result_;
+
+ *result = list_new( *result, copystr( *r ) );
+}
+
+
+LIST * imported_modules( module_t * module )
+{
+ LIST * result = L0;
+ if ( module->imported_modules )
+ hashenumerate( module->imported_modules, add_module_name, &result );
+ return result;
+}
diff --git a/jam-files/engine/modules.h b/jam-files/engine/modules.h
new file mode 100644
index 00000000..60053a23
--- /dev/null
+++ b/jam-files/engine/modules.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2001-2004 David Abrahams.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef MODULES_DWA10182001_H
+# define MODULES_DWA10182001_H
+
+#include "lists.h"
+
+struct module_t
+{
+ char* name;
+ struct hash* rules;
+ struct hash* variables;
+ struct hash* imported_modules;
+ struct module_t* class_module;
+ struct hash* native_rules;
+ int user_module;
+};
+
+typedef struct module_t module_t ; /* MSVC debugger gets confused unless this is provided */
+
+module_t* bindmodule( char* name );
+module_t* root_module();
+void enter_module( module_t* );
+void exit_module( module_t* );
+void delete_module( module_t* );
+
+void import_module(LIST* module_names, module_t* target_module);
+LIST* imported_modules(module_t* module);
+
+struct hash* demand_rules( module_t* );
+
+
+#endif
+
diff --git a/jam-files/engine/modules/order.c b/jam-files/engine/modules/order.c
new file mode 100644
index 00000000..d77943a7
--- /dev/null
+++ b/jam-files/engine/modules/order.c
@@ -0,0 +1,144 @@
+/* Copyright Vladimir Prus 2004. Distributed under the Boost */
+/* Software License, Version 1.0. (See accompanying */
+/* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */
+
+#include "../native.h"
+#include "../lists.h"
+#include "../strings.h"
+#include "../newstr.h"
+#include "../variable.h"
+
+
+/* Use quite klugy approach: when we add order dependency from 'a' to 'b',
+ just append 'b' to of value of variable 'a'.
+*/
+LIST *add_pair( PARSE *parse, FRAME *frame )
+{
+ LIST* arg = lol_get( frame->args, 0 );
+
+ var_set(arg->string, list_copy(0, arg->next), VAR_APPEND);
+
+ return L0;
+}
+
+/** Given a list and a value, returns position of that value in
+ the list, or -1 if not found.
+*/
+int list_index(LIST* list, const char* value)
+{
+ int result = 0;
+ for(; list; list = list->next, ++result) {
+ if (strcmp(list->string, value) == 0)
+ return result;
+ }
+ return -1;
+}
+
+enum colors { white, gray, black };
+
+/* Main routite of topological sort. Calls itself recursively on all
+ adjacent vertices which were not yet visited. After that, 'current_vertex'
+ is added to '*result_ptr'.
+*/
+void do_ts(int** graph, int current_vertex, int* colors, int** result_ptr)
+{
+ int i;
+
+ colors[current_vertex] = gray;
+ for(i = 0; graph[current_vertex][i] != -1; ++i) {
+ int adjacent_vertex = graph[current_vertex][i];
+
+ if (colors[adjacent_vertex] == white)
+ do_ts(graph, adjacent_vertex, colors, result_ptr);
+ /* The vertex is either black, in which case we don't have to do
+ anything, a gray, in which case we have a loop. If we have a loop,
+ it's not clear what useful diagnostic we can emit, so we emit
+ nothing. */
+ }
+ colors[current_vertex] = black;
+ **result_ptr = current_vertex;
+ (*result_ptr)++;
+}
+
+void topological_sort(int** graph, int num_vertices, int* result)
+{
+ int i;
+ int* colors = (int*)BJAM_CALLOC(num_vertices, sizeof(int));
+ for (i = 0; i < num_vertices; ++i)
+ colors[i] = white;
+
+ for(i = 0; i < num_vertices; ++i)
+ if (colors[i] == white)
+ do_ts(graph, i, colors, &result);
+
+ BJAM_FREE(colors);
+}
+
+LIST *order( PARSE *parse, FRAME *frame )
+{
+ LIST* arg = lol_get( frame->args, 0 );
+ LIST* tmp;
+ LIST* result = 0;
+ int src;
+
+ /* We need to create a graph of order dependencies between
+ the passed objects. We assume that there are no duplicates
+ passed to 'add_pair'.
+ */
+ int length = list_length(arg);
+ int** graph = (int**)BJAM_CALLOC(length, sizeof(int*));
+ int* order = (int*)BJAM_MALLOC((length+1)*sizeof(int));
+
+ for(tmp = arg, src = 0; tmp; tmp = tmp->next, ++src) {
+ /* For all object this one depend upon, add elements
+ to 'graph' */
+ LIST* dependencies = var_get(tmp->string);
+ int index = 0;
+
+ graph[src] = (int*)BJAM_CALLOC(list_length(dependencies)+1, sizeof(int));
+ for(; dependencies; dependencies = dependencies->next) {
+ int dst = list_index(arg, dependencies->string);
+ if (dst != -1)
+ graph[src][index++] = dst;
+ }
+ graph[src][index] = -1;
+ }
+
+ topological_sort(graph, length, order);
+
+ {
+ int index = length-1;
+ for(; index >= 0; --index) {
+ int i;
+ tmp = arg;
+ for (i = 0; i < order[index]; ++i, tmp = tmp->next);
+ result = list_new(result, tmp->string);
+ }
+ }
+
+ /* Clean up */
+ {
+ int i;
+ for(i = 0; i < length; ++i)
+ BJAM_FREE(graph[i]);
+ BJAM_FREE(graph);
+ BJAM_FREE(order);
+ }
+
+ return result;
+}
+
+void init_order()
+{
+ {
+ char* args[] = { "first", "second", 0 };
+ declare_native_rule("class@order", "add-pair", args, add_pair, 1);
+ }
+
+ {
+ char* args[] = { "objects", "*", 0 };
+ declare_native_rule("class@order", "order", args, order, 1);
+ }
+
+
+}
diff --git a/jam-files/engine/modules/path.c b/jam-files/engine/modules/path.c
new file mode 100644
index 00000000..f5d09622
--- /dev/null
+++ b/jam-files/engine/modules/path.c
@@ -0,0 +1,32 @@
+/* Copyright Vladimir Prus 2003. Distributed under the Boost */
+/* Software License, Version 1.0. (See accompanying */
+/* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */
+
+#include "../native.h"
+#include "../timestamp.h"
+#include "../newstr.h"
+
+LIST *path_exists( PARSE *parse, FRAME *frame )
+{
+ LIST* l = lol_get( frame->args, 0 );
+
+ time_t time;
+ timestamp(l->string, &time);
+ if (time != 0)
+ {
+ return list_new(0, newstr("true"));
+ }
+ else
+ {
+ return L0;
+ }
+}
+
+void init_path()
+{
+ {
+ char* args[] = { "location", 0 };
+ declare_native_rule("path", "exists", args, path_exists, 1);
+ }
+
+}
diff --git a/jam-files/engine/modules/property-set.c b/jam-files/engine/modules/property-set.c
new file mode 100644
index 00000000..2b0fb5d9
--- /dev/null
+++ b/jam-files/engine/modules/property-set.c
@@ -0,0 +1,110 @@
+/* Copyright Vladimir Prus 2003. Distributed under the Boost */
+/* Software License, Version 1.0. (See accompanying */
+/* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */
+
+#include "../native.h"
+#include "../timestamp.h"
+#include "../newstr.h"
+#include "../strings.h"
+#include "../lists.h"
+#include "../variable.h"
+#include "../compile.h"
+
+LIST* get_grist(char* f)
+{
+ char* end = strchr(f, '>');
+ string s[1];
+ LIST* result;
+
+ string_new(s);
+
+ string_append_range(s, f, end+1);
+ result = list_new(0, newstr(s->value));
+
+ string_free(s);
+ return result;
+}
+
+/*
+rule create ( raw-properties * )
+{
+ raw-properties = [ sequence.unique
+ [ sequence.insertion-sort $(raw-properties) ] ] ;
+
+ local key = $(raw-properties:J=-:E=) ;
+
+ if ! $(.ps.$(key))
+ {
+ .ps.$(key) = [ new property-set $(raw-properties) ] ;
+ }
+ return $(.ps.$(key)) ;
+}
+*/
+
+LIST *property_set_create( PARSE *parse, FRAME *frame )
+{
+ LIST* properties = lol_get( frame->args, 0 );
+ LIST* sorted = 0;
+#if 0
+ LIST* order_sensitive = 0;
+#endif
+ LIST* unique;
+ LIST* tmp;
+ LIST* val;
+ string var[1];
+
+#if 0
+ /* Sort all properties which are not order sensitive */
+ for(tmp = properties; tmp; tmp = tmp->next) {
+ LIST* g = get_grist(tmp->string);
+ LIST* att = call_rule("feature.attributes", frame, g, 0);
+ if (list_in(att, "order-sensitive")) {
+ order_sensitive = list_new( order_sensitive, tmp->string);
+ } else {
+ sorted = list_new( sorted, tmp->string);
+ }
+ list_free(att);
+ }
+
+ sorted = list_sort(sorted);
+ sorted = list_append(sorted, order_sensitive);
+ unique = list_unique(sorted);
+#endif
+ sorted = list_sort(properties);
+ unique = list_unique(sorted);
+
+ string_new(var);
+ string_append(var, ".ps.");
+
+ for(tmp = unique; tmp; tmp = tmp->next) {
+ string_append(var, tmp->string);
+ string_push_back(var, '-');
+ }
+ val = var_get(var->value);
+ if (val == 0)
+ {
+ val = call_rule("new", frame,
+ list_append(list_new(0, "property-set"), unique), 0);
+
+ var_set(newstr(var->value), list_copy(0, val), VAR_SET);
+ }
+ else
+ {
+ val = list_copy(0, val);
+ }
+
+ string_free(var);
+ /* The 'unique' variable is freed in 'call_rule'. */
+ list_free(sorted);
+
+ return val;
+
+}
+
+void init_property_set()
+{
+ {
+ char* args[] = { "raw-properties", "*", 0 };
+ declare_native_rule("property-set", "create", args, property_set_create, 1);
+ }
+}
diff --git a/jam-files/engine/modules/readme.txt b/jam-files/engine/modules/readme.txt
new file mode 100644
index 00000000..2edf6e17
--- /dev/null
+++ b/jam-files/engine/modules/readme.txt
@@ -0,0 +1,3 @@
+
+This directory constains sources which declare native
+rules for Boost.Build modules. \ No newline at end of file
diff --git a/jam-files/engine/modules/regex.c b/jam-files/engine/modules/regex.c
new file mode 100644
index 00000000..d048ba1d
--- /dev/null
+++ b/jam-files/engine/modules/regex.c
@@ -0,0 +1,96 @@
+/* Copyright Vladimir Prus 2003. Distributed under the Boost */
+/* Software License, Version 1.0. (See accompanying */
+/* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */
+
+#include "../native.h"
+#include "../timestamp.h"
+#include "../newstr.h"
+#include "../strings.h"
+#include "../regexp.h"
+#include "../compile.h"
+
+/*
+rule transform ( list * : pattern : indices * )
+{
+ indices ?= 1 ;
+ local result ;
+ for local e in $(list)
+ {
+ local m = [ MATCH $(pattern) : $(e) ] ;
+ if $(m)
+ {
+ result += $(m[$(indices)]) ;
+ }
+ }
+ return $(result) ;
+}
+*/
+LIST *regex_transform( PARSE *parse, FRAME *frame )
+{
+ LIST* l = lol_get( frame->args, 0 );
+ LIST* pattern = lol_get( frame->args, 1 );
+ LIST* indices_list = lol_get(frame->args, 2);
+ int* indices = 0;
+ int size;
+ int* p;
+ LIST* result = 0;
+
+ string buf[1];
+ string_new(buf);
+
+ if (indices_list)
+ {
+ size = list_length(indices_list);
+ indices = (int*)BJAM_MALLOC(size*sizeof(int));
+ for(p = indices; indices_list; indices_list = indices_list->next)
+ {
+ *p++ = atoi(indices_list->string);
+ }
+ }
+ else
+ {
+ size = 1;
+ indices = (int*)BJAM_MALLOC(sizeof(int));
+ *indices = 1;
+ }
+
+ {
+ /* Result is cached and intentionally never freed */
+ regexp *re = regex_compile( pattern->string );
+
+ for(; l; l = l->next)
+ {
+ if( regexec( re, l->string ) )
+ {
+ int i = 0;
+ for(; i < size; ++i)
+ {
+ int index = indices[i];
+ /* Skip empty submatches. Not sure it's right in all cases,
+ but surely is right for the case for which this routine
+ is optimized -- header scanning.
+ */
+ if (re->startp[index] != re->endp[index])
+ {
+ string_append_range( buf, re->startp[index], re->endp[index] );
+ result = list_new( result, newstr( buf->value ) );
+ string_truncate( buf, 0 );
+ }
+ }
+ }
+ }
+ string_free( buf );
+ }
+
+ BJAM_FREE(indices);
+
+ return result;
+}
+
+void init_regex()
+{
+ {
+ char* args[] = { "list", "*", ":", "pattern", ":", "indices", "*", 0 };
+ declare_native_rule("regex", "transform", args, regex_transform, 2);
+ }
+}
diff --git a/jam-files/engine/modules/sequence.c b/jam-files/engine/modules/sequence.c
new file mode 100644
index 00000000..bda80d94
--- /dev/null
+++ b/jam-files/engine/modules/sequence.c
@@ -0,0 +1,42 @@
+/* Copyright Vladimir Prus 2003. Distributed under the Boost */
+/* Software License, Version 1.0. (See accompanying */
+/* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */
+
+#include "../native.h"
+
+# ifndef max
+# define max( a,b ) ((a)>(b)?(a):(b))
+# endif
+
+
+LIST *sequence_select_highest_ranked( PARSE *parse, FRAME *frame )
+{
+ /* Returns all of 'elements' for which corresponding element in parallel */
+ /* list 'rank' is equal to the maximum value in 'rank'. */
+
+ LIST* elements = lol_get( frame->args, 0 );
+ LIST* rank = lol_get( frame->args, 1 );
+
+ LIST* result = 0;
+ LIST* tmp;
+ int highest_rank = -1;
+
+ for (tmp = rank; tmp; tmp = tmp->next)
+ highest_rank = max(highest_rank, atoi(tmp->string));
+
+ for (; rank; rank = rank->next, elements = elements->next)
+ if (atoi(rank->string) == highest_rank)
+ result = list_new(result, elements->string);
+
+ return result;
+}
+
+void init_sequence()
+{
+ {
+ char* args[] = { "elements", "*", ":", "rank", "*", 0 };
+ declare_native_rule("sequence", "select-highest-ranked", args,
+ sequence_select_highest_ranked, 1);
+ }
+
+}
diff --git a/jam-files/engine/modules/set.c b/jam-files/engine/modules/set.c
new file mode 100644
index 00000000..f8219403
--- /dev/null
+++ b/jam-files/engine/modules/set.c
@@ -0,0 +1,41 @@
+/* Copyright Vladimir Prus 2003. Distributed under the Boost */
+/* Software License, Version 1.0. (See accompanying */
+/* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */
+
+#include "../native.h"
+
+/*
+ local result = ;
+ local element ;
+ for element in $(B)
+ {
+ if ! ( $(element) in $(A) )
+ {
+ result += $(element) ;
+ }
+ }
+ return $(result) ;
+*/
+LIST *set_difference( PARSE *parse, FRAME *frame )
+{
+
+ LIST* b = lol_get( frame->args, 0 );
+ LIST* a = lol_get( frame->args, 1 );
+
+ LIST* result = 0;
+ for(; b; b = b->next)
+ {
+ if (!list_in(a, b->string))
+ result = list_new(result, b->string);
+ }
+ return result;
+}
+
+void init_set()
+{
+ {
+ char* args[] = { "B", "*", ":", "A", "*", 0 };
+ declare_native_rule("set", "difference", args, set_difference, 1);
+ }
+
+}
diff --git a/jam-files/engine/native.c b/jam-files/engine/native.c
new file mode 100644
index 00000000..4c289959
--- /dev/null
+++ b/jam-files/engine/native.c
@@ -0,0 +1,36 @@
+/* Copyright Vladimir Prus 2003. Distributed under the Boost */
+/* Software License, Version 1.0. (See accompanying */
+/* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */
+
+#include "native.h"
+#include "hash.h"
+
+# define P0 (PARSE *)0
+# define C0 (char *)0
+
+
+void declare_native_rule(char* module, char* rule, char** args,
+ LIST*(*f)(PARSE*, FRAME*), int version)
+{
+ module_t* m = bindmodule(module);
+ if (m->native_rules == 0) {
+ m->native_rules = hashinit( sizeof( native_rule_t ), "native rules");
+ }
+
+ {
+ native_rule_t n, *np = &n;
+ n.name = rule;
+ if (args)
+ {
+ n.arguments = args_new();
+ lol_build( n.arguments->data, args );
+ }
+ else
+ {
+ n.arguments = 0;
+ }
+ n.procedure = parse_make( f, P0, P0, P0, C0, C0, 0 );
+ n.version = version;
+ hashenter(m->native_rules, (HASHDATA**)&np);
+ }
+}
diff --git a/jam-files/engine/native.h b/jam-files/engine/native.h
new file mode 100644
index 00000000..3fc710b9
--- /dev/null
+++ b/jam-files/engine/native.h
@@ -0,0 +1,34 @@
+/* Copyright David Abrahams 2003. Distributed under the Boost */
+/* Software License, Version 1.0. (See accompanying */
+/* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */
+
+#ifndef NATIVE_H_VP_2003_12_09
+#define NATIVE_H_VP_2003_12_09
+
+#include "rules.h"
+
+struct native_rule_t
+{
+ char* name;
+ argument_list* arguments;
+ PARSE* procedure;
+ /* Version of the interface that the native rule provides.
+ It's possible that we want to change the set parameter
+ for existing native rule. In that case, version number
+ should be incremented so that Boost.Build can check for
+ version it relies on.
+
+ Versions are numbered from 1.
+ */
+ int version;
+};
+
+/* MSVC debugger gets confused unless this is provided */
+typedef struct native_rule_t native_rule_t ;
+
+void declare_native_rule(char* module, char* rule, char** args,
+ LIST*(*f)(PARSE*, FRAME*), int version);
+
+
+
+#endif
diff --git a/jam-files/engine/newstr.c b/jam-files/engine/newstr.c
new file mode 100644
index 00000000..6a229eb2
--- /dev/null
+++ b/jam-files/engine/newstr.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+# include "jam.h"
+# include "newstr.h"
+# include "hash.h"
+# include "compile.h"
+# include <stddef.h>
+# include <stdlib.h>
+
+/*
+ * newstr.c - string manipulation routines
+ *
+ * To minimize string copying, string creation, copying, and freeing
+ * is done through newstr.
+ *
+ * External functions:
+ *
+ * newstr() - return a dynamically allocated copy of a string
+ * copystr() - return a copy of a string previously returned by newstr()
+ * freestr() - free a string returned by newstr() or copystr()
+ * str_done() - free string tables
+ *
+ * Once a string is passed to newstr(), the returned string is readonly.
+ *
+ * This implementation builds a hash table of all strings, so that multiple
+ * calls of newstr() on the same string allocate memory for the string once.
+ * Strings are never actually freed.
+ */
+
+typedef char * STRING;
+
+static struct hash * strhash = 0;
+static int strtotal = 0;
+static int strcount_in = 0;
+static int strcount_out = 0;
+
+
+/*
+ * Immortal string allocator implementation speeds string allocation and cuts
+ * down on internal fragmentation.
+ */
+
+# define STRING_BLOCK 4096
+typedef struct strblock
+{
+ struct strblock * next;
+ char data[STRING_BLOCK];
+} strblock;
+
+static strblock * strblock_chain = 0;
+
+/* Storage remaining in the current strblock */
+static char * storage_start = 0;
+static char * storage_finish = 0;
+
+
+/*
+ * allocate() - Allocate n bytes of immortal string storage.
+ */
+
+static char * allocate( size_t const n )
+{
+#ifdef BJAM_NEWSTR_NO_ALLOCATE
+ return (char*)BJAM_MALLOC_ATOMIC(n);
+#else
+ /* See if we can grab storage from an existing block. */
+ size_t remaining = storage_finish - storage_start;
+ if ( remaining >= n )
+ {
+ char * result = storage_start;
+ storage_start += n;
+ return result;
+ }
+ else /* Must allocate a new block. */
+ {
+ strblock * new_block;
+ size_t nalloc = n;
+ if ( nalloc < STRING_BLOCK )
+ nalloc = STRING_BLOCK;
+
+ /* Allocate a new block and link into the chain. */
+ new_block = (strblock *)BJAM_MALLOC( offsetof( strblock, data[0] ) + nalloc * sizeof( new_block->data[0] ) );
+ if ( new_block == 0 )
+ return 0;
+ new_block->next = strblock_chain;
+ strblock_chain = new_block;
+
+ /* Take future allocations out of the larger remaining space. */
+ if ( remaining < nalloc - n )
+ {
+ storage_start = new_block->data + n;
+ storage_finish = new_block->data + nalloc;
+ }
+ return new_block->data;
+ }
+#endif
+}
+
+
+/*
+ * newstr() - return a dynamically allocated copy of a string.
+ */
+
+char * newstr( char * string )
+{
+ STRING str;
+ STRING * s = &str;
+
+ if ( !strhash )
+ strhash = hashinit( sizeof( STRING ), "strings" );
+
+ *s = string;
+
+ if ( hashenter( strhash, (HASHDATA **)&s ) )
+ {
+ int l = strlen( string );
+ char * m = (char *)allocate( l + 1 );
+
+ strtotal += l + 1;
+ memcpy( m, string, l + 1 );
+ *s = m;
+ }
+
+ strcount_in += 1;
+ return *s;
+}
+
+
+/*
+ * copystr() - return a copy of a string previously returned by newstr()
+ */
+
+char * copystr( char * s )
+{
+ strcount_in += 1;
+ return s;
+}
+
+
+/*
+ * freestr() - free a string returned by newstr() or copystr()
+ */
+
+void freestr( char * s )
+{
+ strcount_out += 1;
+}
+
+
+/*
+ * str_done() - free string tables.
+ */
+
+void str_done()
+{
+ /* Reclaim string blocks. */
+ while ( strblock_chain != 0 )
+ {
+ strblock * n = strblock_chain->next;
+ BJAM_FREE(strblock_chain);
+ strblock_chain = n;
+ }
+
+ hashdone( strhash );
+
+ if ( DEBUG_MEM )
+ printf( "%dK in strings\n", strtotal / 1024 );
+
+ /* printf( "--- %d strings of %d dangling\n", strcount_in-strcount_out, strcount_in ); */
+}
diff --git a/jam-files/engine/newstr.h b/jam-files/engine/newstr.h
new file mode 100644
index 00000000..84a4d7b6
--- /dev/null
+++ b/jam-files/engine/newstr.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * newstr.h - string manipulation routines
+ */
+
+char * copystr ( char * );
+void freestr ( char * );
+char * newstr ( char * );
+void str_done();
diff --git a/jam-files/engine/option.c b/jam-files/engine/option.c
new file mode 100644
index 00000000..d25e5e8a
--- /dev/null
+++ b/jam-files/engine/option.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+# include "jam.h"
+# include "option.h"
+
+/*
+ * option.c - command line option processing
+ *
+ * {o >o
+ * \<>) "Process command line options as defined in <option.h>.
+ * Return the number of argv[] elements used up by options,
+ * or -1 if an invalid option flag was given or an argument
+ * was supplied for an option that does not require one."
+ */
+
+int getoptions( int argc, char * * argv, char * opts, bjam_option * optv )
+{
+ int i;
+ int optc = N_OPTS;
+
+ memset( (char *)optv, '\0', sizeof( *optv ) * N_OPTS );
+
+ for ( i = 0; i < argc; ++i )
+ {
+ char *arg;
+
+ if ( ( argv[ i ][ 0 ] != '-' ) ||
+ ( ( argv[ i ][ 1 ] != '-' ) && !isalpha( argv[ i ][ 1 ] ) ) )
+ continue;
+
+ if ( !optc-- )
+ {
+ printf( "too many options (%d max)\n", N_OPTS );
+ return -1;
+ }
+
+ for ( arg = &argv[ i ][ 1 ]; *arg; ++arg )
+ {
+ char * f;
+
+ for ( f = opts; *f; ++f )
+ if ( *f == *arg )
+ break;
+
+ if ( !*f )
+ {
+ printf( "Invalid option: -%c\n", *arg );
+ return -1;
+ }
+
+ optv->flag = *f;
+
+ if ( f[ 1 ] != ':' )
+ {
+ optv++->val = "true";
+ }
+ else if ( arg[ 1 ] )
+ {
+ optv++->val = &arg[1];
+ break;
+ }
+ else if ( ++i < argc )
+ {
+ optv++->val = argv[ i ];
+ break;
+ }
+ else
+ {
+ printf( "option: -%c needs argument\n", *f );
+ return -1;
+ }
+ }
+ }
+
+ return i;
+}
+
+
+/*
+ * Name: getoptval() - find an option given its character.
+ */
+
+char * getoptval( bjam_option * optv, char opt, int subopt )
+{
+ int i;
+ for ( i = 0; i < N_OPTS; ++i, ++optv )
+ if ( ( optv->flag == opt ) && !subopt-- )
+ return optv->val;
+ return 0;
+}
diff --git a/jam-files/engine/option.h b/jam-files/engine/option.h
new file mode 100644
index 00000000..99ef620d
--- /dev/null
+++ b/jam-files/engine/option.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * option.h - command line option processing
+ *
+ * {o >o
+ * \ -) "Command line option."
+ */
+
+typedef struct bjam_option
+{
+ char flag; /* filled in by getoption() */
+ char *val; /* set to random address if true */
+} bjam_option;
+
+# define N_OPTS 256
+
+int getoptions( int argc, char **argv, char *opts, bjam_option *optv );
+char * getoptval( bjam_option *optv, char opt, int subopt );
diff --git a/jam-files/engine/output.c b/jam-files/engine/output.c
new file mode 100644
index 00000000..483c6ca9
--- /dev/null
+++ b/jam-files/engine/output.c
@@ -0,0 +1,125 @@
+/*
+ Copyright 2007 Rene Rivera
+ Distributed under the Boost Software License, Version 1.0.
+ (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+*/
+
+#include "jam.h"
+#include "output.h"
+#include "newstr.h"
+#include <stdio.h>
+
+#define bjam_out (stdout)
+#define bjam_err (stderr)
+
+static void out_
+(
+ char const * data,
+ FILE * io
+)
+{
+ while ( *data )
+ {
+ size_t len = strcspn(data,"\r");
+ data += fwrite(data,1,len,io);
+ if ( *data == '\r' ) ++data;
+ }
+}
+
+
+void out_action
+(
+ char const * action,
+ char const * target,
+ char const * command,
+ char const * out_data,
+ char const * err_data,
+ int exit_reason
+)
+{
+ /* Print out the action+target line, if the action is quite the action
+ * should be null.
+ */
+ if ( action )
+ {
+ fprintf( bjam_out, "%s %s\n", action, target );
+ }
+
+ /* Print out the command executed if given -d+2. */
+ if ( DEBUG_EXEC )
+ {
+ fputs( command, bjam_out );
+ fputc( '\n', bjam_out );
+ }
+
+ /* Print out the command executed to the command stream. */
+ if ( globs.cmdout )
+ {
+ fputs( command, globs.cmdout );
+ }
+
+ switch ( exit_reason )
+ {
+ case EXIT_OK:
+ break;
+ case EXIT_FAIL:
+ break;
+ case EXIT_TIMEOUT:
+ {
+ /* Process expired, make user aware with explicit message. */
+ if ( action )
+ {
+ /* But only output for non-quietly actions. */
+ fprintf( bjam_out, "%ld second time limit exceeded\n", globs.timeout );
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Print out the command output, if requested, or if the program failed. */
+ if ( action || exit_reason != EXIT_OK)
+ {
+ /* But only output for non-quietly actions. */
+ if ( ( 0 != out_data ) &&
+ ( ( globs.pipe_action & 1 /* STDOUT_FILENO */ ) ||
+ ( globs.pipe_action == 0 ) ) )
+ {
+ out_( out_data, bjam_out );
+ }
+ if ( ( 0 != err_data ) &&
+ ( globs.pipe_action & 2 /* STDERR_FILENO */ ) )
+ {
+ out_( err_data, bjam_err );
+ }
+ }
+
+ fflush( bjam_out );
+ fflush( bjam_err );
+ fflush( globs.cmdout );
+}
+
+
+char * outf_int( int value )
+{
+ char buffer[50];
+ sprintf( buffer, "%i", value );
+ return newstr( buffer );
+}
+
+
+char * outf_double( double value )
+{
+ char buffer[50];
+ sprintf( buffer, "%f", value );
+ return newstr( buffer );
+}
+
+
+char * outf_time( time_t value )
+{
+ char buffer[50];
+ strftime( buffer, 49, "%Y-%m-%d %H:%M:%SZ", gmtime( &value ) );
+ return newstr( buffer );
+}
diff --git a/jam-files/engine/output.h b/jam-files/engine/output.h
new file mode 100644
index 00000000..9e9876cf
--- /dev/null
+++ b/jam-files/engine/output.h
@@ -0,0 +1,29 @@
+/*
+ Copyright 2007 Rene Rivera
+ Distributed under the Boost Software License, Version 1.0.
+ (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+*/
+
+#ifndef BJAM_OUTPUT_H
+#define BJAM_OUTPUT_H
+
+#include <time.h>
+
+#define EXIT_OK 0
+#define EXIT_FAIL 1
+#define EXIT_TIMEOUT 2
+
+void out_action(
+ const char * action,
+ const char * target,
+ const char * command,
+ const char * out_data,
+ const char * err_data,
+ int exit_reason
+ );
+
+char * outf_int( int value );
+char * outf_double( double value );
+char * outf_time( time_t value );
+
+#endif
diff --git a/jam-files/engine/parse.c b/jam-files/engine/parse.c
new file mode 100644
index 00000000..9114fa05
--- /dev/null
+++ b/jam-files/engine/parse.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright 1993, 2000 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/* This file is ALSO:
+ * Copyright 2001-2004 David Abrahams.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+#include "jam.h"
+#include "lists.h"
+#include "parse.h"
+#include "scan.h"
+#include "newstr.h"
+#include "modules.h"
+#include "frames.h"
+
+/*
+ * parse.c - make and destroy parse trees as driven by the parser
+ *
+ * 09/07/00 (seiwald) - ref count on PARSE to avoid freeing when used,
+ * as per Matt Armstrong.
+ * 09/11/00 (seiwald) - structure reworked to reflect that (*func)()
+ * returns a LIST *.
+ */
+
+static PARSE * yypsave;
+
+void parse_file( char * f, FRAME * frame )
+{
+ /* Suspend scan of current file and push this new file in the stream. */
+ yyfparse( f );
+
+ /* Now parse each block of rules and execute it. Execute it outside of the
+ * parser so that recursive calls to yyrun() work (no recursive yyparse's).
+ */
+
+ for ( ; ; )
+ {
+ PARSE * p;
+
+ /* Filled by yyparse() calling parse_save(). */
+ yypsave = 0;
+
+ /* If parse error or empty parse, outta here. */
+ if ( yyparse() || !( p = yypsave ) )
+ break;
+
+ /* Run the parse tree. */
+ parse_evaluate( p, frame );
+ parse_free( p );
+ }
+}
+
+
+void parse_save( PARSE * p )
+{
+ yypsave = p;
+}
+
+
+PARSE * parse_make(
+ LIST * (* func)( PARSE *, FRAME * ),
+ PARSE * left,
+ PARSE * right,
+ PARSE * third,
+ char * string,
+ char * string1,
+ int num )
+{
+ PARSE * p = (PARSE *)BJAM_MALLOC( sizeof( PARSE ) );
+
+ p->func = func;
+ p->left = left;
+ p->right = right;
+ p->third = third;
+ p->string = string;
+ p->string1 = string1;
+ p->num = num;
+ p->refs = 1;
+ p->rulename = 0;
+
+ if ( left )
+ {
+ p->file = left->file;
+ p->line = left->line;
+ }
+ else
+ {
+ yyinput_stream( &p->file, &p->line );
+ }
+
+ return p;
+}
+
+
+void parse_refer( PARSE * p )
+{
+ ++p->refs;
+}
+
+
+void parse_free( PARSE * p )
+{
+ if ( --p->refs )
+ return;
+
+ if ( p->string )
+ freestr( p->string );
+ if ( p->string1 )
+ freestr( p->string1 );
+ if ( p->left )
+ parse_free( p->left );
+ if ( p->right )
+ parse_free( p->right );
+ if ( p->third )
+ parse_free( p->third );
+ if ( p->rulename )
+ freestr( p->rulename );
+
+ BJAM_FREE( (char *)p );
+}
+
+
+LIST * parse_evaluate( PARSE * p, FRAME * frame )
+{
+ frame->procedure = p;
+ return (*p->func)( p, frame );
+}
diff --git a/jam-files/engine/parse.h b/jam-files/engine/parse.h
new file mode 100644
index 00000000..e324972f
--- /dev/null
+++ b/jam-files/engine/parse.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 1993, 2000 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/* This file is ALSO:
+ * Copyright 2001-2004 David Abrahams.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+#ifndef PARSE_DWA20011020_H
+#define PARSE_DWA20011020_H
+
+#include "frames.h"
+#include "modules.h"
+#include "lists.h"
+
+/*
+ * parse.h - make and destroy parse trees as driven by the parser.
+ */
+
+/*
+ * Parse tree node.
+ */
+
+struct _PARSE {
+ LIST * (* func)( PARSE *, FRAME * );
+ PARSE * left;
+ PARSE * right;
+ PARSE * third;
+ char * string;
+ char * string1;
+ int num;
+ int refs;
+/* module * module; */
+ char * rulename;
+ char * file;
+ int line;
+};
+
+void parse_file( char *, FRAME * );
+void parse_save( PARSE * );
+
+PARSE * parse_make(
+ LIST * (* func)( PARSE *, FRAME * ),
+ PARSE * left,
+ PARSE * right,
+ PARSE * third,
+ char * string,
+ char * string1,
+ int num );
+
+void parse_refer ( PARSE * );
+void parse_free ( PARSE * );
+LIST * parse_evaluate( PARSE *, FRAME * );
+
+#endif
diff --git a/jam-files/engine/patchlevel.h b/jam-files/engine/patchlevel.h
new file mode 100644
index 00000000..699efd84
--- /dev/null
+++ b/jam-files/engine/patchlevel.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/* Keep JAMVERSYM in sync with VERSION. */
+/* It can be accessed as $(JAMVERSION) in the Jamfile. */
+
+#define VERSION_MAJOR 2011
+#define VERSION_MINOR 04
+#define VERSION_PATCH 0
+#define VERSION_MAJOR_SYM "2011"
+#define VERSION_MINOR_SYM "04"
+#define VERSION_PATCH_SYM "00"
+#define VERSION "2011.4"
+#define JAMVERSYM "JAMVERSION=2011.4"
diff --git a/jam-files/engine/pathmac.c b/jam-files/engine/pathmac.c
new file mode 100644
index 00000000..e2c250e3
--- /dev/null
+++ b/jam-files/engine/pathmac.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/* This file is ALSO:
+ * Copyright 2001-2004 David Abrahams.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+# include "jam.h"
+# include "pathsys.h"
+
+# ifdef OS_MAC
+
+# define DELIM ':'
+
+/*
+ * pathunix.c - manipulate file names on UNIX, NT, OS2
+ *
+ * External routines:
+ *
+ * path_parse() - split a file name into dir/base/suffix/member
+ * path_build() - build a filename given dir/base/suffix/member
+ * path_parent() - make a PATHNAME point to its parent dir
+ *
+ * File_parse() and path_build() just manipuate a string and a structure;
+ * they do not make system calls.
+ *
+ * 04/08/94 (seiwald) - Coherent/386 support added.
+ * 12/26/93 (seiwald) - handle dir/.suffix properly in path_build()
+ * 12/19/94 (mikem) - solaris string table insanity support
+ * 12/21/94 (wingerd) Use backslashes for pathnames - the NT way.
+ * 02/14/95 (seiwald) - parse and build /xxx properly
+ * 02/23/95 (wingerd) Compilers on NT can handle "/" in pathnames, so we
+ * should expect hdr searches to come up with strings
+ * like "thing/thing.h". So we need to test for "/" as
+ * well as "\" when parsing pathnames.
+ * 03/16/95 (seiwald) - fixed accursed typo on line 69.
+ * 05/03/96 (seiwald) - split from filent.c, fileunix.c
+ * 12/20/96 (seiwald) - when looking for the rightmost . in a file name,
+ * don't include the archive member name.
+ * 01/10/01 (seiwald) - path_parse now strips the trailing : from the
+ * directory name, unless the directory name is all
+ * :'s, so that $(d:P) works.
+ */
+
+/*
+ * path_parse() - split a file name into dir/base/suffix/member
+ */
+
+void
+path_parse(
+ char *file,
+ PATHNAME *f )
+{
+ char *p, *q;
+ char *end;
+
+ memset( (char *)f, 0, sizeof( *f ) );
+
+ /* Look for <grist> */
+
+ if ( file[0] == '<' && ( p = strchr( file, '>' ) ) )
+ {
+ f->f_grist.ptr = file;
+ f->f_grist.len = p - file;
+ file = p + 1;
+ }
+
+ /* Look for dir: */
+
+ if ( p = strrchr( file, DELIM ) )
+ {
+ f->f_dir.ptr = file;
+ f->f_dir.len = p - file;
+ file = p + 1;
+
+ /* All :'s? Include last : as part of directory name */
+
+ while ( ( p > f->f_dir.ptr ) && ( *--p == DELIM ) );
+
+ if ( p == f->f_dir.ptr )
+ ++f->f_dir.len;
+ }
+
+ end = file + strlen( file );
+
+ /* Look for (member). */
+
+ if ( ( p = strchr( file, '(' ) ) && ( end[-1] == ')' ) )
+ {
+ f->f_member.ptr = p + 1;
+ f->f_member.len = end - p - 2;
+ end = p;
+ }
+
+ /* Look for .suffix */
+ /* This would be memrchr() */
+
+ p = 0;
+ q = file;
+
+ while ( q = memchr( q, '.', end - q ) )
+ p = q++;
+
+ if ( p )
+ {
+ f->f_suffix.ptr = p;
+ f->f_suffix.len = end - p;
+ end = p;
+ }
+
+ /* Leaves base */
+
+ f->f_base.ptr = file;
+ f->f_base.len = end - file;
+}
+
+/*
+ * path_build() - build a filename given dir/base/suffix/member.
+ */
+
+# define DIR_EMPTY 0 /* "" */
+# define DIR_DOT 1 /* : */
+# define DIR_DOTDOT 2 /* :: */
+# define DIR_ABS 3 /* dira:dirb: */
+# define DIR_REL 4 /* :dira:dirb: */
+
+# define G_DIR 0 /* take dir */
+# define G_ROOT 1 /* take root */
+# define G_CAT 2 /* prepend root to dir */
+# define G_DTDR 3 /* :: of rel dir */
+# define G_DDDD 4 /* make it ::: (../..) */
+# define G_MT 5 /* leave it empty */
+
+char grid[5][5] = {
+/* EMPTY DOT DOTDOT ABS REL */
+/* EMPTY */ { G_MT, G_DIR, G_DIR, G_DIR, G_DIR },
+/* DOT */ { G_ROOT, G_DIR, G_DIR, G_DIR, G_DIR },
+/* DOTDOT */ { G_ROOT, G_ROOT, G_DDDD, G_DIR, G_DTDR },
+/* ABS */ { G_ROOT, G_ROOT, G_ROOT, G_DIR, G_CAT },
+/* REL */ { G_ROOT, G_ROOT, G_ROOT, G_DIR, G_CAT }
+};
+
+static int file_flags( char * ptr, int len )
+{
+ if ( !len )
+ return DIR_EMPTY;
+ if ( ( len == 1 ) && ( ptr[0] == DELIM ) )
+ return DIR_DOT;
+ if ( ( len == 2 ) && ( ptr[0] == DELIM ) && ( ptr[1] == DELIM ) )
+ return DIR_DOTDOT;
+ if ( ptr[0] == DELIM )
+ return DIR_REL;
+ return DIR_ABS;
+}
+
+
+void path_build( PATHNAME * f, string * file, int binding )
+{
+ int dflag;
+ int rflag;
+ int act;
+
+ file_build1( f, file );
+
+ /* Combine root & directory, according to the grid. */
+
+ dflag = file_flags( f->f_dir.ptr, f->f_dir.len );
+ rflag = file_flags( f->f_root.ptr, f->f_root.len );
+
+ switch ( act = grid[ rflag ][ dflag ] )
+ {
+ case G_DTDR:
+ {
+ /* :: of rel dir */
+ string_push_back( file, DELIM );
+ }
+ /* fall through */
+
+ case G_DIR:
+ /* take dir */
+ string_append_range( file, f->f_dir.ptr, f->f_dir.ptr + f->f_dir.len );
+ break;
+
+ case G_ROOT:
+ /* take root */
+ string_append_range( file, f->f_root.ptr, f->f_root.ptr + f->f_root.len );
+ break;
+
+ case G_CAT:
+ /* prepend root to dir */
+ string_append_range( file, f->f_root.ptr, f->f_root.ptr + f->f_root.len );
+ if ( file->value[ file->size - 1 ] == DELIM )
+ string_pop_back( file );
+ string_append_range( file, f->f_dir.ptr, f->f_dir.ptr + f->f_dir.len );
+ break;
+
+ case G_DDDD:
+ /* make it ::: (../..) */
+ string_append( file, ":::" );
+ break;
+ }
+
+ /* Put : between dir and file (if none already). */
+
+ if ( ( act != G_MT ) &&
+ ( file->value[ file->size - 1 ] != DELIM ) &&
+ ( f->f_base.len || f->f_suffix.len ) )
+ {
+ string_push_back( file, DELIM );
+ }
+
+ if ( f->f_base.len )
+ string_append_range( file, f->f_base.ptr, f->f_base.ptr + f->f_base.len );
+
+ if ( f->f_suffix.len )
+ string_append_range( file, f->f_suffix.ptr, f->f_suffix.ptr + f->f_suffix.len );
+
+ if ( f->f_member.len )
+ {
+ string_push_back( file, '(' );
+ string_append_range( file, f->f_member.ptr, f->f_member.ptr + f->f_member.len );
+ string_push_back( file, ')' );
+ }
+
+ if ( DEBUG_SEARCH )
+ printf( " -> '%s'\n", file->value );
+}
+
+
+/*
+ * path_parent() - make a PATHNAME point to its parent dir
+ */
+
+void path_parent( PATHNAME * f )
+{
+ /* Just set everything else to nothing. */
+
+ f->f_base.ptr =
+ f->f_suffix.ptr =
+ f->f_member.ptr = "";
+
+ f->f_base.len =
+ f->f_suffix.len =
+ f->f_member.len = 0;
+}
+
+# endif /* OS_MAC */
diff --git a/jam-files/engine/pathsys.h b/jam-files/engine/pathsys.h
new file mode 100644
index 00000000..73775810
--- /dev/null
+++ b/jam-files/engine/pathsys.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * pathsys.h - PATHNAME struct
+ */
+
+/*
+ * PATHNAME - a name of a file, broken into <grist>dir/base/suffix(member)
+ *
+ * <grist> is salt to distinguish between targets that otherwise would
+ * have the same name: it never appears in the bound name of a target.
+ * (member) is an archive member name: the syntax is arbitrary, but must
+ * agree in path_parse(), path_build() and the Jambase.
+ *
+ * On VMS, we keep track of whether the original path was a directory
+ * (without a file), so that $(VAR:D) can climb to the parent.
+ */
+
+#ifndef PATHSYS_VP_20020211_H
+# define PATHSYS_VP_20020211_H
+
+#include "strings.h"
+
+typedef struct _pathname PATHNAME;
+typedef struct _pathpart PATHPART;
+
+struct _pathpart
+{
+ char * ptr;
+ int len;
+};
+
+struct _pathname
+{
+ PATHPART part[6];
+#ifdef OS_VMS
+ int parent;
+#endif
+
+#define f_grist part[0]
+#define f_root part[1]
+#define f_dir part[2]
+#define f_base part[3]
+#define f_suffix part[4]
+#define f_member part[5]
+};
+
+void path_build( PATHNAME * f, string * file, int binding );
+void path_build1( PATHNAME * f, string * file );
+
+void path_parse( char * file, PATHNAME * f );
+void path_parent( PATHNAME * f );
+
+#ifdef NT
+
+/** Returns newstr-allocated string with long equivivalent of 'short_name'.
+ If none exists -- i.e. 'short_path' is already long path, it's returned
+ unaltered. */
+char * short_path_to_long_path( char * short_path );
+
+#endif
+
+#ifdef USE_PATHUNIX
+/** Returns a static pointer to the system dependent path to the temporary
+ directory. NOTE: *without* a trailing path separator.
+*/
+const char * path_tmpdir( void );
+
+/** Returns a new temporary name.
+*/
+const char * path_tmpnam( void );
+
+/** Returns a new temporary path.
+*/
+const char * path_tmpfile( void );
+#endif
+
+/** Give the first argument to 'main', return a full path to
+ our executable. Returns null in the unlikely case it
+ cannot be determined. Caller is responsible for freeing
+ the string.
+
+ Implemented in jam.c
+*/
+char * executable_path (char *argv0);
+
+#endif
diff --git a/jam-files/engine/pathunix.c b/jam-files/engine/pathunix.c
new file mode 100644
index 00000000..2daad14b
--- /dev/null
+++ b/jam-files/engine/pathunix.c
@@ -0,0 +1,457 @@
+/*
+ * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/* This file is ALSO:
+ * Copyright 2001-2004 David Abrahams.
+ * Copyright 2005 Rene Rivera.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+# include "jam.h"
+# include "pathsys.h"
+# include "strings.h"
+# include "newstr.h"
+# include "filesys.h"
+# include <time.h>
+# include <stdlib.h>
+# ifndef OS_NT
+# include <unistd.h>
+# endif
+
+# ifdef USE_PATHUNIX
+
+/*
+ * pathunix.c - manipulate file names on UNIX, NT, OS2, AmigaOS
+ *
+ * External routines:
+ *
+ * path_parse() - split a file name into dir/base/suffix/member
+ * path_build() - build a filename given dir/base/suffix/member
+ * path_parent() - make a PATHNAME point to its parent dir
+ *
+ * File_parse() and path_build() just manipuate a string and a structure;
+ * they do not make system calls.
+ *
+ * 04/08/94 (seiwald) - Coherent/386 support added.
+ * 12/26/93 (seiwald) - handle dir/.suffix properly in path_build()
+ * 12/19/94 (mikem) - solaris string table insanity support
+ * 12/21/94 (wingerd) Use backslashes for pathnames - the NT way.
+ * 02/14/95 (seiwald) - parse and build /xxx properly
+ * 02/23/95 (wingerd) Compilers on NT can handle "/" in pathnames, so we
+ * should expect hdr searches to come up with strings
+ * like "thing/thing.h". So we need to test for "/" as
+ * well as "\" when parsing pathnames.
+ * 03/16/95 (seiwald) - fixed accursed typo on line 69.
+ * 05/03/96 (seiwald) - split from filent.c, fileunix.c
+ * 12/20/96 (seiwald) - when looking for the rightmost . in a file name,
+ * don't include the archive member name.
+ * 01/13/01 (seiwald) - turn on \ handling on UNIX, on by accident
+ */
+
+/*
+ * path_parse() - split a file name into dir/base/suffix/member
+ */
+
+void path_parse( char * file, PATHNAME * f )
+{
+ char * p;
+ char * q;
+ char * end;
+
+ memset( (char *)f, 0, sizeof( *f ) );
+
+ /* Look for <grist> */
+
+ if ( ( file[0] == '<' ) && ( p = strchr( file, '>' ) ) )
+ {
+ f->f_grist.ptr = file;
+ f->f_grist.len = p - file;
+ file = p + 1;
+ }
+
+ /* Look for dir/ */
+
+ p = strrchr( file, '/' );
+
+# if PATH_DELIM == '\\'
+ /* On NT, look for dir\ as well */
+ {
+ char *p1 = strrchr( file, '\\' );
+ p = p1 > p ? p1 : p;
+ }
+# endif
+
+ if ( p )
+ {
+ f->f_dir.ptr = file;
+ f->f_dir.len = p - file;
+
+ /* Special case for / - dirname is /, not "" */
+
+ if ( !f->f_dir.len )
+ f->f_dir.len = 1;
+
+# if PATH_DELIM == '\\'
+ /* Special case for D:/ - dirname is D:/, not "D:" */
+
+ if ( f->f_dir.len == 2 && file[1] == ':' )
+ f->f_dir.len = 3;
+# endif
+
+ file = p + 1;
+ }
+
+ end = file + strlen( file );
+
+ /* Look for (member) */
+
+ if ( ( p = strchr( file, '(' ) ) && ( end[ -1 ] == ')' ) )
+ {
+ f->f_member.ptr = p + 1;
+ f->f_member.len = end - p - 2;
+ end = p;
+ }
+
+ /* Look for .suffix */
+ /* This would be memrchr() */
+
+ p = 0;
+ q = file;
+
+ while ( ( q = (char *)memchr( q, '.', end - q ) ) )
+ p = q++;
+
+ if ( p )
+ {
+ f->f_suffix.ptr = p;
+ f->f_suffix.len = end - p;
+ end = p;
+ }
+
+ /* Leaves base */
+
+ f->f_base.ptr = file;
+ f->f_base.len = end - file;
+}
+
+/*
+ * path_delims - the string of legal path delimiters
+ */
+static char path_delims[] = {
+ PATH_DELIM,
+# if PATH_DELIM == '\\'
+ '/',
+# endif
+ 0
+};
+
+/*
+ * is_path_delim() - true iff c is a path delimiter
+ */
+static int is_path_delim( char c )
+{
+ char* p = strchr( path_delims, c );
+ return p && *p;
+}
+
+/*
+ * as_path_delim() - convert c to a path delimiter if it isn't one
+ * already
+ */
+static char as_path_delim( char c )
+{
+ return is_path_delim( c ) ? c : PATH_DELIM;
+}
+
+/*
+ * path_build() - build a filename given dir/base/suffix/member
+ *
+ * To avoid changing slash direction on NT when reconstituting paths,
+ * instead of unconditionally appending PATH_DELIM we check the
+ * past-the-end character of the previous path element. If it is in
+ * path_delims, we append that, and only append PATH_DELIM as a last
+ * resort. This heuristic is based on the fact that PATHNAME objects
+ * are usually the result of calling path_parse, which leaves the
+ * original slashes in the past-the-end position. Correctness depends
+ * on the assumption that all strings are zero terminated, so a
+ * past-the-end character will always be available.
+ *
+ * As an attendant patch, we had to ensure that backslashes are used
+ * explicitly in timestamp.c
+ */
+
+void
+path_build(
+ PATHNAME *f,
+ string *file,
+ int binding )
+{
+ file_build1( f, file );
+
+ /* Don't prepend root if it's . or directory is rooted */
+# if PATH_DELIM == '/'
+
+ if ( f->f_root.len
+ && !( f->f_root.len == 1 && f->f_root.ptr[0] == '.' )
+ && !( f->f_dir.len && f->f_dir.ptr[0] == '/' ) )
+
+# else /* unix */
+
+ if ( f->f_root.len
+ && !( f->f_root.len == 1 && f->f_root.ptr[0] == '.' )
+ && !( f->f_dir.len && f->f_dir.ptr[0] == '/' )
+ && !( f->f_dir.len && f->f_dir.ptr[0] == '\\' )
+ && !( f->f_dir.len && f->f_dir.ptr[1] == ':' ) )
+
+# endif /* unix */
+
+ {
+ string_append_range( file, f->f_root.ptr, f->f_root.ptr + f->f_root.len );
+ /* If 'root' already ends with path delimeter,
+ don't add yet another one. */
+ if ( ! is_path_delim( f->f_root.ptr[f->f_root.len-1] ) )
+ string_push_back( file, as_path_delim( f->f_root.ptr[f->f_root.len] ) );
+ }
+
+ if ( f->f_dir.len )
+ string_append_range( file, f->f_dir.ptr, f->f_dir.ptr + f->f_dir.len );
+
+ /* UNIX: Put / between dir and file */
+ /* NT: Put \ between dir and file */
+
+ if ( f->f_dir.len && ( f->f_base.len || f->f_suffix.len ) )
+ {
+ /* UNIX: Special case for dir \ : don't add another \ */
+ /* NT: Special case for dir / : don't add another / */
+
+# if PATH_DELIM == '\\'
+ if ( !( f->f_dir.len == 3 && f->f_dir.ptr[1] == ':' ) )
+# endif
+ if ( !( f->f_dir.len == 1 && is_path_delim( f->f_dir.ptr[0] ) ) )
+ string_push_back( file, as_path_delim( f->f_dir.ptr[f->f_dir.len] ) );
+ }
+
+ if ( f->f_base.len )
+ {
+ string_append_range( file, f->f_base.ptr, f->f_base.ptr + f->f_base.len );
+ }
+
+ if ( f->f_suffix.len )
+ {
+ string_append_range( file, f->f_suffix.ptr, f->f_suffix.ptr + f->f_suffix.len );
+ }
+
+ if ( f->f_member.len )
+ {
+ string_push_back( file, '(' );
+ string_append_range( file, f->f_member.ptr, f->f_member.ptr + f->f_member.len );
+ string_push_back( file, ')' );
+ }
+}
+
+/*
+ * path_parent() - make a PATHNAME point to its parent dir
+ */
+
+void
+path_parent( PATHNAME *f )
+{
+ /* just set everything else to nothing */
+
+ f->f_base.ptr =
+ f->f_suffix.ptr =
+ f->f_member.ptr = "";
+
+ f->f_base.len =
+ f->f_suffix.len =
+ f->f_member.len = 0;
+}
+
+#ifdef NT
+#include <windows.h>
+#include <tchar.h>
+
+/* The definition of this in winnt.h is not ANSI-C compatible. */
+#undef INVALID_FILE_ATTRIBUTES
+#define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
+
+
+DWORD ShortPathToLongPath(LPCTSTR lpszShortPath,LPTSTR lpszLongPath,DWORD
+ cchBuffer)
+{
+ LONG i=0;
+ TCHAR path[_MAX_PATH]={0};
+ TCHAR ret[_MAX_PATH]={0};
+ LONG pos=0, prev_pos=0;
+ LONG len=_tcslen(lpszShortPath);
+
+ /* Is the string valid? */
+ if (!lpszShortPath) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+
+ /* Is the path valid? */
+ if (GetFileAttributes(lpszShortPath)==INVALID_FILE_ATTRIBUTES)
+ return 0;
+
+ /* Convert "/" to "\" */
+ for (i=0;i<len;++i) {
+ if (lpszShortPath[i]==_T('/'))
+ path[i]=_T('\\');
+ else
+ path[i]=lpszShortPath[i];
+ }
+
+ /* UNC path? */
+ if (path[0]==_T('\\') && path[1]==_T('\\')) {
+ pos=2;
+ for (i=0;i<2;++i) {
+ while (path[pos]!=_T('\\') && path[pos]!=_T('\0'))
+ ++pos;
+ ++pos;
+ }
+ _tcsncpy(ret,path,pos-1);
+ } /* Drive letter? */
+ else if (path[1]==_T(':')) {
+ if (path[2]==_T('\\'))
+ pos=3;
+ if (len==3) {
+ if (cchBuffer>3)
+ _tcscpy(lpszLongPath,lpszShortPath);
+ return len;
+ }
+ _tcsncpy(ret,path,2);
+ }
+
+ /* Expand the path for each subpath, and strip trailing backslashes */
+ for (prev_pos = pos-1;pos<=len;++pos) {
+ if (path[pos]==_T('\\') || (path[pos]==_T('\0') &&
+ path[pos-1]!=_T('\\'))) {
+ WIN32_FIND_DATA fd;
+ HANDLE hf=0;
+ TCHAR c=path[pos];
+ char* new_element;
+ path[pos]=_T('\0');
+
+ /* the path[prev_pos+1]... path[pos] range is the part of
+ path we're handling right now. We need to find long
+ name for that element and add it. */
+ new_element = path + prev_pos + 1;
+
+ /* First add separator, but only if there's something in result already. */
+ if (ret[0] != _T('\0'))
+ {
+ _tcscat(ret,_T("\\"));
+ }
+
+ /* If it's ".." element, we need to append it, not
+ the name in parent that FindFirstFile will return.
+ Same goes for "." */
+
+ if (new_element[0] == _T('.') && new_element[1] == _T('\0') ||
+ new_element[0] == _T('.') && new_element[1] == _T('.')
+ && new_element[2] == _T('\0'))
+ {
+ _tcscat(ret, new_element);
+ }
+ else
+ {
+ hf=FindFirstFile(path, &fd);
+ if (hf==INVALID_HANDLE_VALUE)
+ return 0;
+
+ _tcscat(ret,fd.cFileName);
+ FindClose(hf);
+ }
+
+ path[pos]=c;
+
+ prev_pos = pos;
+ }
+ }
+
+ len=_tcslen(ret)+1;
+ if (cchBuffer>=len)
+ _tcscpy(lpszLongPath,ret);
+
+ return len;
+}
+
+char* short_path_to_long_path(char* short_path)
+{
+ char buffer2[_MAX_PATH];
+ int ret = ShortPathToLongPath(short_path, buffer2, _MAX_PATH);
+
+ if (ret)
+ return newstr(buffer2);
+ else
+ return newstr(short_path);
+}
+
+#endif
+
+static string path_tmpdir_buffer[1];
+static const char * path_tmpdir_result = 0;
+
+const char * path_tmpdir()
+{
+ if (!path_tmpdir_result)
+ {
+ # ifdef OS_NT
+ DWORD pathLength = 0;
+ pathLength = GetTempPath(pathLength,NULL);
+ string_new(path_tmpdir_buffer);
+ string_reserve(path_tmpdir_buffer,pathLength);
+ pathLength = GetTempPathA(pathLength,path_tmpdir_buffer[0].value);
+ path_tmpdir_buffer[0].value[pathLength-1] = '\0';
+ path_tmpdir_buffer[0].size = pathLength-1;
+ # else
+ const char * t = getenv("TMPDIR");
+ if (!t)
+ {
+ t = "/tmp";
+ }
+ string_new(path_tmpdir_buffer);
+ string_append(path_tmpdir_buffer,t);
+ # endif
+ path_tmpdir_result = path_tmpdir_buffer[0].value;
+ }
+ return path_tmpdir_result;
+}
+
+const char * path_tmpnam(void)
+{
+ char name_buffer[64];
+ # ifdef OS_NT
+ unsigned long c0 = GetCurrentProcessId();
+ # else
+ unsigned long c0 = getpid();
+ # endif
+ static unsigned long c1 = 0;
+ if (0 == c1) c1 = time(0)&0xffff;
+ c1 += 1;
+ sprintf(name_buffer,"jam%lx%lx.000",c0,c1);
+ return newstr(name_buffer);
+}
+
+const char * path_tmpfile(void)
+{
+ const char * result = 0;
+
+ string file_path;
+ string_copy(&file_path,path_tmpdir());
+ string_push_back(&file_path,PATH_DELIM);
+ string_append(&file_path,path_tmpnam());
+ result = newstr(file_path.value);
+ string_free(&file_path);
+
+ return result;
+}
+
+
+# endif /* unix, NT, OS/2, AmigaOS */
diff --git a/jam-files/engine/pathvms.c b/jam-files/engine/pathvms.c
new file mode 100644
index 00000000..975fe5a5
--- /dev/null
+++ b/jam-files/engine/pathvms.c
@@ -0,0 +1,406 @@
+/*
+ * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/* This file is ALSO:
+ * Copyright 2001-2004 David Abrahams.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+# include "jam.h"
+# include "pathsys.h"
+
+# ifdef OS_VMS
+
+# define DEBUG
+
+/*
+ * pathvms.c - manipulate file names on VMS
+ *
+ * External routines:
+ *
+ * path_parse() - split a file name into dir/base/suffix/member
+ * path_build() - build a filename given dir/base/suffix/member
+ * path_parent() - make a PATHNAME point to its parent dir
+ *
+ * File_parse() and path_build() just manipuate a string and a structure;
+ * they do not make system calls.
+ *
+ * WARNING! This file contains voodoo logic, as black magic is
+ * necessary for wrangling with VMS file name. Woe be to people
+ * who mess with this code.
+ *
+ * 02/09/95 (seiwald) - bungled R=[xxx] - was using directory length!
+ * 05/03/96 (seiwald) - split from filevms.c
+ */
+
+/*
+ * path_parse() - split a file name into dir/base/suffix/member.
+ */
+
+void path_parse( char * file, PATHNAME * f )
+{
+ char * p;
+ char * q;
+ char * end;
+
+ memset( (char *)f, 0, sizeof( *f ) );
+
+ /* Look for <grist> */
+
+ if ( ( file[0] == '<' ) && ( p = strchr( file, '>' ) ) )
+ {
+ f->f_grist.ptr = file;
+ f->f_grist.len = p - file;
+ file = p + 1;
+ }
+
+ /* Look for dev:[dir] or dev: */
+
+ if ( ( p = strchr( file, ']' ) ) || ( p = strchr( file, ':' ) ) )
+ {
+ f->f_dir.ptr = file;
+ f->f_dir.len = p + 1 - file;
+ file = p + 1;
+ }
+
+ end = file + strlen( file );
+
+ /* Look for (member). */
+
+ if ( ( p = strchr( file, '(' ) ) && ( end[ -1 ] == ')' ) )
+ {
+ f->f_member.ptr = p + 1;
+ f->f_member.len = end - p - 2;
+ end = p;
+ }
+
+ /* Look for .suffix */
+ /* This would be memrchr(). */
+
+ p = 0;
+ q = file;
+
+ while ( q = (char *)memchr( q, '.', end - q ) )
+ p = q++;
+
+ if ( p )
+ {
+ f->f_suffix.ptr = p;
+ f->f_suffix.len = end - p;
+ end = p;
+ }
+
+ /* Leaves base. */
+ f->f_base.ptr = file;
+ f->f_base.len = end - file;
+
+ /* Is this a directory without a file spec? */
+ f->parent = 0;
+}
+
+/*
+ * dir mods result
+ * --- --- ------
+ * Rerooting:
+ *
+ * (none) :R=dev: dev:
+ * devd: :R=dev: devd:
+ * devd:[dir] :R=dev: devd:[dir]
+ * [.dir] :R=dev: dev:[dir] questionable
+ * [dir] :R=dev: dev:[dir]
+ *
+ * (none) :R=[rdir] [rdir] questionable
+ * devd: :R=[rdir] devd:
+ * devd:[dir] :R=[rdir] devd:[dir]
+ * [.dir] :R=[rdir] [rdir.dir] questionable
+ * [dir] :R=[rdir] [rdir]
+ *
+ * (none) :R=dev:[root] dev:[root]
+ * devd: :R=dev:[root] devd:
+ * devd:[dir] :R=dev:[root] devd:[dir]
+ * [.dir] :R=dev:[root] dev:[root.dir]
+ * [dir] :R=dev:[root] [dir]
+ *
+ * Climbing to parent:
+ *
+ */
+
+# define DIR_EMPTY 0 /* empty string */
+# define DIR_DEV 1 /* dev: */
+# define DIR_DEVDIR 2 /* dev:[dir] */
+# define DIR_DOTDIR 3 /* [.dir] */
+# define DIR_DASHDIR 4 /* [-] or [-.dir] */
+# define DIR_ABSDIR 5 /* [dir] */
+# define DIR_ROOT 6 /* [000000] or dev:[000000] */
+
+# define G_DIR 0 /* take just dir */
+# define G_ROOT 1 /* take just root */
+# define G_VAD 2 /* root's dev: + [abs] */
+# define G_DRD 3 /* root's dev:[dir] + [.rel] */
+# define G_VRD 4 /* root's dev: + [.rel] made [abs] */
+# define G_DDD 5 /* root's dev:[dir] + . + [dir] */
+
+static int grid[7][7] = {
+
+/* root/dir EMPTY DEV DEVDIR DOTDIR DASH, ABSDIR ROOT */
+/* EMPTY */ G_DIR, G_DIR, G_DIR, G_DIR, G_DIR, G_DIR, G_DIR,
+/* DEV */ G_ROOT, G_DIR, G_DIR, G_VRD, G_VAD, G_VAD, G_VAD,
+/* DEVDIR */ G_ROOT, G_DIR, G_DIR, G_DRD, G_VAD, G_VAD, G_VAD,
+/* DOTDIR */ G_ROOT, G_DIR, G_DIR, G_DRD, G_DIR, G_DIR, G_DIR,
+/* DASHDIR */ G_ROOT, G_DIR, G_DIR, G_DRD, G_DDD, G_DIR, G_DIR,
+/* ABSDIR */ G_ROOT, G_DIR, G_DIR, G_DRD, G_DIR, G_DIR, G_DIR,
+/* ROOT */ G_ROOT, G_DIR, G_DIR, G_VRD, G_DIR, G_DIR, G_DIR,
+
+};
+
+struct dirinf
+{
+ int flags;
+
+ struct
+ {
+ char * ptr;
+ int len;
+ } dev, dir;
+};
+
+static char * strnchr( char * buf, int c, int len )
+{
+ while ( len-- )
+ if ( *buf && ( *buf++ == c ) )
+ return buf - 1;
+ return 0;
+}
+
+
+static void dir_flags( char * buf, int len, struct dirinf * i )
+{
+ char * p;
+
+ if ( !buf || !len )
+ {
+ i->flags = DIR_EMPTY;
+ i->dev.ptr =
+ i->dir.ptr = 0;
+ i->dev.len =
+ i->dir.len = 0;
+ }
+ else if ( p = strnchr( buf, ':', len ) )
+ {
+ i->dev.ptr = buf;
+ i->dev.len = p + 1 - buf;
+ i->dir.ptr = buf + i->dev.len;
+ i->dir.len = len - i->dev.len;
+ i->flags = i->dir.len && *i->dir.ptr == '[' ? DIR_DEVDIR : DIR_DEV;
+ }
+ else
+ {
+ i->dev.ptr = buf;
+ i->dev.len = 0;
+ i->dir.ptr = buf;
+ i->dir.len = len;
+
+ if ( ( *buf == '[' ) && ( buf[1] == ']' ) )
+ i->flags = DIR_EMPTY;
+ else if ( ( *buf == '[' ) && ( buf[1] == '.' ) )
+ i->flags = DIR_DOTDIR;
+ else if ( ( *buf == '[' ) && ( buf[1] == '-' ) )
+ i->flags = DIR_DASHDIR;
+ else
+ i->flags = DIR_ABSDIR;
+ }
+
+ /* But if its rooted in any way. */
+
+ if ( ( i->dir.len == 8 ) && !strncmp( i->dir.ptr, "[000000]", 8 ) )
+ i->flags = DIR_ROOT;
+}
+
+
+/*
+ * path_build() - build a filename given dir/base/suffix/member
+ */
+
+void path_build( PATHNAME * f, string * file, int binding )
+{
+ struct dirinf root;
+ struct dirinf dir;
+ int g;
+
+ file_build1( f, file );
+
+ /* Get info on root and dir for combining. */
+ dir_flags( f->f_root.ptr, f->f_root.len, &root );
+ dir_flags( f->f_dir.ptr, f->f_dir.len, &dir );
+
+ /* Combine. */
+ switch ( g = grid[ root.flags ][ dir.flags ] )
+ {
+ case G_DIR:
+ /* take dir */
+ string_append_range( file, f->f_dir.ptr, f->f_dir.ptr + f->f_dir.len );
+ break;
+
+ case G_ROOT:
+ /* take root */
+ string_append_range( file, f->f_root.ptr, f->f_root.ptr + f->f_root.len );
+ break;
+
+ case G_VAD:
+ /* root's dev + abs directory */
+ string_append_range( file, root.dev.ptr, root.dev.ptr + root.dev.len );
+ string_append_range( file, dir.dir.ptr, dir.dir.ptr + dir.dir.len );
+ break;
+
+ case G_DRD:
+ case G_DDD:
+ /* root's dev:[dir] + rel directory */
+ string_append_range( file, f->f_root.ptr, f->f_root.ptr + f->f_root.len );
+
+ /* sanity checks: root ends with ] */
+
+ if ( file->value[file->size - 1] == ']' )
+ string_pop_back( file );
+
+ /* Add . if separating two -'s */
+
+ if ( g == G_DDD )
+ string_push_back( file, '.' );
+
+ /* skip [ of dir */
+ string_append_range( file, dir.dir.ptr + 1, dir.dir.ptr + 1 + dir.dir.len - 1 );
+ break;
+
+ case G_VRD:
+ /* root's dev + rel directory made abs */
+ string_append_range( file, root.dev.ptr, root.dev.ptr + root.dev.len );
+ string_push_back( file, '[' );
+ /* skip [. of rel dir */
+ string_append_range( file, dir.dir.ptr + 2, dir.dir.ptr + 2 + dir.dir.len - 2 );
+ break;
+ }
+
+# ifdef DEBUG
+ if ( DEBUG_SEARCH && ( root.flags || dir.flags ) )
+ printf( "%d x %d = %d (%s)\n", root.flags, dir.flags,
+ grid[ root.flags ][ dir.flags ], file->value );
+# endif
+
+ /*
+ * Now do the special :P modifier when no file was present.
+ * (none) (none)
+ * [dir1.dir2] [dir1]
+ * [dir] [000000]
+ * [.dir] (none)
+ * [] []
+ */
+
+ if ( ( file->value[ file->size - 1 ] == ']' ) && f->parent )
+ {
+ char * p = file->value + file->size;
+ while ( p-- > file->value )
+ {
+ if ( *p == '.' )
+ {
+ /* If we've truncated everything and left with '[',
+ return empty string. */
+ if ( p == file->value + 1 )
+ string_truncate( file, 0 );
+ else
+ {
+ string_truncate( file, p - file->value );
+ string_push_back( file, ']' );
+ }
+ break;
+ }
+
+ if ( *p == '-' )
+ {
+ /* handle .- or - */
+ if ( ( p > file->value ) && ( p[ -1 ] == '.' ) )
+ --p;
+
+ *p++ = ']';
+ break;
+ }
+
+ if ( *p == '[' )
+ {
+ if ( p[ 1 ] == ']' )
+ {
+ /* CONSIDER: I don't see any use of this code. We immediately
+ break, and 'p' is a local variable. */
+ p += 2;
+ }
+ else
+ {
+ string_truncate( file, p - file->value );
+ string_append( file, "[000000]" );
+ }
+ break;
+ }
+ }
+ }
+
+ /* Now copy the file pieces. */
+ if ( f->f_base.len )
+ {
+ string_append_range( file, f->f_base.ptr, f->f_base.ptr + f->f_base.len );
+ }
+
+ /* If there is no suffix, we append a "." onto all generated names. This
+ * keeps VMS from appending its own (wrong) idea of what the suffix should
+ * be.
+ */
+ if ( f->f_suffix.len )
+ string_append_range( file, f->f_suffix.ptr, f->f_suffix.ptr + f->f_suffix.len );
+ else if ( binding && f->f_base.len )
+ string_push_back( file, '.' );
+
+ if ( f->f_member.len )
+ {
+ string_push_back( file, '(' );
+ string_append_range( file, f->f_member.ptr, f->f_member.ptr + f->f_member.len );
+ string_push_back( file, ')' );
+ }
+
+# ifdef DEBUG
+ if ( DEBUG_SEARCH )
+ printf( "built %.*s + %.*s / %.*s suf %.*s mem %.*s -> %s\n",
+ f->f_root.len, f->f_root.ptr,
+ f->f_dir.len, f->f_dir.ptr,
+ f->f_base.len, f->f_base.ptr,
+ f->f_suffix.len, f->f_suffix.ptr,
+ f->f_member.len, f->f_member.ptr,
+ file->value );
+# endif
+}
+
+
+/*
+ * path_parent() - make a PATHNAME point to its parent dir
+ */
+
+void path_parent( PATHNAME * f )
+{
+ if ( f->f_base.len )
+ {
+ f->f_base.ptr =
+ f->f_suffix.ptr =
+ f->f_member.ptr = "";
+
+ f->f_base.len =
+ f->f_suffix.len =
+ f->f_member.len = 0;
+ }
+ else
+ {
+ f->parent = 1;
+ }
+}
+
+# endif /* VMS */
diff --git a/jam-files/engine/pwd.c b/jam-files/engine/pwd.c
new file mode 100644
index 00000000..90c8eb17
--- /dev/null
+++ b/jam-files/engine/pwd.c
@@ -0,0 +1,66 @@
+/* Copyright Vladimir Prus 2002, Rene Rivera 2005. Distributed under the Boost */
+/* Software License, Version 1.0. (See accompanying */
+/* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */
+
+#include "jam.h"
+#include "lists.h"
+#include "newstr.h"
+#include "pathsys.h"
+#include "mem.h"
+
+#include <limits.h>
+#include <errno.h>
+
+/* MinGW on windows declares PATH_MAX in limits.h */
+#if defined(NT) && ! defined(__GNUC__)
+#include <direct.h>
+#define PATH_MAX _MAX_PATH
+#else
+#include <unistd.h>
+#if defined(__COMO__)
+ #include <linux/limits.h>
+#endif
+#endif
+
+#ifndef PATH_MAX
+ #define PATH_MAX 1024
+#endif
+
+/* The current directory can't change in bjam, so optimize this to cache
+** the result.
+*/
+static char * pwd_result = NULL;
+
+
+LIST*
+pwd(void)
+{
+ if (!pwd_result)
+ {
+ int buffer_size = PATH_MAX;
+ char * result_buffer = 0;
+ do
+ {
+ char * buffer = BJAM_MALLOC_RAW(buffer_size);
+ result_buffer = getcwd(buffer,buffer_size);
+ if (result_buffer)
+ {
+ #ifdef NT
+ pwd_result = short_path_to_long_path(result_buffer);
+ #else
+ pwd_result = newstr(result_buffer);
+ #endif
+ }
+ buffer_size *= 2;
+ BJAM_FREE_RAW(buffer);
+ }
+ while (!pwd_result && errno == ERANGE);
+
+ if (!pwd_result)
+ {
+ perror("can not get current directory");
+ return L0;
+ }
+ }
+ return list_new(L0, pwd_result);
+}
diff --git a/jam-files/engine/pwd.h b/jam-files/engine/pwd.h
new file mode 100644
index 00000000..37cb531e
--- /dev/null
+++ b/jam-files/engine/pwd.h
@@ -0,0 +1,10 @@
+/* Copyright Vladimir Prus 2002. Distributed under the Boost */
+/* Software License, Version 1.0. (See accompanying */
+/* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */
+
+#ifndef PWD_H
+#define PWD_H
+
+LIST* pwd(void);
+
+#endif
diff --git a/jam-files/engine/regexp.c b/jam-files/engine/regexp.c
new file mode 100644
index 00000000..30197a2f
--- /dev/null
+++ b/jam-files/engine/regexp.c
@@ -0,0 +1,1328 @@
+/*
+ * regcomp and regexec -- regsub and regerror are elsewhere
+ *
+ * Copyright (c) 1986 by University of Toronto.
+ * Written by Henry Spencer. Not derived from licensed software.
+ *
+ * Permission is granted to anyone to use this software for any
+ * purpose on any computer system, and to redistribute it freely,
+ * subject to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of
+ * this software, no matter how awful, even if they arise
+ * from defects in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either
+ * by explicit claim or by omission.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *** THIS IS AN ALTERED VERSION. It was altered by John Gilmore,
+ *** hoptoad!gnu, on 27 Dec 1986, to add \n as an alternative to |
+ *** to assist in implementing egrep.
+ *** THIS IS AN ALTERED VERSION. It was altered by John Gilmore,
+ *** hoptoad!gnu, on 27 Dec 1986, to add \< and \> for word-matching
+ *** as in BSD grep and ex.
+ *** THIS IS AN ALTERED VERSION. It was altered by John Gilmore,
+ *** hoptoad!gnu, on 28 Dec 1986, to optimize characters quoted with \.
+ *** THIS IS AN ALTERED VERSION. It was altered by James A. Woods,
+ *** ames!jaw, on 19 June 1987, to quash a regcomp() redundancy.
+ *** THIS IS AN ALTERED VERSION. It was altered by Christopher Seiwald
+ *** seiwald@vix.com, on 28 August 1993, for use in jam. Regmagic.h
+ *** was moved into regexp.h, and the include of regexp.h now uses "'s
+ *** to avoid conflicting with the system regexp.h. Const, bless its
+ *** soul, was removed so it can compile everywhere. The declaration
+ *** of strchr() was in conflict on AIX, so it was removed (as it is
+ *** happily defined in string.h).
+ *** THIS IS AN ALTERED VERSION. It was altered by Christopher Seiwald
+ *** seiwald@perforce.com, on 20 January 2000, to use function prototypes.
+ *
+ * Beware that some of this code is subtly aware of the way operator precedence
+ * is structured in regular expressions. Serious changes in regular-expression
+ * syntax might require a total rethink.
+ */
+
+
+#include "jam.h"
+#include "regexp.h"
+#include <stdio.h>
+#include <ctype.h>
+#ifndef ultrix
+ #include <stdlib.h>
+#endif
+#include <string.h>
+
+
+/*
+ * The "internal use only" fields in regexp.h are present to pass info from
+ * compile to execute that permits the execute phase to run lots faster on
+ * simple cases. They are:
+ :
+ * regstart char that must begin a match; '\0' if none obvious.
+ * reganch is the match anchored (at beginning-of-line only)?
+ * regmust string (pointer into program) that match must include, or NULL.
+ * regmlen length of regmust string.
+ *
+ * Regstart and reganch permit very fast decisions on suitable starting points
+ * for a match, cutting down the work a lot. Regmust permits fast rejection of
+ * lines that cannot possibly match. The regmust tests are costly enough that
+ * regcomp() supplies a regmust only if the r.e. contains something potentially
+ * expensive (at present, the only such thing detected is * or + at the start of
+ * the r.e., which can involve a lot of backup). Regmlen is supplied because the
+ * test in regexec() needs it and regcomp() is computing it anyway.
+ */
+
+/*
+ * Structure for regexp "program". This is essentially a linear encoding of a
+ * nondeterministic finite-state machine (aka syntax charts or "railroad normal
+ * form" in parsing technology). Each node is an opcode plus a "next" pointer,
+ * possibly plus an operand. "Next" pointers of all nodes except BRANCH
+ * implement concatenation; a "next" pointer with a BRANCH on both ends of it is
+ * connecting two alternatives. [Here we have one of the subtle syntax
+ * dependencies: an individual BRANCH, as opposed to a collection of them, is
+ * never concatenated with anything because of operator precedence.] The operand
+ * of some types of node is a literal string; for others, it is a node leading
+ * into a sub-FSM. In particular, the operand of a BRANCH node is the first node
+ * of the branch. [NB this is *not* a tree structure: the tail of the branch
+ * connects to the thing following the set of BRANCHes.] The opcodes are:
+ */
+
+/* definition number opnd? meaning */
+#define END 0 /* no End of program. */
+#define BOL 1 /* no Match "" at beginning of line. */
+#define EOL 2 /* no Match "" at end of line. */
+#define ANY 3 /* no Match any one character. */
+#define ANYOF 4 /* str Match any character in this string. */
+#define ANYBUT 5 /* str Match any character not in this string. */
+#define BRANCH 6 /* node Match this alternative, or the next... */
+#define BACK 7 /* no Match "", "next" ptr points backward. */
+#define EXACTLY 8 /* str Match this string. */
+#define NOTHING 9 /* no Match empty string. */
+#define STAR 10 /* node Match this (simple) thing 0 or more times. */
+#define PLUS 11 /* node Match this (simple) thing 1 or more times. */
+#define WORDA 12 /* no Match "" at wordchar, where prev is nonword */
+#define WORDZ 13 /* no Match "" at nonwordchar, where prev is word */
+#define OPEN 20 /* no Mark this point in input as start of #n. */
+ /* OPEN+1 is number 1, etc. */
+#define CLOSE 30 /* no Analogous to OPEN. */
+
+
+/*
+ * Opcode notes:
+ *
+ * BRANCH The set of branches constituting a single choice are hooked
+ * together with their "next" pointers, since precedence prevents
+ * anything being concatenated to any individual branch. The
+ * "next" pointer of the last BRANCH in a choice points to the
+ * thing following the whole choice. This is also where the
+ * final "next" pointer of each individual branch points; each
+ * branch starts with the operand node of a BRANCH node.
+ *
+ * BACK Normal "next" pointers all implicitly point forward; BACK
+ * exists to make loop structures possible.
+ *
+ * STAR,PLUS '?', and complex '*' and '+', are implemented as circular
+ * BRANCH structures using BACK. Simple cases (one character
+ * per match) are implemented with STAR and PLUS for speed
+ * and to minimize recursive plunges.
+ *
+ * OPEN,CLOSE ...are numbered at compile time.
+ */
+
+/*
+ * A node is one char of opcode followed by two chars of "next" pointer.
+ * "Next" pointers are stored as two 8-bit pieces, high order first. The
+ * value is a positive offset from the opcode of the node containing it.
+ * An operand, if any, simply follows the node. (Note that much of the
+ * code generation knows about this implicit relationship.)
+ *
+ * Using two bytes for the "next" pointer is vast overkill for most things,
+ * but allows patterns to get big without disasters.
+ */
+#define OP(p) (*(p))
+#define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377))
+#define OPERAND(p) ((p) + 3)
+
+/*
+ * See regmagic.h for one further detail of program structure.
+ */
+
+
+/*
+ * Utility definitions.
+ */
+#ifndef CHARBITS
+#define UCHARAT(p) ((int)*(unsigned char *)(p))
+#else
+#define UCHARAT(p) ((int)*(p)&CHARBITS)
+#endif
+
+#define FAIL(m) { regerror(m); return(NULL); }
+#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?')
+
+/*
+ * Flags to be passed up and down.
+ */
+#define HASWIDTH 01 /* Known never to match null string. */
+#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */
+#define SPSTART 04 /* Starts with * or +. */
+#define WORST 0 /* Worst case. */
+
+/*
+ * Global work variables for regcomp().
+ */
+static char *regparse; /* Input-scan pointer. */
+static int regnpar; /* () count. */
+static char regdummy;
+static char *regcode; /* Code-emit pointer; &regdummy = don't. */
+static long regsize; /* Code size. */
+
+/*
+ * Forward declarations for regcomp()'s friends.
+ */
+#ifndef STATIC
+#define STATIC static
+#endif
+STATIC char *reg( int paren, int *flagp );
+STATIC char *regbranch( int *flagp );
+STATIC char *regpiece( int *flagp );
+STATIC char *regatom( int *flagp );
+STATIC char *regnode( int op );
+STATIC char *regnext( register char *p );
+STATIC void regc( int b );
+STATIC void reginsert( char op, char *opnd );
+STATIC void regtail( char *p, char *val );
+STATIC void regoptail( char *p, char *val );
+#ifdef STRCSPN
+STATIC int strcspn();
+#endif
+
+/*
+ - regcomp - compile a regular expression into internal code
+ *
+ * We can't allocate space until we know how big the compiled form will be,
+ * but we can't compile it (and thus know how big it is) until we've got a
+ * place to put the code. So we cheat: we compile it twice, once with code
+ * generation turned off and size counting turned on, and once "for real".
+ * This also means that we don't allocate space until we are sure that the
+ * thing really will compile successfully, and we never have to move the
+ * code and thus invalidate pointers into it. (Note that it has to be in
+ * one piece because free() must be able to free it all.)
+ *
+ * Beware that the optimization-preparation code in here knows about some
+ * of the structure of the compiled regexp.
+ */
+regexp *
+regcomp( char *exp )
+{
+ register regexp *r;
+ register char *scan;
+ register char *longest;
+ register unsigned len;
+ int flags;
+
+ if (exp == NULL)
+ FAIL("NULL argument");
+
+ /* First pass: determine size, legality. */
+#ifdef notdef
+ if (exp[0] == '.' && exp[1] == '*') exp += 2; /* aid grep */
+#endif
+ regparse = (char *)exp;
+ regnpar = 1;
+ regsize = 0L;
+ regcode = &regdummy;
+ regc(MAGIC);
+ if (reg(0, &flags) == NULL)
+ return(NULL);
+
+ /* Small enough for pointer-storage convention? */
+ if (regsize >= 32767L) /* Probably could be 65535L. */
+ FAIL("regexp too big");
+
+ /* Allocate space. */
+ r = (regexp *)BJAM_MALLOC(sizeof(regexp) + (unsigned)regsize);
+ if (r == NULL)
+ FAIL("out of space");
+
+ /* Second pass: emit code. */
+ regparse = (char *)exp;
+ regnpar = 1;
+ regcode = r->program;
+ regc(MAGIC);
+ if (reg(0, &flags) == NULL)
+ return(NULL);
+
+ /* Dig out information for optimizations. */
+ r->regstart = '\0'; /* Worst-case defaults. */
+ r->reganch = 0;
+ r->regmust = NULL;
+ r->regmlen = 0;
+ scan = r->program+1; /* First BRANCH. */
+ if (OP(regnext(scan)) == END) { /* Only one top-level choice. */
+ scan = OPERAND(scan);
+
+ /* Starting-point info. */
+ if (OP(scan) == EXACTLY)
+ r->regstart = *OPERAND(scan);
+ else if (OP(scan) == BOL)
+ r->reganch++;
+
+ /*
+ * If there's something expensive in the r.e., find the
+ * longest literal string that must appear and make it the
+ * regmust. Resolve ties in favor of later strings, since
+ * the regstart check works with the beginning of the r.e.
+ * and avoiding duplication strengthens checking. Not a
+ * strong reason, but sufficient in the absence of others.
+ */
+ if (flags&SPSTART) {
+ longest = NULL;
+ len = 0;
+ for (; scan != NULL; scan = regnext(scan))
+ if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) {
+ longest = OPERAND(scan);
+ len = strlen(OPERAND(scan));
+ }
+ r->regmust = longest;
+ r->regmlen = len;
+ }
+ }
+
+ return(r);
+}
+
+/*
+ - reg - regular expression, i.e. main body or parenthesized thing
+ *
+ * Caller must absorb opening parenthesis.
+ *
+ * Combining parenthesis handling with the base level of regular expression
+ * is a trifle forced, but the need to tie the tails of the branches to what
+ * follows makes it hard to avoid.
+ */
+static char *
+reg(
+ int paren, /* Parenthesized? */
+ int *flagp )
+{
+ register char *ret;
+ register char *br;
+ register char *ender;
+ register int parno = 0;
+ int flags;
+
+ *flagp = HASWIDTH; /* Tentatively. */
+
+ /* Make an OPEN node, if parenthesized. */
+ if (paren) {
+ if (regnpar >= NSUBEXP)
+ FAIL("too many ()");
+ parno = regnpar;
+ regnpar++;
+ ret = regnode(OPEN+parno);
+ } else
+ ret = NULL;
+
+ /* Pick up the branches, linking them together. */
+ br = regbranch(&flags);
+ if (br == NULL)
+ return(NULL);
+ if (ret != NULL)
+ regtail(ret, br); /* OPEN -> first. */
+ else
+ ret = br;
+ if (!(flags&HASWIDTH))
+ *flagp &= ~HASWIDTH;
+ *flagp |= flags&SPSTART;
+ while (*regparse == '|' || *regparse == '\n') {
+ regparse++;
+ br = regbranch(&flags);
+ if (br == NULL)
+ return(NULL);
+ regtail(ret, br); /* BRANCH -> BRANCH. */
+ if (!(flags&HASWIDTH))
+ *flagp &= ~HASWIDTH;
+ *flagp |= flags&SPSTART;
+ }
+
+ /* Make a closing node, and hook it on the end. */
+ ender = regnode((paren) ? CLOSE+parno : END);
+ regtail(ret, ender);
+
+ /* Hook the tails of the branches to the closing node. */
+ for (br = ret; br != NULL; br = regnext(br))
+ regoptail(br, ender);
+
+ /* Check for proper termination. */
+ if (paren && *regparse++ != ')') {
+ FAIL("unmatched ()");
+ } else if (!paren && *regparse != '\0') {
+ if (*regparse == ')') {
+ FAIL("unmatched ()");
+ } else
+ FAIL("junk on end"); /* "Can't happen". */
+ /* NOTREACHED */
+ }
+
+ return(ret);
+}
+
+/*
+ - regbranch - one alternative of an | operator
+ *
+ * Implements the concatenation operator.
+ */
+static char *
+regbranch( int *flagp )
+{
+ register char *ret;
+ register char *chain;
+ register char *latest;
+ int flags;
+
+ *flagp = WORST; /* Tentatively. */
+
+ ret = regnode(BRANCH);
+ chain = NULL;
+ while (*regparse != '\0' && *regparse != ')' &&
+ *regparse != '\n' && *regparse != '|') {
+ latest = regpiece(&flags);
+ if (latest == NULL)
+ return(NULL);
+ *flagp |= flags&HASWIDTH;
+ if (chain == NULL) /* First piece. */
+ *flagp |= flags&SPSTART;
+ else
+ regtail(chain, latest);
+ chain = latest;
+ }
+ if (chain == NULL) /* Loop ran zero times. */
+ (void) regnode(NOTHING);
+
+ return(ret);
+}
+
+/*
+ - regpiece - something followed by possible [*+?]
+ *
+ * Note that the branching code sequences used for ? and the general cases
+ * of * and + are somewhat optimized: they use the same NOTHING node as
+ * both the endmarker for their branch list and the body of the last branch.
+ * It might seem that this node could be dispensed with entirely, but the
+ * endmarker role is not redundant.
+ */
+static char *
+regpiece( int *flagp )
+{
+ register char *ret;
+ register char op;
+ register char *next;
+ int flags;
+
+ ret = regatom(&flags);
+ if (ret == NULL)
+ return(NULL);
+
+ op = *regparse;
+ if (!ISMULT(op)) {
+ *flagp = flags;
+ return(ret);
+ }
+
+ if (!(flags&HASWIDTH) && op != '?')
+ FAIL("*+ operand could be empty");
+ *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH);
+
+ if (op == '*' && (flags&SIMPLE))
+ reginsert(STAR, ret);
+ else if (op == '*') {
+ /* Emit x* as (x&|), where & means "self". */
+ reginsert(BRANCH, ret); /* Either x */
+ regoptail(ret, regnode(BACK)); /* and loop */
+ regoptail(ret, ret); /* back */
+ regtail(ret, regnode(BRANCH)); /* or */
+ regtail(ret, regnode(NOTHING)); /* null. */
+ } else if (op == '+' && (flags&SIMPLE))
+ reginsert(PLUS, ret);
+ else if (op == '+') {
+ /* Emit x+ as x(&|), where & means "self". */
+ next = regnode(BRANCH); /* Either */
+ regtail(ret, next);
+ regtail(regnode(BACK), ret); /* loop back */
+ regtail(next, regnode(BRANCH)); /* or */
+ regtail(ret, regnode(NOTHING)); /* null. */
+ } else if (op == '?') {
+ /* Emit x? as (x|) */
+ reginsert(BRANCH, ret); /* Either x */
+ regtail(ret, regnode(BRANCH)); /* or */
+ next = regnode(NOTHING); /* null. */
+ regtail(ret, next);
+ regoptail(ret, next);
+ }
+ regparse++;
+ if (ISMULT(*regparse))
+ FAIL("nested *?+");
+
+ return(ret);
+}
+
+/*
+ - regatom - the lowest level
+ *
+ * Optimization: gobbles an entire sequence of ordinary characters so that
+ * it can turn them into a single node, which is smaller to store and
+ * faster to run. Backslashed characters are exceptions, each becoming a
+ * separate node; the code is simpler that way and it's not worth fixing.
+ */
+static char *
+regatom( int *flagp )
+{
+ register char *ret;
+ int flags;
+
+ *flagp = WORST; /* Tentatively. */
+
+ switch (*regparse++) {
+ /* FIXME: these chars only have meaning at beg/end of pat? */
+ case '^':
+ ret = regnode(BOL);
+ break;
+ case '$':
+ ret = regnode(EOL);
+ break;
+ case '.':
+ ret = regnode(ANY);
+ *flagp |= HASWIDTH|SIMPLE;
+ break;
+ case '[': {
+ register int classr;
+ register int classend;
+
+ if (*regparse == '^') { /* Complement of range. */
+ ret = regnode(ANYBUT);
+ regparse++;
+ } else
+ ret = regnode(ANYOF);
+ if (*regparse == ']' || *regparse == '-')
+ regc(*regparse++);
+ while (*regparse != '\0' && *regparse != ']') {
+ if (*regparse == '-') {
+ regparse++;
+ if (*regparse == ']' || *regparse == '\0')
+ regc('-');
+ else {
+ classr = UCHARAT(regparse-2)+1;
+ classend = UCHARAT(regparse);
+ if (classr > classend+1)
+ FAIL("invalid [] range");
+ for (; classr <= classend; classr++)
+ regc(classr);
+ regparse++;
+ }
+ } else
+ regc(*regparse++);
+ }
+ regc('\0');
+ if (*regparse != ']')
+ FAIL("unmatched []");
+ regparse++;
+ *flagp |= HASWIDTH|SIMPLE;
+ }
+ break;
+ case '(':
+ ret = reg(1, &flags);
+ if (ret == NULL)
+ return(NULL);
+ *flagp |= flags&(HASWIDTH|SPSTART);
+ break;
+ case '\0':
+ case '|':
+ case '\n':
+ case ')':
+ FAIL("internal urp"); /* Supposed to be caught earlier. */
+ break;
+ case '?':
+ case '+':
+ case '*':
+ FAIL("?+* follows nothing");
+ break;
+ case '\\':
+ switch (*regparse++) {
+ case '\0':
+ FAIL("trailing \\");
+ break;
+ case '<':
+ ret = regnode(WORDA);
+ break;
+ case '>':
+ ret = regnode(WORDZ);
+ break;
+ /* FIXME: Someday handle \1, \2, ... */
+ default:
+ /* Handle general quoted chars in exact-match routine */
+ goto de_fault;
+ }
+ break;
+ de_fault:
+ default:
+ /*
+ * Encode a string of characters to be matched exactly.
+ *
+ * This is a bit tricky due to quoted chars and due to
+ * '*', '+', and '?' taking the SINGLE char previous
+ * as their operand.
+ *
+ * On entry, the char at regparse[-1] is going to go
+ * into the string, no matter what it is. (It could be
+ * following a \ if we are entered from the '\' case.)
+ *
+ * Basic idea is to pick up a good char in ch and
+ * examine the next char. If it's *+? then we twiddle.
+ * If it's \ then we frozzle. If it's other magic char
+ * we push ch and terminate the string. If none of the
+ * above, we push ch on the string and go around again.
+ *
+ * regprev is used to remember where "the current char"
+ * starts in the string, if due to a *+? we need to back
+ * up and put the current char in a separate, 1-char, string.
+ * When regprev is NULL, ch is the only char in the
+ * string; this is used in *+? handling, and in setting
+ * flags |= SIMPLE at the end.
+ */
+ {
+ char *regprev;
+ register char ch;
+
+ regparse--; /* Look at cur char */
+ ret = regnode(EXACTLY);
+ for ( regprev = 0 ; ; ) {
+ ch = *regparse++; /* Get current char */
+ switch (*regparse) { /* look at next one */
+
+ default:
+ regc(ch); /* Add cur to string */
+ break;
+
+ case '.': case '[': case '(':
+ case ')': case '|': case '\n':
+ case '$': case '^':
+ case '\0':
+ /* FIXME, $ and ^ should not always be magic */
+ magic:
+ regc(ch); /* dump cur char */
+ goto done; /* and we are done */
+
+ case '?': case '+': case '*':
+ if (!regprev) /* If just ch in str, */
+ goto magic; /* use it */
+ /* End mult-char string one early */
+ regparse = regprev; /* Back up parse */
+ goto done;
+
+ case '\\':
+ regc(ch); /* Cur char OK */
+ switch (regparse[1]){ /* Look after \ */
+ case '\0':
+ case '<':
+ case '>':
+ /* FIXME: Someday handle \1, \2, ... */
+ goto done; /* Not quoted */
+ default:
+ /* Backup point is \, scan * point is after it. */
+ regprev = regparse;
+ regparse++;
+ continue; /* NOT break; */
+ }
+ }
+ regprev = regparse; /* Set backup point */
+ }
+ done:
+ regc('\0');
+ *flagp |= HASWIDTH;
+ if (!regprev) /* One char? */
+ *flagp |= SIMPLE;
+ }
+ break;
+ }
+
+ return(ret);
+}
+
+/*
+ - regnode - emit a node
+ */
+static char * /* Location. */
+regnode( int op )
+{
+ register char *ret;
+ register char *ptr;
+
+ ret = regcode;
+ if (ret == &regdummy) {
+ regsize += 3;
+ return(ret);
+ }
+
+ ptr = ret;
+ *ptr++ = op;
+ *ptr++ = '\0'; /* Null "next" pointer. */
+ *ptr++ = '\0';
+ regcode = ptr;
+
+ return(ret);
+}
+
+/*
+ - regc - emit (if appropriate) a byte of code
+ */
+static void
+regc( int b )
+{
+ if (regcode != &regdummy)
+ *regcode++ = b;
+ else
+ regsize++;
+}
+
+/*
+ - reginsert - insert an operator in front of already-emitted operand
+ *
+ * Means relocating the operand.
+ */
+static void
+reginsert(
+ char op,
+ char *opnd )
+{
+ register char *src;
+ register char *dst;
+ register char *place;
+
+ if (regcode == &regdummy) {
+ regsize += 3;
+ return;
+ }
+
+ src = regcode;
+ regcode += 3;
+ dst = regcode;
+ while (src > opnd)
+ *--dst = *--src;
+
+ place = opnd; /* Op node, where operand used to be. */
+ *place++ = op;
+ *place++ = '\0';
+ *place++ = '\0';
+}
+
+/*
+ - regtail - set the next-pointer at the end of a node chain
+ */
+static void
+regtail(
+ char *p,
+ char *val )
+{
+ register char *scan;
+ register char *temp;
+ register int offset;
+
+ if (p == &regdummy)
+ return;
+
+ /* Find last node. */
+ scan = p;
+ for (;;) {
+ temp = regnext(scan);
+ if (temp == NULL)
+ break;
+ scan = temp;
+ }
+
+ if (OP(scan) == BACK)
+ offset = scan - val;
+ else
+ offset = val - scan;
+ *(scan+1) = (offset>>8)&0377;
+ *(scan+2) = offset&0377;
+}
+
+/*
+ - regoptail - regtail on operand of first argument; nop if operandless
+ */
+
+static void
+regoptail(
+ char *p,
+ char *val )
+{
+ /* "Operandless" and "op != BRANCH" are synonymous in practice. */
+ if (p == NULL || p == &regdummy || OP(p) != BRANCH)
+ return;
+ regtail(OPERAND(p), val);
+}
+
+/*
+ * regexec and friends
+ */
+
+/*
+ * Global work variables for regexec().
+ */
+static char *reginput; /* String-input pointer. */
+static char *regbol; /* Beginning of input, for ^ check. */
+static char **regstartp; /* Pointer to startp array. */
+static char **regendp; /* Ditto for endp. */
+
+/*
+ * Forwards.
+ */
+STATIC int regtry( regexp *prog, char *string );
+STATIC int regmatch( char *prog );
+STATIC int regrepeat( char *p );
+
+#ifdef DEBUG
+int regnarrate = 0;
+void regdump();
+STATIC char *regprop();
+#endif
+
+/*
+ - regexec - match a regexp against a string
+ */
+int
+regexec(
+ register regexp *prog,
+ register char *string )
+{
+ register char *s;
+
+ /* Be paranoid... */
+ if (prog == NULL || string == NULL) {
+ regerror("NULL parameter");
+ return(0);
+ }
+
+ /* Check validity of program. */
+ if (UCHARAT(prog->program) != MAGIC) {
+ regerror("corrupted program");
+ return(0);
+ }
+
+ /* If there is a "must appear" string, look for it. */
+ if ( prog->regmust != NULL )
+ {
+ s = (char *)string;
+ while ( ( s = strchr( s, prog->regmust[ 0 ] ) ) != NULL )
+ {
+ if ( !strncmp( s, prog->regmust, prog->regmlen ) )
+ break; /* Found it. */
+ ++s;
+ }
+ if ( s == NULL ) /* Not present. */
+ return 0;
+ }
+
+ /* Mark beginning of line for ^ . */
+ regbol = (char *)string;
+
+ /* Simplest case: anchored match need be tried only once. */
+ if ( prog->reganch )
+ return regtry( prog, string );
+
+ /* Messy cases: unanchored match. */
+ s = (char *)string;
+ if (prog->regstart != '\0')
+ /* We know what char it must start with. */
+ while ((s = strchr(s, prog->regstart)) != NULL) {
+ if (regtry(prog, s))
+ return(1);
+ s++;
+ }
+ else
+ /* We do not -- general case. */
+ do {
+ if ( regtry( prog, s ) )
+ return( 1 );
+ } while ( *s++ != '\0' );
+
+ /* Failure. */
+ return 0;
+}
+
+
+/*
+ * regtry() - try match at specific point.
+ */
+
+static int /* 0 failure, 1 success */
+regtry(
+ regexp *prog,
+ char *string )
+{
+ register int i;
+ register char * * sp;
+ register char * * ep;
+
+ reginput = string;
+ regstartp = prog->startp;
+ regendp = prog->endp;
+
+ sp = prog->startp;
+ ep = prog->endp;
+ for ( i = NSUBEXP; i > 0; --i )
+ {
+ *sp++ = NULL;
+ *ep++ = NULL;
+ }
+ if ( regmatch( prog->program + 1 ) )
+ {
+ prog->startp[ 0 ] = string;
+ prog->endp[ 0 ] = reginput;
+ return 1;
+ }
+ else
+ return 0;
+}
+
+
+/*
+ * regmatch() - main matching routine.
+ *
+ * Conceptually the strategy is simple: check to see whether the current node
+ * matches, call self recursively to see whether the rest matches, and then act
+ * accordingly. In practice we make some effort to avoid recursion, in
+ * particular by going through "ordinary" nodes (that do not need to know
+ * whether the rest of the match failed) by a loop instead of by recursion.
+ */
+
+static int /* 0 failure, 1 success */
+regmatch( char * prog )
+{
+ char * scan; /* Current node. */
+ char * next; /* Next node. */
+
+ scan = prog;
+#ifdef DEBUG
+ if (scan != NULL && regnarrate)
+ fprintf(stderr, "%s(\n", regprop(scan));
+#endif
+ while (scan != NULL) {
+#ifdef DEBUG
+ if (regnarrate)
+ fprintf(stderr, "%s...\n", regprop(scan));
+#endif
+ next = regnext(scan);
+
+ switch (OP(scan)) {
+ case BOL:
+ if (reginput != regbol)
+ return(0);
+ break;
+ case EOL:
+ if (*reginput != '\0')
+ return(0);
+ break;
+ case WORDA:
+ /* Must be looking at a letter, digit, or _ */
+ if ((!isalnum(*reginput)) && *reginput != '_')
+ return(0);
+ /* Prev must be BOL or nonword */
+ if (reginput > regbol &&
+ (isalnum(reginput[-1]) || reginput[-1] == '_'))
+ return(0);
+ break;
+ case WORDZ:
+ /* Must be looking at non letter, digit, or _ */
+ if (isalnum(*reginput) || *reginput == '_')
+ return(0);
+ /* We don't care what the previous char was */
+ break;
+ case ANY:
+ if (*reginput == '\0')
+ return(0);
+ reginput++;
+ break;
+ case EXACTLY: {
+ register int len;
+ register char *opnd;
+
+ opnd = OPERAND(scan);
+ /* Inline the first character, for speed. */
+ if (*opnd != *reginput)
+ return(0);
+ len = strlen(opnd);
+ if (len > 1 && strncmp(opnd, reginput, len) != 0)
+ return(0);
+ reginput += len;
+ }
+ break;
+ case ANYOF:
+ if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == NULL)
+ return(0);
+ reginput++;
+ break;
+ case ANYBUT:
+ if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != NULL)
+ return(0);
+ reginput++;
+ break;
+ case NOTHING:
+ break;
+ case BACK:
+ break;
+ case OPEN+1:
+ case OPEN+2:
+ case OPEN+3:
+ case OPEN+4:
+ case OPEN+5:
+ case OPEN+6:
+ case OPEN+7:
+ case OPEN+8:
+ case OPEN+9: {
+ register int no;
+ register char *save;
+
+ no = OP(scan) - OPEN;
+ save = reginput;
+
+ if (regmatch(next)) {
+ /*
+ * Don't set startp if some later
+ * invocation of the same parentheses
+ * already has.
+ */
+ if (regstartp[no] == NULL)
+ regstartp[no] = save;
+ return(1);
+ } else
+ return(0);
+ }
+ break;
+ case CLOSE+1:
+ case CLOSE+2:
+ case CLOSE+3:
+ case CLOSE+4:
+ case CLOSE+5:
+ case CLOSE+6:
+ case CLOSE+7:
+ case CLOSE+8:
+ case CLOSE+9: {
+ register int no;
+ register char *save;
+
+ no = OP(scan) - CLOSE;
+ save = reginput;
+
+ if (regmatch(next)) {
+ /*
+ * Don't set endp if some later
+ * invocation of the same parentheses
+ * already has.
+ */
+ if (regendp[no] == NULL)
+ regendp[no] = save;
+ return(1);
+ } else
+ return(0);
+ }
+ break;
+ case BRANCH: {
+ register char *save;
+
+ if (OP(next) != BRANCH) /* No choice. */
+ next = OPERAND(scan); /* Avoid recursion. */
+ else {
+ do {
+ save = reginput;
+ if (regmatch(OPERAND(scan)))
+ return(1);
+ reginput = save;
+ scan = regnext(scan);
+ } while (scan != NULL && OP(scan) == BRANCH);
+ return(0);
+ /* NOTREACHED */
+ }
+ }
+ break;
+ case STAR:
+ case PLUS: {
+ register char nextch;
+ register int no;
+ register char *save;
+ register int min;
+
+ /*
+ * Lookahead to avoid useless match attempts
+ * when we know what character comes next.
+ */
+ nextch = '\0';
+ if (OP(next) == EXACTLY)
+ nextch = *OPERAND(next);
+ min = (OP(scan) == STAR) ? 0 : 1;
+ save = reginput;
+ no = regrepeat(OPERAND(scan));
+ while (no >= min) {
+ /* If it could work, try it. */
+ if (nextch == '\0' || *reginput == nextch)
+ if (regmatch(next))
+ return(1);
+ /* Couldn't or didn't -- back up. */
+ no--;
+ reginput = save + no;
+ }
+ return(0);
+ }
+ break;
+ case END:
+ return(1); /* Success! */
+ break;
+ default:
+ regerror("memory corruption");
+ return(0);
+ break;
+ }
+
+ scan = next;
+ }
+
+ /*
+ * We get here only if there's trouble -- normally "case END" is
+ * the terminating point.
+ */
+ regerror("corrupted pointers");
+ return(0);
+}
+
+/*
+ - regrepeat - repeatedly match something simple, report how many
+ */
+static int
+regrepeat( char *p )
+{
+ register int count = 0;
+ register char *scan;
+ register char *opnd;
+
+ scan = reginput;
+ opnd = OPERAND(p);
+ switch (OP(p)) {
+ case ANY:
+ count = strlen(scan);
+ scan += count;
+ break;
+ case EXACTLY:
+ while (*opnd == *scan) {
+ count++;
+ scan++;
+ }
+ break;
+ case ANYOF:
+ while (*scan != '\0' && strchr(opnd, *scan) != NULL) {
+ count++;
+ scan++;
+ }
+ break;
+ case ANYBUT:
+ while (*scan != '\0' && strchr(opnd, *scan) == NULL) {
+ count++;
+ scan++;
+ }
+ break;
+ default: /* Oh dear. Called inappropriately. */
+ regerror("internal foulup");
+ count = 0; /* Best compromise. */
+ break;
+ }
+ reginput = scan;
+
+ return(count);
+}
+
+/*
+ - regnext - dig the "next" pointer out of a node
+ */
+static char *
+regnext( register char *p )
+{
+ register int offset;
+
+ if (p == &regdummy)
+ return(NULL);
+
+ offset = NEXT(p);
+ if (offset == 0)
+ return(NULL);
+
+ if (OP(p) == BACK)
+ return(p-offset);
+ else
+ return(p+offset);
+}
+
+#ifdef DEBUG
+
+STATIC char *regprop();
+
+/*
+ - regdump - dump a regexp onto stdout in vaguely comprehensible form
+ */
+void
+regdump( regexp *r )
+{
+ register char *s;
+ register char op = EXACTLY; /* Arbitrary non-END op. */
+ register char *next;
+
+
+ s = r->program + 1;
+ while (op != END) { /* While that wasn't END last time... */
+ op = OP(s);
+ printf("%2d%s", s-r->program, regprop(s)); /* Where, what. */
+ next = regnext(s);
+ if (next == NULL) /* Next ptr. */
+ printf("(0)");
+ else
+ printf("(%d)", (s-r->program)+(next-s));
+ s += 3;
+ if (op == ANYOF || op == ANYBUT || op == EXACTLY) {
+ /* Literal string, where present. */
+ while (*s != '\0') {
+ putchar(*s);
+ s++;
+ }
+ s++;
+ }
+ putchar('\n');
+ }
+
+ /* Header fields of interest. */
+ if (r->regstart != '\0')
+ printf("start `%c' ", r->regstart);
+ if (r->reganch)
+ printf("anchored ");
+ if (r->regmust != NULL)
+ printf("must have \"%s\"", r->regmust);
+ printf("\n");
+}
+
+/*
+ - regprop - printable representation of opcode
+ */
+static char *
+regprop( char *op )
+{
+ register char *p;
+ static char buf[50];
+
+ (void) strcpy(buf, ":");
+
+ switch (OP(op)) {
+ case BOL:
+ p = "BOL";
+ break;
+ case EOL:
+ p = "EOL";
+ break;
+ case ANY:
+ p = "ANY";
+ break;
+ case ANYOF:
+ p = "ANYOF";
+ break;
+ case ANYBUT:
+ p = "ANYBUT";
+ break;
+ case BRANCH:
+ p = "BRANCH";
+ break;
+ case EXACTLY:
+ p = "EXACTLY";
+ break;
+ case NOTHING:
+ p = "NOTHING";
+ break;
+ case BACK:
+ p = "BACK";
+ break;
+ case END:
+ p = "END";
+ break;
+ case OPEN+1:
+ case OPEN+2:
+ case OPEN+3:
+ case OPEN+4:
+ case OPEN+5:
+ case OPEN+6:
+ case OPEN+7:
+ case OPEN+8:
+ case OPEN+9:
+ sprintf(buf+strlen(buf), "OPEN%d", OP(op)-OPEN);
+ p = NULL;
+ break;
+ case CLOSE+1:
+ case CLOSE+2:
+ case CLOSE+3:
+ case CLOSE+4:
+ case CLOSE+5:
+ case CLOSE+6:
+ case CLOSE+7:
+ case CLOSE+8:
+ case CLOSE+9:
+ sprintf(buf+strlen(buf), "CLOSE%d", OP(op)-CLOSE);
+ p = NULL;
+ break;
+ case STAR:
+ p = "STAR";
+ break;
+ case PLUS:
+ p = "PLUS";
+ break;
+ case WORDA:
+ p = "WORDA";
+ break;
+ case WORDZ:
+ p = "WORDZ";
+ break;
+ default:
+ regerror("corrupted opcode");
+ break;
+ }
+ if (p != NULL)
+ (void) strcat(buf, p);
+ return(buf);
+}
+#endif
+
+/*
+ * The following is provided for those people who do not have strcspn() in
+ * their C libraries. They should get off their butts and do something
+ * about it; at least one public-domain implementation of those (highly
+ * useful) string routines has been published on Usenet.
+ */
+#ifdef STRCSPN
+/*
+ * strcspn - find length of initial segment of s1 consisting entirely
+ * of characters not from s2
+ */
+
+static int
+strcspn(
+ char *s1,
+ char *s2 )
+{
+ register char *scan1;
+ register char *scan2;
+ register int count;
+
+ count = 0;
+ for (scan1 = s1; *scan1 != '\0'; scan1++) {
+ for (scan2 = s2; *scan2 != '\0';) /* ++ moved down. */
+ if (*scan1 == *scan2++)
+ return(count);
+ count++;
+ }
+ return(count);
+}
+#endif
diff --git a/jam-files/engine/regexp.h b/jam-files/engine/regexp.h
new file mode 100644
index 00000000..9d4604f6
--- /dev/null
+++ b/jam-files/engine/regexp.h
@@ -0,0 +1,32 @@
+/*
+ * Definitions etc. for regexp(3) routines.
+ *
+ * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof],
+ * not the System V one.
+ */
+#ifndef REGEXP_DWA20011023_H
+# define REGEXP_DWA20011023_H
+
+#define NSUBEXP 10
+typedef struct regexp {
+ char *startp[NSUBEXP];
+ char *endp[NSUBEXP];
+ char regstart; /* Internal use only. */
+ char reganch; /* Internal use only. */
+ char *regmust; /* Internal use only. */
+ int regmlen; /* Internal use only. */
+ char program[1]; /* Unwarranted chumminess with compiler. */
+} regexp;
+
+regexp *regcomp( char *exp );
+int regexec( regexp *prog, char *string );
+void regerror( char *s );
+
+/*
+ * The first byte of the regexp internal "program" is actually this magic
+ * number; the start node begins in the second byte.
+ */
+#define MAGIC 0234
+
+#endif
+
diff --git a/jam-files/engine/rules.c b/jam-files/engine/rules.c
new file mode 100644
index 00000000..a0be1d34
--- /dev/null
+++ b/jam-files/engine/rules.c
@@ -0,0 +1,810 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+# include "jam.h"
+# include "lists.h"
+# include "parse.h"
+# include "variable.h"
+# include "rules.h"
+# include "newstr.h"
+# include "hash.h"
+# include "modules.h"
+# include "search.h"
+# include "lists.h"
+# include "pathsys.h"
+# include "timestamp.h"
+
+/* This file is ALSO:
+ * Copyright 2001-2004 David Abrahams.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+/*
+ * rules.c - access to RULEs, TARGETs, and ACTIONs
+ *
+ * External routines:
+ *
+ * bindrule() - return pointer to RULE, creating it if necessary.
+ * bindtarget() - return pointer to TARGET, creating it if necessary.
+ * touch_target() - mark a target to simulate being new.
+ * targetlist() - turn list of target names into a TARGET chain.
+ * targetentry() - add a TARGET to a chain of TARGETS.
+ * actionlist() - append to an ACTION chain.
+ * addsettings() - add a deferred "set" command to a target.
+ * pushsettings() - set all target specific variables.
+ * popsettings() - reset target specific variables to their pre-push values.
+ * freesettings() - delete a settings list.
+ * rules_done() - free RULE and TARGET tables.
+ *
+ * 04/12/94 (seiwald) - actionlist() now just appends a single action.
+ * 08/23/94 (seiwald) - Support for '+=' (append to variable)
+ */
+
+static void set_rule_actions( RULE *, rule_actions * );
+static void set_rule_body ( RULE *, argument_list *, PARSE * procedure );
+
+static struct hash * targethash = 0;
+
+struct _located_target
+{
+ char * file_name;
+ TARGET * target;
+};
+typedef struct _located_target LOCATED_TARGET ;
+
+static struct hash * located_targets = 0;
+
+
+/*
+ * target_include() - adds the 'included' TARGET to the list of targets included
+ * by the 'including' TARGET. Such targets are modeled as dependencies of the
+ * internal include node belonging to the 'including' TARGET.
+ */
+
+void target_include( TARGET * including, TARGET * included )
+{
+ TARGET * internal;
+ if ( !including->includes )
+ {
+ including->includes = copytarget( including );
+ including->includes->original_target = including;
+ }
+ internal = including->includes;
+ internal->depends = targetentry( internal->depends, included );
+}
+
+
+/*
+ * enter_rule() - return pointer to RULE, creating it if necessary in
+ * target_module.
+ */
+
+static RULE * enter_rule( char * rulename, module_t * target_module )
+{
+ RULE rule;
+ RULE * r = &rule;
+
+ r->name = rulename;
+
+ if ( hashenter( demand_rules( target_module ), (HASHDATA * *)&r ) )
+ {
+ r->name = newstr( rulename ); /* never freed */
+ r->procedure = (PARSE *)0;
+ r->module = 0;
+ r->actions = 0;
+ r->arguments = 0;
+ r->exported = 0;
+ r->module = target_module;
+#ifdef HAVE_PYTHON
+ r->python_function = 0;
+#endif
+ }
+ return r;
+}
+
+
+/*
+ * define_rule() - return pointer to RULE, creating it if necessary in
+ * target_module. Prepare it to accept a body or action originating in
+ * src_module.
+ */
+
+static RULE * define_rule
+(
+ module_t * src_module,
+ char * rulename,
+ module_t * target_module
+)
+{
+ RULE * r = enter_rule( rulename, target_module );
+ if ( r->module != src_module ) /* if the rule was imported from elsewhere, clear it now */
+ {
+ set_rule_body( r, 0, 0 );
+ set_rule_actions( r, 0 );
+ r->module = src_module; /* r will be executed in the source module */
+ }
+ return r;
+}
+
+
+void rule_free( RULE * r )
+{
+ freestr( r->name );
+ r->name = "";
+ parse_free( r->procedure );
+ r->procedure = 0;
+ if ( r->arguments )
+ args_free( r->arguments );
+ r->arguments = 0;
+ if ( r->actions )
+ actions_free( r->actions );
+ r->actions = 0;
+}
+
+
+/*
+ * bindtarget() - return pointer to TARGET, creating it if necessary.
+ */
+
+TARGET * bindtarget( char const * target_name )
+{
+ TARGET target;
+ TARGET * t = &target;
+
+ if ( !targethash )
+ targethash = hashinit( sizeof( TARGET ), "targets" );
+
+ /* Perforce added const everywhere. No time to merge that change. */
+#ifdef NT
+ target_name = short_path_to_long_path( (char *)target_name );
+#endif
+ t->name = (char *)target_name;
+
+ if ( hashenter( targethash, (HASHDATA * *)&t ) )
+ {
+ memset( (char *)t, '\0', sizeof( *t ) );
+ t->name = newstr( (char *)target_name ); /* never freed */
+ t->boundname = t->name; /* default for T_FLAG_NOTFILE */
+ }
+
+ return t;
+}
+
+
+static void bind_explicitly_located_target( void * xtarget, void * data )
+{
+ TARGET * t = (TARGET *)xtarget;
+ if ( !( t->flags & T_FLAG_NOTFILE ) )
+ {
+ /* Check if there's a setting for LOCATE */
+ SETTINGS * s = t->settings;
+ for ( ; s ; s = s->next )
+ {
+ if ( strcmp( s->symbol, "LOCATE" ) == 0 )
+ {
+ pushsettings( t->settings );
+ /* We are binding a target with explicit LOCATE. So third
+ * argument is of no use: nothing will be returned through it.
+ */
+ t->boundname = search( t->name, &t->time, 0, 0 );
+ popsettings( t->settings );
+ break;
+ }
+ }
+ }
+}
+
+
+void bind_explicitly_located_targets()
+{
+ if ( targethash )
+ hashenumerate( targethash, bind_explicitly_located_target, (void *)0 );
+}
+
+
+/* TODO: It is probably not a good idea to use functions in other modules like
+ this. */
+void call_bind_rule( char * target, char * boundname );
+
+
+TARGET * search_for_target ( char * name, LIST * search_path )
+{
+ PATHNAME f[1];
+ string buf[1];
+ LOCATED_TARGET lt;
+ LOCATED_TARGET * lta = &lt;
+ time_t time;
+ int found = 0;
+ TARGET * result;
+
+ string_new( buf );
+
+ path_parse( name, f );
+
+ f->f_grist.ptr = 0;
+ f->f_grist.len = 0;
+
+ while ( search_path )
+ {
+ f->f_root.ptr = search_path->string;
+ f->f_root.len = strlen( search_path->string );
+
+ string_truncate( buf, 0 );
+ path_build( f, buf, 1 );
+
+ lt.file_name = buf->value ;
+
+ if ( !located_targets )
+ located_targets = hashinit( sizeof(LOCATED_TARGET),
+ "located targets" );
+
+ if ( hashcheck( located_targets, (HASHDATA * *)&lta ) )
+ {
+ return lta->target;
+ }
+
+ timestamp( buf->value, &time );
+ if ( time )
+ {
+ found = 1;
+ break;
+ }
+
+ search_path = list_next( search_path );
+ }
+
+ if ( !found )
+ {
+ f->f_root.ptr = 0;
+ f->f_root.len = 0;
+
+ string_truncate( buf, 0 );
+ path_build( f, buf, 1 );
+
+ timestamp( buf->value, &time );
+ }
+
+ result = bindtarget( name );
+ result->boundname = newstr( buf->value );
+ result->time = time;
+ result->binding = time ? T_BIND_EXISTS : T_BIND_MISSING;
+
+ call_bind_rule( result->name, result->boundname );
+
+ string_free( buf );
+
+ return result;
+}
+
+
+/*
+ * copytarget() - make a new target with the old target's name.
+ *
+ * Not entered into hash table -- for internal nodes.
+ */
+
+TARGET * copytarget( const TARGET * ot )
+{
+ TARGET * t = (TARGET *)BJAM_MALLOC( sizeof( *t ) );
+ memset( (char *)t, '\0', sizeof( *t ) );
+ t->name = copystr( ot->name );
+ t->boundname = t->name;
+
+ t->flags |= T_FLAG_NOTFILE | T_FLAG_INTERNAL;
+
+ return t;
+}
+
+
+/*
+ * touch_target() - mark a target to simulate being new.
+ */
+
+void touch_target( char * t )
+{
+ bindtarget( t )->flags |= T_FLAG_TOUCHED;
+}
+
+
+/*
+ * targetlist() - turn list of target names into a TARGET chain.
+ *
+ * Inputs:
+ * chain existing TARGETS to append to
+ * targets list of target names
+ */
+
+TARGETS * targetlist( TARGETS * chain, LIST * target_names )
+{
+ for ( ; target_names; target_names = list_next( target_names ) )
+ chain = targetentry( chain, bindtarget( target_names->string ) );
+ return chain;
+}
+
+
+/*
+ * targetentry() - add a TARGET to a chain of TARGETS.
+ *
+ * Inputs:
+ * chain existing TARGETS to append to
+ * target new target to append
+ */
+
+TARGETS * targetentry( TARGETS * chain, TARGET * target )
+{
+ TARGETS * c = (TARGETS *)BJAM_MALLOC( sizeof( TARGETS ) );
+ c->target = target;
+
+ if ( !chain ) chain = c;
+ else chain->tail->next = c;
+ chain->tail = c;
+ c->next = 0;
+
+ return chain;
+}
+
+
+/*
+ * targetchain() - append two TARGET chains.
+ *
+ * Inputs:
+ * chain exisitng TARGETS to append to
+ * target new target to append
+ */
+
+TARGETS * targetchain( TARGETS * chain, TARGETS * targets )
+{
+ if ( !targets ) return chain;
+ if ( !chain ) return targets;
+
+ chain->tail->next = targets;
+ chain->tail = targets->tail;
+
+ return chain;
+}
+
+/*
+ * actionlist() - append to an ACTION chain.
+ */
+
+ACTIONS * actionlist( ACTIONS * chain, ACTION * action )
+{
+ ACTIONS * actions = (ACTIONS *)BJAM_MALLOC( sizeof( ACTIONS ) );
+
+ actions->action = action;
+
+ if ( !chain ) chain = actions;
+ else chain->tail->next = actions;
+ chain->tail = actions;
+ actions->next = 0;
+
+ return chain;
+}
+
+static SETTINGS * settings_freelist;
+
+
+/*
+ * addsettings() - add a deferred "set" command to a target.
+ *
+ * Adds a variable setting (varname=list) onto a chain of settings for a
+ * particular target. 'flag' controls the relationship between new and old
+ * values in the same way as in var_set() function (see variable.c). Returns
+ * the head of the settings chain.
+ */
+
+SETTINGS * addsettings( SETTINGS * head, int flag, char * symbol, LIST * value )
+{
+ SETTINGS * v;
+
+ /* Look for previous settings. */
+ for ( v = head; v; v = v->next )
+ if ( !strcmp( v->symbol, symbol ) )
+ break;
+
+ /* If not previously set, alloc a new. */
+ /* If appending, do so. */
+ /* Else free old and set new. */
+ if ( !v )
+ {
+ v = settings_freelist;
+
+ if ( v )
+ settings_freelist = v->next;
+ else
+ v = (SETTINGS *)BJAM_MALLOC( sizeof( *v ) );
+
+ v->symbol = newstr( symbol );
+ v->value = value;
+ v->next = head;
+ v->multiple = 0;
+ head = v;
+ }
+ else if ( flag == VAR_APPEND )
+ {
+ v->value = list_append( v->value, value );
+ }
+ else if ( flag != VAR_DEFAULT )
+ {
+ list_free( v->value );
+ v->value = value;
+ }
+ else
+ list_free( value );
+
+ /* Return (new) head of list. */
+ return head;
+}
+
+
+/*
+ * pushsettings() - set all target specific variables.
+ */
+
+void pushsettings( SETTINGS * v )
+{
+ for ( ; v; v = v->next )
+ v->value = var_swap( v->symbol, v->value );
+}
+
+
+/*
+ * popsettings() - reset target specific variables to their pre-push values.
+ */
+
+void popsettings( SETTINGS * v )
+{
+ pushsettings( v ); /* just swap again */
+}
+
+
+/*
+ * copysettings() - duplicate a settings list, returning the new copy.
+ */
+
+SETTINGS * copysettings( SETTINGS * head )
+{
+ SETTINGS * copy = 0;
+ SETTINGS * v;
+ for ( v = head; v; v = v->next )
+ copy = addsettings( copy, VAR_SET, v->symbol, list_copy( 0, v->value ) );
+ return copy;
+}
+
+
+/*
+ * freetargets() - delete a targets list.
+ */
+
+void freetargets( TARGETS * chain )
+{
+ while ( chain )
+ {
+ TARGETS * n = chain->next;
+ BJAM_FREE( chain );
+ chain = n;
+ }
+}
+
+
+/*
+ * freeactions() - delete an action list.
+ */
+
+void freeactions( ACTIONS * chain )
+{
+ while ( chain )
+ {
+ ACTIONS * n = chain->next;
+ BJAM_FREE( chain );
+ chain = n;
+ }
+}
+
+
+/*
+ * freesettings() - delete a settings list.
+ */
+
+void freesettings( SETTINGS * v )
+{
+ while ( v )
+ {
+ SETTINGS * n = v->next;
+ freestr( v->symbol );
+ list_free( v->value );
+ v->next = settings_freelist;
+ settings_freelist = v;
+ v = n;
+ }
+}
+
+
+static void freetarget( void * xt, void * data )
+{
+ TARGET * t = (TARGET *)xt;
+ if ( t->settings ) freesettings( t->settings );
+ if ( t->depends ) freetargets ( t->depends );
+ if ( t->includes ) freetarget ( t->includes, (void *)0 );
+ if ( t->actions ) freeactions ( t->actions );
+}
+
+
+/*
+ * rules_done() - free RULE and TARGET tables.
+ */
+
+void rules_done()
+{
+ hashenumerate( targethash, freetarget, 0 );
+ hashdone( targethash );
+ while ( settings_freelist )
+ {
+ SETTINGS * n = settings_freelist->next;
+ BJAM_FREE( settings_freelist );
+ settings_freelist = n;
+ }
+}
+
+
+/*
+ * args_new() - make a new reference-counted argument list.
+ */
+
+argument_list * args_new()
+{
+ argument_list * r = (argument_list *)BJAM_MALLOC( sizeof(argument_list) );
+ r->reference_count = 0;
+ lol_init( r->data );
+ return r;
+}
+
+
+/*
+ * args_refer() - add a new reference to the given argument list.
+ */
+
+void args_refer( argument_list * a )
+{
+ ++a->reference_count;
+}
+
+
+/*
+ * args_free() - release a reference to the given argument list.
+ */
+
+void args_free( argument_list * a )
+{
+ if ( --a->reference_count <= 0 )
+ {
+ lol_free( a->data );
+ BJAM_FREE( a );
+ }
+}
+
+
+/*
+ * actions_refer() - add a new reference to the given actions.
+ */
+
+void actions_refer( rule_actions * a )
+{
+ ++a->reference_count;
+}
+
+
+/*
+ * actions_free() - release a reference to the given actions.
+ */
+
+void actions_free( rule_actions * a )
+{
+ if ( --a->reference_count <= 0 )
+ {
+ freestr( a->command );
+ list_free( a->bindlist );
+ BJAM_FREE( a );
+ }
+}
+
+
+/*
+ * set_rule_body() - set the argument list and procedure of the given rule.
+ */
+
+static void set_rule_body( RULE * rule, argument_list * args, PARSE * procedure )
+{
+ if ( args )
+ args_refer( args );
+ if ( rule->arguments )
+ args_free( rule->arguments );
+ rule->arguments = args;
+
+ if ( procedure )
+ parse_refer( procedure );
+ if ( rule->procedure )
+ parse_free( rule->procedure );
+ rule->procedure = procedure;
+}
+
+
+/*
+ * global_name() - given a rule, return the name for a corresponding rule in the
+ * global module.
+ */
+
+static char * global_rule_name( RULE * r )
+{
+ if ( r->module == root_module() )
+ return r->name;
+
+ {
+ char name[4096] = "";
+ strncat( name, r->module->name, sizeof( name ) - 1 );
+ strncat( name, r->name, sizeof( name ) - 1 );
+ return newstr( name);
+ }
+}
+
+
+/*
+ * global_rule() - given a rule, produce the corresponding entry in the global
+ * module.
+ */
+
+static RULE * global_rule( RULE * r )
+{
+ if ( r->module == root_module() )
+ return r;
+
+ {
+ char * name = global_rule_name( r );
+ RULE * result = define_rule( r->module, name, root_module() );
+ freestr( name );
+ return result;
+ }
+}
+
+
+/*
+ * new_rule_body() - make a new rule named rulename in the given module, with
+ * the given argument list and procedure. If exported is true, the rule is
+ * exported to the global module as modulename.rulename.
+ */
+
+RULE * new_rule_body( module_t * m, char * rulename, argument_list * args, PARSE * procedure, int exported )
+{
+ RULE * local = define_rule( m, rulename, m );
+ local->exported = exported;
+ set_rule_body( local, args, procedure );
+
+ /* Mark the procedure with the global rule name, regardless of whether the
+ * rule is exported. That gives us something reasonably identifiable that we
+ * can use, e.g. in profiling output. Only do this once, since this could be
+ * called multiple times with the same procedure.
+ */
+ if ( procedure->rulename == 0 )
+ procedure->rulename = global_rule_name( local );
+
+ return local;
+}
+
+
+static void set_rule_actions( RULE * rule, rule_actions * actions )
+{
+ if ( actions )
+ actions_refer( actions );
+ if ( rule->actions )
+ actions_free( rule->actions );
+ rule->actions = actions;
+}
+
+
+static rule_actions * actions_new( char * command, LIST * bindlist, int flags )
+{
+ rule_actions * result = (rule_actions *)BJAM_MALLOC( sizeof( rule_actions ) );
+ result->command = copystr( command );
+ result->bindlist = bindlist;
+ result->flags = flags;
+ result->reference_count = 0;
+ return result;
+}
+
+
+RULE * new_rule_actions( module_t * m, char * rulename, char * command, LIST * bindlist, int flags )
+{
+ RULE * local = define_rule( m, rulename, m );
+ RULE * global = global_rule( local );
+ set_rule_actions( local, actions_new( command, bindlist, flags ) );
+ set_rule_actions( global, local->actions );
+ return local;
+}
+
+
+/*
+ * Looks for a rule in the specified module, and returns it, if found. First
+ * checks if the rule is present in the module's rule table. Second, if name of
+ * the rule is in the form name1.name2 and name1 is in the list of imported
+ * modules, look in module 'name1' for rule 'name2'.
+ */
+
+RULE * lookup_rule( char * rulename, module_t * m, int local_only )
+{
+ RULE rule;
+ RULE * r = &rule;
+ RULE * result = 0;
+ module_t * original_module = m;
+
+ r->name = rulename;
+
+ if ( m->class_module )
+ m = m->class_module;
+
+ if ( m->rules && hashcheck( m->rules, (HASHDATA * *)&r ) )
+ result = r;
+ else if ( !local_only && m->imported_modules )
+ {
+ /* Try splitting the name into module and rule. */
+ char *p = strchr( r->name, '.' ) ;
+ if ( p )
+ {
+ *p = '\0';
+ /* Now, r->name keeps the module name, and p+1 keeps the rule name.
+ */
+ if ( hashcheck( m->imported_modules, (HASHDATA * *)&r ) )
+ result = lookup_rule( p + 1, bindmodule( rulename ), 1 );
+ *p = '.';
+ }
+ }
+
+ if ( result )
+ {
+ if ( local_only && !result->exported )
+ result = 0;
+ else
+ {
+ /* Lookup started in class module. We have found a rule in class
+ * module, which is marked for execution in that module, or in some
+ * instances. Mark it for execution in the instance where we started
+ * the lookup.
+ */
+ int execute_in_class = ( result->module == m );
+ int execute_in_some_instance = ( result->module->class_module &&
+ ( result->module->class_module == m ) );
+ if ( ( original_module != m ) &&
+ ( execute_in_class || execute_in_some_instance ) )
+ result->module = original_module;
+ }
+ }
+
+ return result;
+}
+
+
+RULE * bindrule( char * rulename, module_t * m )
+{
+ RULE * result = lookup_rule( rulename, m, 0 );
+ if ( !result )
+ result = lookup_rule( rulename, root_module(), 0 );
+ /* We have only one caller, 'evaluate_rule', which will complain about
+ * calling an undefined rule. We could issue the error here, but we do not
+ * have the necessary information, such as frame.
+ */
+ if ( !result )
+ result = enter_rule( rulename, m );
+ return result;
+}
+
+
+RULE * import_rule( RULE * source, module_t * m, char * name )
+{
+ RULE * dest = define_rule( source->module, name, m );
+ set_rule_body( dest, source->arguments, source->procedure );
+ set_rule_actions( dest, source->actions );
+ return dest;
+}
diff --git a/jam-files/engine/rules.h b/jam-files/engine/rules.h
new file mode 100644
index 00000000..806a1469
--- /dev/null
+++ b/jam-files/engine/rules.h
@@ -0,0 +1,280 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/* This file is ALSO:
+ * Copyright 2001-2004 David Abrahams.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+#ifndef RULES_DWA_20011020_H
+#define RULES_DWA_20011020_H
+
+#include "modules.h"
+#include "jam.h"
+#include "parse.h"
+
+
+/*
+ * rules.h - targets, rules, and related information
+ *
+ * This file describes the structures holding the targets, rules, and
+ * related information accumulated by interpreting the statements
+ * of the jam files.
+ *
+ * The following are defined:
+ *
+ * RULE - a generic jam rule, the product of RULE and ACTIONS.
+ * ACTIONS - a chain of ACTIONs.
+ * ACTION - a RULE instance with targets and sources.
+ * SETTINGS - variables to set when executing a TARGET's ACTIONS.
+ * TARGETS - a chain of TARGETs.
+ * TARGET - an entity (e.g. a file) that can be built.
+ *
+ * 04/11/94 (seiwald) - Combined deps & headers into deps[2] in TARGET.
+ * 04/12/94 (seiwald) - actionlist() now just appends a single action.
+ * 06/01/94 (seiwald) - new 'actions existing' does existing sources
+ * 12/20/94 (seiwald) - NOTIME renamed NOTFILE.
+ * 01/19/95 (seiwald) - split DONTKNOW into CANTFIND/CANTMAKE.
+ * 02/02/95 (seiwald) - new LEAVES modifier on targets.
+ * 02/14/95 (seiwald) - new NOUPDATE modifier on targets.
+ */
+
+typedef struct _rule RULE;
+typedef struct _target TARGET;
+typedef struct _targets TARGETS;
+typedef struct _action ACTION;
+typedef struct _actions ACTIONS;
+typedef struct _settings SETTINGS ;
+
+/* RULE - a generic jam rule, the product of RULE and ACTIONS. */
+
+/* A rule's argument list. */
+struct argument_list
+{
+ int reference_count;
+ LOL data[1];
+};
+
+/* Build actions corresponding to a rule. */
+struct rule_actions
+{
+ int reference_count;
+ char * command; /* command string from ACTIONS */
+ LIST * bindlist;
+ int flags; /* modifiers on ACTIONS */
+
+#define RULE_NEWSRCS 0x01 /* $(>) is updated sources only */
+#define RULE_TOGETHER 0x02 /* combine actions on single target */
+#define RULE_IGNORE 0x04 /* ignore return status of executes */
+#define RULE_QUIETLY 0x08 /* do not mention it unless verbose */
+#define RULE_PIECEMEAL 0x10 /* split exec so each $(>) is small */
+#define RULE_EXISTING 0x20 /* $(>) is pre-exisitng sources only */
+};
+
+typedef struct rule_actions rule_actions;
+typedef struct argument_list argument_list;
+
+struct _rule
+{
+ char * name;
+ PARSE * procedure; /* parse tree from RULE */
+ argument_list * arguments; /* argument checking info, or NULL for unchecked
+ */
+ rule_actions * actions; /* build actions, or NULL for no actions */
+ module_t * module; /* module in which this rule is executed */
+ int exported; /* nonzero if this rule is supposed to appear in
+ * the global module and be automatically
+ * imported into other modules
+ */
+#ifdef HAVE_PYTHON
+ PyObject * python_function;
+#endif
+};
+
+/* ACTIONS - a chain of ACTIONs. */
+struct _actions
+{
+ ACTIONS * next;
+ ACTIONS * tail; /* valid only for head */
+ ACTION * action;
+};
+
+/* ACTION - a RULE instance with targets and sources. */
+struct _action
+{
+ RULE * rule;
+ TARGETS * targets;
+ TARGETS * sources; /* aka $(>) */
+ char running; /* has been started */
+ char status; /* see TARGET status */
+};
+
+/* SETTINGS - variables to set when executing a TARGET's ACTIONS. */
+struct _settings
+{
+ SETTINGS * next;
+ char * symbol; /* symbol name for var_set() */
+ LIST * value; /* symbol value for var_set() */
+ int multiple;
+};
+
+/* TARGETS - a chain of TARGETs. */
+struct _targets
+{
+ TARGETS * next;
+ TARGETS * tail; /* valid only for head */
+ TARGET * target;
+};
+
+/* TARGET - an entity (e.g. a file) that can be built. */
+struct _target
+{
+ char * name;
+ char * boundname; /* if search() relocates target */
+ ACTIONS * actions; /* rules to execute, if any */
+ SETTINGS * settings; /* variables to define */
+
+ short flags; /* status info */
+
+#define T_FLAG_TEMP 0x0001 /* TEMPORARY applied */
+#define T_FLAG_NOCARE 0x0002 /* NOCARE applied */
+#define T_FLAG_NOTFILE 0x0004 /* NOTFILE applied */
+#define T_FLAG_TOUCHED 0x0008 /* ALWAYS applied or -t target */
+#define T_FLAG_LEAVES 0x0010 /* LEAVES applied */
+#define T_FLAG_NOUPDATE 0x0020 /* NOUPDATE applied */
+#define T_FLAG_VISITED 0x0040 /* CWM: Used in debugging */
+
+/* This flag has been added to support a new built-in rule named "RMBAD". It is
+ * used to force removal of outdated targets whose dependencies fail to build.
+ */
+#define T_FLAG_RMOLD 0x0080 /* RMBAD applied */
+
+/* This flag was added to support a new built-in rule named "FAIL_EXPECTED" used
+ * to indicate that the result of running a given action should be inverted,
+ * i.e. ok <=> fail. This is useful for launching certain test runs from a
+ * Jamfile.
+ */
+#define T_FLAG_FAIL_EXPECTED 0x0100 /* FAIL_EXPECTED applied */
+
+#define T_FLAG_INTERNAL 0x0200 /* internal INCLUDES node */
+
+/* Indicates that the target must be a file. This prevents matching non-files,
+ * like directories, when a target is searched.
+ */
+#define T_FLAG_ISFILE 0x0400
+
+#define T_FLAG_PRECIOUS 0x0800
+
+ char binding; /* how target relates to a real file or
+ * folder
+ */
+
+#define T_BIND_UNBOUND 0 /* a disembodied name */
+#define T_BIND_MISSING 1 /* could not find real file */
+#define T_BIND_PARENTS 2 /* using parent's timestamp */
+#define T_BIND_EXISTS 3 /* real file, timestamp valid */
+
+ TARGETS * depends; /* dependencies */
+ TARGETS * dependants; /* the inverse of dependencies */
+ TARGETS * rebuilds; /* targets that should be force-rebuilt
+ * whenever this one is
+ */
+ TARGET * includes; /* internal includes node */
+ TARGET * original_target; /* original_target->includes = this */
+ char rescanned;
+
+ time_t time; /* update time */
+ time_t leaf; /* update time of leaf sources */
+
+ char fate; /* make0()'s diagnosis */
+
+#define T_FATE_INIT 0 /* nothing done to target */
+#define T_FATE_MAKING 1 /* make0(target) on stack */
+
+#define T_FATE_STABLE 2 /* target did not need updating */
+#define T_FATE_NEWER 3 /* target newer than parent */
+
+#define T_FATE_SPOIL 4 /* >= SPOIL rebuilds parents */
+#define T_FATE_ISTMP 4 /* unneeded temp target oddly present */
+
+#define T_FATE_BUILD 5 /* >= BUILD rebuilds target */
+#define T_FATE_TOUCHED 5 /* manually touched with -t */
+#define T_FATE_REBUILD 6
+#define T_FATE_MISSING 7 /* is missing, needs updating */
+#define T_FATE_NEEDTMP 8 /* missing temp that must be rebuild */
+#define T_FATE_OUTDATED 9 /* is out of date, needs updating */
+#define T_FATE_UPDATE 10 /* deps updated, needs updating */
+
+#define T_FATE_BROKEN 11 /* >= BROKEN ruins parents */
+#define T_FATE_CANTFIND 11 /* no rules to make missing target */
+#define T_FATE_CANTMAKE 12 /* can not find dependencies */
+
+ char progress; /* tracks make1() progress */
+
+#define T_MAKE_INIT 0 /* make1(target) not yet called */
+#define T_MAKE_ONSTACK 1 /* make1(target) on stack */
+#define T_MAKE_ACTIVE 2 /* make1(target) in make1b() */
+#define T_MAKE_RUNNING 3 /* make1(target) running commands */
+#define T_MAKE_DONE 4 /* make1(target) done */
+
+#ifdef OPT_SEMAPHORE
+ #define T_MAKE_SEMAPHORE 5 /* Special target type for semaphores */
+#endif
+
+#ifdef OPT_SEMAPHORE
+ TARGET * semaphore; /* used in serialization */
+#endif
+
+ char status; /* exec_cmd() result */
+
+ int asynccnt; /* child deps outstanding */
+ TARGETS * parents; /* used by make1() for completion */
+ char * cmds; /* type-punned command list */
+
+ char * failed;
+};
+
+
+/* Action related functions. */
+ACTIONS * actionlist ( ACTIONS *, ACTION * );
+void freeactions ( ACTIONS * );
+SETTINGS * addsettings ( SETTINGS *, int flag, char * symbol, LIST * value );
+void pushsettings ( SETTINGS * );
+void popsettings ( SETTINGS * );
+SETTINGS * copysettings ( SETTINGS * );
+void freesettings ( SETTINGS * );
+void actions_refer( rule_actions * );
+void actions_free ( rule_actions * );
+
+/* Argument list related functions. */
+void args_free ( argument_list * );
+argument_list * args_new ();
+void args_refer( argument_list * );
+
+/* Rule related functions. */
+RULE * bindrule ( char * rulename, module_t * );
+RULE * import_rule ( RULE * source, module_t *, char * name );
+RULE * new_rule_body ( module_t *, char * rulename, argument_list *, PARSE * procedure, int exprt );
+RULE * new_rule_actions( module_t *, char * rulename, char * command, LIST * bindlist, int flags );
+void rule_free ( RULE * );
+
+/* Target related functions. */
+void bind_explicitly_located_targets();
+TARGET * bindtarget ( char const * target_name );
+TARGET * copytarget ( TARGET const * t );
+void freetargets ( TARGETS * );
+TARGET * search_for_target ( char * name, LIST * search_path );
+TARGETS * targetchain ( TARGETS * chain, TARGETS * );
+TARGETS * targetentry ( TARGETS * chain, TARGET * );
+void target_include ( TARGET * including, TARGET * included );
+TARGETS * targetlist ( TARGETS * chain, LIST * target_names );
+void touch_target ( char * t );
+
+/* Final module cleanup. */
+void rules_done();
+
+#endif
diff --git a/jam-files/engine/scan.c b/jam-files/engine/scan.c
new file mode 100644
index 00000000..11c44c0e
--- /dev/null
+++ b/jam-files/engine/scan.c
@@ -0,0 +1,418 @@
+/*
+ * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+#include "jam.h"
+#include "lists.h"
+#include "parse.h"
+#include "scan.h"
+#include "jamgram.h"
+#include "jambase.h"
+#include "newstr.h"
+
+/*
+ * scan.c - the jam yacc scanner
+ *
+ * 12/26/93 (seiwald) - bump buf in yylex to 10240 - yuk.
+ * 09/16/94 (seiwald) - check for overflows, unmatched {}'s, etc.
+ * Also handle tokens abutting EOF by remembering
+ * to return EOF now matter how many times yylex()
+ * reinvokes yyline().
+ * 02/11/95 (seiwald) - honor only punctuation keywords if SCAN_PUNCT.
+ * 07/27/95 (seiwald) - Include jamgram.h after scan.h, so that YYSTYPE is
+ * defined before Linux's yacc tries to redefine it.
+ */
+
+struct keyword
+{
+ char * word;
+ int type;
+} keywords[] =
+{
+#include "jamgramtab.h"
+ { 0, 0 }
+};
+
+struct include
+{
+ struct include * next; /* next serial include file */
+ char * string; /* pointer into current line */
+ char * * strings; /* for yyfparse() -- text to parse */
+ FILE * file; /* for yyfparse() -- file being read */
+ char * fname; /* for yyfparse() -- file name */
+ int line; /* line counter for error messages */
+ char buf[ 512 ]; /* for yyfparse() -- line buffer */
+};
+
+static struct include * incp = 0; /* current file; head of chain */
+
+static int scanmode = SCAN_NORMAL;
+static int anyerrors = 0;
+
+
+static char * symdump( YYSTYPE * );
+
+#define BIGGEST_TOKEN 10240 /* no single token can be larger */
+
+
+/*
+ * Set parser mode: normal, string, or keyword.
+ */
+
+void yymode( int n )
+{
+ scanmode = n;
+}
+
+
+void yyerror( char * s )
+{
+ /* We use yylval instead of incp to access the error location information as
+ * the incp pointer will already be reset to 0 in case the error occurred at
+ * EOF.
+ *
+ * The two may differ only if we get an error while reading a lexical token
+ * spanning muliple lines, e.g. a multi-line string literal or action body,
+ * in which case yylval location information will hold the information about
+ * where this token started while incp will hold the information about where
+ * reading it broke.
+ *
+ * TODO: Test the theory about when yylval and incp location information are
+ * the same and when they differ.
+ */
+ printf( "%s:%d: %s at %s\n", yylval.file, yylval.line, s, symdump( &yylval ) );
+ ++anyerrors;
+}
+
+
+int yyanyerrors()
+{
+ return anyerrors != 0;
+}
+
+
+void yyfparse( char * s )
+{
+ struct include * i = (struct include *)BJAM_MALLOC( sizeof( *i ) );
+
+ /* Push this onto the incp chain. */
+ i->string = "";
+ i->strings = 0;
+ i->file = 0;
+ i->fname = copystr( s );
+ i->line = 0;
+ i->next = incp;
+ incp = i;
+
+ /* If the filename is "+", it means use the internal jambase. */
+ if ( !strcmp( s, "+" ) )
+ i->strings = jambase;
+}
+
+
+/*
+ * yyline() - read new line and return first character.
+ *
+ * Fabricates a continuous stream of characters across include files, returning
+ * EOF at the bitter end.
+ */
+
+int yyline()
+{
+ struct include * i = incp;
+
+ if ( !incp )
+ return EOF;
+
+ /* Once we start reading from the input stream, we reset the include
+ * insertion point so that the next include file becomes the head of the
+ * list.
+ */
+
+ /* If there is more data in this line, return it. */
+ if ( *i->string )
+ return *i->string++;
+
+ /* If we are reading from an internal string list, go to the next string. */
+ if ( i->strings )
+ {
+ if ( *i->strings )
+ {
+ ++i->line;
+ i->string = *(i->strings++);
+ return *i->string++;
+ }
+ }
+ else
+ {
+ /* If necessary, open the file. */
+ if ( !i->file )
+ {
+ FILE * f = stdin;
+ if ( strcmp( i->fname, "-" ) && !( f = fopen( i->fname, "r" ) ) )
+ perror( i->fname );
+ i->file = f;
+ }
+
+ /* If there is another line in this file, start it. */
+ if ( i->file && fgets( i->buf, sizeof( i->buf ), i->file ) )
+ {
+ ++i->line;
+ i->string = i->buf;
+ return *i->string++;
+ }
+ }
+
+ /* This include is done. Free it up and return EOF so yyparse() returns to
+ * parse_file().
+ */
+
+ incp = i->next;
+
+ /* Close file, free name. */
+ if ( i->file && ( i->file != stdin ) )
+ fclose( i->file );
+ freestr( i->fname );
+ BJAM_FREE( (char *)i );
+
+ return EOF;
+}
+
+
+/*
+ * yylex() - set yylval to current token; return its type.
+ *
+ * Macros to move things along:
+ *
+ * yychar() - return and advance character; invalid after EOF.
+ * yyprev() - back up one character; invalid before yychar().
+ *
+ * yychar() returns a continuous stream of characters, until it hits the EOF of
+ * the current include file.
+ */
+
+#define yychar() ( *incp->string ? *incp->string++ : yyline() )
+#define yyprev() ( incp->string-- )
+
+int yylex()
+{
+ int c;
+ char buf[ BIGGEST_TOKEN ];
+ char * b = buf;
+
+ if ( !incp )
+ goto eof;
+
+ /* Get first character (whitespace or of token). */
+ c = yychar();
+
+ if ( scanmode == SCAN_STRING )
+ {
+ /* If scanning for a string (action's {}'s), look for the closing brace.
+ * We handle matching braces, if they match.
+ */
+
+ int nest = 1;
+
+ while ( ( c != EOF ) && ( b < buf + sizeof( buf ) ) )
+ {
+ if ( c == '{' )
+ ++nest;
+
+ if ( ( c == '}' ) && !--nest )
+ break;
+
+ *b++ = c;
+
+ c = yychar();
+
+ /* Turn trailing "\r\n" sequences into plain "\n" for Cygwin. */
+ if ( ( c == '\n' ) && ( b[ -1 ] == '\r' ) )
+ --b;
+ }
+
+ /* We ate the ending brace -- regurgitate it. */
+ if ( c != EOF )
+ yyprev();
+
+ /* Check for obvious errors. */
+ if ( b == buf + sizeof( buf ) )
+ {
+ yyerror( "action block too big" );
+ goto eof;
+ }
+
+ if ( nest )
+ {
+ yyerror( "unmatched {} in action block" );
+ goto eof;
+ }
+
+ *b = 0;
+ yylval.type = STRING;
+ yylval.string = newstr( buf );
+ yylval.file = incp->fname;
+ yylval.line = incp->line;
+ }
+ else
+ {
+ char * b = buf;
+ struct keyword * k;
+ int inquote = 0;
+ int notkeyword;
+
+ /* Eat white space. */
+ for ( ;; )
+ {
+ /* Skip past white space. */
+ while ( ( c != EOF ) && isspace( c ) )
+ c = yychar();
+
+ /* Not a comment? */
+ if ( c != '#' )
+ break;
+
+ /* Swallow up comment line. */
+ while ( ( ( c = yychar() ) != EOF ) && ( c != '\n' ) ) ;
+ }
+
+ /* c now points to the first character of a token. */
+ if ( c == EOF )
+ goto eof;
+
+ yylval.file = incp->fname;
+ yylval.line = incp->line;
+
+ /* While scanning the word, disqualify it for (expensive) keyword lookup
+ * when we can: $anything, "anything", \anything
+ */
+ notkeyword = c == '$';
+
+ /* Look for white space to delimit word. "'s get stripped but preserve
+ * white space. \ protects next character.
+ */
+ while
+ (
+ ( c != EOF ) &&
+ ( b < buf + sizeof( buf ) ) &&
+ ( inquote || !isspace( c ) )
+ )
+ {
+ if ( c == '"' )
+ {
+ /* begin or end " */
+ inquote = !inquote;
+ notkeyword = 1;
+ }
+ else if ( c != '\\' )
+ {
+ /* normal char */
+ *b++ = c;
+ }
+ else if ( ( c = yychar() ) != EOF )
+ {
+ /* \c */
+ if (c == 'n')
+ c = '\n';
+ else if (c == 'r')
+ c = '\r';
+ else if (c == 't')
+ c = '\t';
+ *b++ = c;
+ notkeyword = 1;
+ }
+ else
+ {
+ /* \EOF */
+ break;
+ }
+
+ c = yychar();
+ }
+
+ /* Check obvious errors. */
+ if ( b == buf + sizeof( buf ) )
+ {
+ yyerror( "string too big" );
+ goto eof;
+ }
+
+ if ( inquote )
+ {
+ yyerror( "unmatched \" in string" );
+ goto eof;
+ }
+
+ /* We looked ahead a character - back up. */
+ if ( c != EOF )
+ yyprev();
+
+ /* Scan token table. Do not scan if it is obviously not a keyword or if
+ * it is an alphabetic when were looking for punctuation.
+ */
+
+ *b = 0;
+ yylval.type = ARG;
+
+ if ( !notkeyword && !( isalpha( *buf ) && ( scanmode == SCAN_PUNCT ) ) )
+ for ( k = keywords; k->word; ++k )
+ if ( ( *buf == *k->word ) && !strcmp( k->word, buf ) )
+ {
+ yylval.type = k->type;
+ yylval.string = k->word; /* used by symdump */
+ break;
+ }
+
+ if ( yylval.type == ARG )
+ yylval.string = newstr( buf );
+ }
+
+ if ( DEBUG_SCAN )
+ printf( "scan %s\n", symdump( &yylval ) );
+
+ return yylval.type;
+
+eof:
+ /* We do not reset yylval.file & yylval.line here so unexpected EOF error
+ * messages would include correct error location information.
+ */
+ yylval.type = EOF;
+ return yylval.type;
+}
+
+
+static char * symdump( YYSTYPE * s )
+{
+ static char buf[ BIGGEST_TOKEN + 20 ];
+ switch ( s->type )
+ {
+ case EOF : sprintf( buf, "EOF" ); break;
+ case 0 : sprintf( buf, "unknown symbol %s", s->string ); break;
+ case ARG : sprintf( buf, "argument %s" , s->string ); break;
+ case STRING: sprintf( buf, "string \"%s\"" , s->string ); break;
+ default : sprintf( buf, "keyword %s" , s->string ); break;
+ }
+ return buf;
+}
+
+
+/*
+ * Get information about the current file and line, for those epsilon
+ * transitions that produce a parse.
+ */
+
+void yyinput_stream( char * * name, int * line )
+{
+ if ( incp )
+ {
+ *name = incp->fname;
+ *line = incp->line;
+ }
+ else
+ {
+ *name = "(builtin)";
+ *line = -1;
+ }
+}
diff --git a/jam-files/engine/scan.h b/jam-files/engine/scan.h
new file mode 100644
index 00000000..3fad1c24
--- /dev/null
+++ b/jam-files/engine/scan.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * scan.h - the jam yacc scanner
+ *
+ * External functions:
+ *
+ * yyerror( char *s ) - print a parsing error message.
+ * yyfparse( char *s ) - scan include file s.
+ * yylex() - parse the next token, returning its type.
+ * yymode() - adjust lexicon of scanner.
+ * yyparse() - declaration for yacc parser.
+ * yyanyerrors() - indicate if any parsing errors occured.
+ *
+ * The yymode() function is for the parser to adjust the lexicon of the scanner.
+ * Aside from normal keyword scanning, there is a mode to handle action strings
+ * (look only for the closing }) and a mode to ignore most keywords when looking
+ * for a punctuation keyword. This allows non-punctuation keywords to be used in
+ * lists without quoting.
+ */
+
+/*
+ * YYSTYPE - value of a lexical token
+ */
+
+#define YYSTYPE YYSYMBOL
+
+typedef struct _YYSTYPE
+{
+ int type;
+ char * string;
+ PARSE * parse;
+ LIST * list;
+ int number;
+ char * file;
+ int line;
+} YYSTYPE;
+
+extern YYSTYPE yylval;
+
+void yymode( int n );
+void yyerror( char * s );
+int yyanyerrors();
+void yyfparse( char * s );
+int yyline();
+int yylex();
+int yyparse();
+void yyinput_stream( char * * name, int * line );
+
+# define SCAN_NORMAL 0 /* normal parsing */
+# define SCAN_STRING 1 /* look only for matching } */
+# define SCAN_PUNCT 2 /* only punctuation keywords */
diff --git a/jam-files/engine/search.c b/jam-files/engine/search.c
new file mode 100644
index 00000000..6c23d97a
--- /dev/null
+++ b/jam-files/engine/search.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/* This file is ALSO:
+ * Copyright 2001-2004 David Abrahams.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+#include "jam.h"
+#include "lists.h"
+#include "search.h"
+#include "timestamp.h"
+#include "pathsys.h"
+#include "variable.h"
+#include "newstr.h"
+#include "compile.h"
+#include "strings.h"
+#include "hash.h"
+#include "filesys.h"
+#include <string.h>
+
+
+typedef struct _binding
+{
+ char * binding;
+ char * target;
+} BINDING;
+
+static struct hash *explicit_bindings = 0;
+
+
+void call_bind_rule
+(
+ char * target_,
+ char * boundname_
+)
+{
+ LIST * bind_rule = var_get( "BINDRULE" );
+ if ( bind_rule )
+ {
+ /* No guarantee that the target is an allocated string, so be on the
+ * safe side.
+ */
+ char * target = copystr( target_ );
+
+ /* Likewise, do not rely on implementation details of newstr.c: allocate
+ * a copy of boundname.
+ */
+ char * boundname = copystr( boundname_ );
+ if ( boundname && target )
+ {
+ /* Prepare the argument list. */
+ FRAME frame[1];
+ frame_init( frame );
+
+ /* First argument is the target name. */
+ lol_add( frame->args, list_new( L0, target ) );
+
+ lol_add( frame->args, list_new( L0, boundname ) );
+ if ( lol_get( frame->args, 1 ) )
+ evaluate_rule( bind_rule->string, frame );
+
+ /* Clean up */
+ frame_free( frame );
+ }
+ else
+ {
+ if ( boundname )
+ freestr( boundname );
+ if ( target )
+ freestr( target );
+ }
+ }
+}
+
+/*
+ * search.c - find a target along $(SEARCH) or $(LOCATE)
+ * First, check if LOCATE is set. If so, use it to determine
+ * the location of target and return, regardless of whether anything
+ * exists on that location.
+ *
+ * Second, examine all directories in SEARCH. If there's file already
+ * or there's another target with the same name which was placed
+ * to this location via LOCATE setting, stop and return the location.
+ * In case of previous target, return it's name via the third argument.
+ *
+ * This bevahiour allow to handle dependency on generated files. If
+ * caller does not expect that target is generated, 0 can be passed as
+ * the third argument.
+ */
+
+char *
+search(
+ char *target,
+ time_t *time,
+ char **another_target,
+ int file
+)
+{
+ PATHNAME f[1];
+ LIST *varlist;
+ string buf[1];
+ int found = 0;
+ /* Will be set to 1 if target location is specified via LOCATE. */
+ int explicitly_located = 0;
+ char *boundname = 0;
+
+ if ( another_target )
+ *another_target = 0;
+
+ if (! explicit_bindings )
+ explicit_bindings = hashinit( sizeof(BINDING),
+ "explicitly specified locations");
+
+ string_new( buf );
+ /* Parse the filename */
+
+ path_parse( target, f );
+
+ f->f_grist.ptr = 0;
+ f->f_grist.len = 0;
+
+ if ( ( varlist = var_get( "LOCATE" ) ) )
+ {
+ f->f_root.ptr = varlist->string;
+ f->f_root.len = strlen( varlist->string );
+
+ path_build( f, buf, 1 );
+
+ if ( DEBUG_SEARCH )
+ printf( "locate %s: %s\n", target, buf->value );
+
+ explicitly_located = 1;
+
+ timestamp( buf->value, time );
+ found = 1;
+ }
+ else if ( ( varlist = var_get( "SEARCH" ) ) )
+ {
+ while ( varlist )
+ {
+ BINDING b, *ba = &b;
+ file_info_t *ff;
+
+ f->f_root.ptr = varlist->string;
+ f->f_root.len = strlen( varlist->string );
+
+ string_truncate( buf, 0 );
+ path_build( f, buf, 1 );
+
+ if ( DEBUG_SEARCH )
+ printf( "search %s: %s\n", target, buf->value );
+
+ ff = file_query(buf->value);
+ timestamp( buf->value, time );
+
+ b.binding = buf->value;
+
+ if ( hashcheck( explicit_bindings, (HASHDATA**)&ba ) )
+ {
+ if ( DEBUG_SEARCH )
+ printf(" search %s: found explicitly located target %s\n",
+ target, ba->target);
+ if ( another_target )
+ *another_target = ba->target;
+ found = 1;
+ break;
+ }
+ else if ( ff && ff->time )
+ {
+ if ( !file || ff->is_file )
+ {
+ found = 1;
+ break;
+ }
+ }
+
+ varlist = list_next( varlist );
+ }
+ }
+
+ if ( !found )
+ {
+ /* Look for the obvious */
+ /* This is a questionable move. Should we look in the */
+ /* obvious place if SEARCH is set? */
+
+ f->f_root.ptr = 0;
+ f->f_root.len = 0;
+
+ string_truncate( buf, 0 );
+ path_build( f, buf, 1 );
+
+ if ( DEBUG_SEARCH )
+ printf( "search %s: %s\n", target, buf->value );
+
+ timestamp( buf->value, time );
+ }
+
+ boundname = newstr( buf->value );
+ string_free( buf );
+
+ if ( explicitly_located )
+ {
+ BINDING b;
+ BINDING * ba = &b;
+ b.binding = boundname;
+ b.target = target;
+ /* CONSIDER: we probably should issue a warning is another file
+ is explicitly bound to the same location. This might break
+ compatibility, though. */
+ hashenter( explicit_bindings, (HASHDATA * *)&ba );
+ }
+
+ /* prepare a call to BINDRULE if the variable is set */
+ call_bind_rule( target, boundname );
+
+ return boundname;
+}
diff --git a/jam-files/engine/search.h b/jam-files/engine/search.h
new file mode 100644
index 00000000..c364cac0
--- /dev/null
+++ b/jam-files/engine/search.h
@@ -0,0 +1,11 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * search.h - find a target along $(SEARCH) or $(LOCATE)
+ */
+
+char *search( char *target, time_t *time, char **another_target, int file );
diff --git a/jam-files/engine/strings.c b/jam-files/engine/strings.c
new file mode 100644
index 00000000..89561237
--- /dev/null
+++ b/jam-files/engine/strings.c
@@ -0,0 +1,201 @@
+/* Copyright David Abrahams 2004. Distributed under the Boost */
+/* Software License, Version 1.0. (See accompanying */
+/* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */
+
+#include "jam.h"
+#include "strings.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+
+#ifndef NDEBUG
+# define JAM_STRING_MAGIC ((char)0xcf)
+# define JAM_STRING_MAGIC_SIZE 4
+static void assert_invariants( string* self )
+{
+ int i;
+
+ if ( self->value == 0 )
+ {
+ assert( self->size == 0 );
+ assert( self->capacity == 0 );
+ assert( self->opt[0] == 0 );
+ return;
+ }
+
+ assert( self->size < self->capacity );
+ assert( ( self->capacity <= sizeof(self->opt) ) == ( self->value == self->opt ) );
+ assert( strlen( self->value ) == self->size );
+
+ for (i = 0; i < 4; ++i)
+ {
+ assert( self->magic[i] == JAM_STRING_MAGIC );
+ assert( self->value[self->capacity + i] == JAM_STRING_MAGIC );
+ }
+}
+#else
+# define JAM_STRING_MAGIC_SIZE 0
+# define assert_invariants(x) do {} while (0)
+#endif
+
+void string_new( string* s )
+{
+ s->value = s->opt;
+ s->size = 0;
+ s->capacity = sizeof(s->opt);
+ s->opt[0] = 0;
+#ifndef NDEBUG
+ memset(s->magic, JAM_STRING_MAGIC, sizeof(s->magic));
+#endif
+ assert_invariants( s );
+}
+
+void string_free( string* s )
+{
+ assert_invariants( s );
+ if ( s->value != s->opt )
+ BJAM_FREE( s->value );
+ string_new( s );
+}
+
+static void string_reserve_internal( string* self, size_t capacity )
+{
+ if ( self->value == self->opt )
+ {
+ self->value = (char*)BJAM_MALLOC_ATOMIC( capacity + JAM_STRING_MAGIC_SIZE );
+ self->value[0] = 0;
+ strncat( self->value, self->opt, sizeof(self->opt) );
+ assert( strlen( self->value ) <= self->capacity ); /* This is a regression test */
+ }
+ else
+ {
+ self->value = (char*)BJAM_REALLOC( self->value, capacity + JAM_STRING_MAGIC_SIZE );
+ }
+#ifndef NDEBUG
+ memcpy( self->value + capacity, self->magic, JAM_STRING_MAGIC_SIZE );
+#endif
+ self->capacity = capacity;
+}
+
+void string_reserve( string* self, size_t capacity )
+{
+ assert_invariants( self );
+ if ( capacity <= self->capacity )
+ return;
+ string_reserve_internal( self, capacity );
+ assert_invariants( self );
+}
+
+static void extend_full( string* self, char const* start, char const* finish )
+{
+ size_t new_size = self->capacity + ( finish - start );
+ size_t new_capacity = self->capacity;
+ size_t old_size = self->capacity;
+ while ( new_capacity < new_size + 1)
+ new_capacity <<= 1;
+ string_reserve_internal( self, new_capacity );
+ memcpy( self->value + old_size, start, new_size - old_size );
+ self->value[new_size] = 0;
+ self->size = new_size;
+}
+
+void string_append( string* self, char const* rhs )
+{
+ char* p = self->value + self->size;
+ char* end = self->value + self->capacity;
+ assert_invariants( self );
+
+ while ( *rhs && p != end)
+ *p++ = *rhs++;
+
+ if ( p != end )
+ {
+ *p = 0;
+ self->size = p - self->value;
+ }
+ else
+ {
+ extend_full( self, rhs, rhs + strlen(rhs) );
+ }
+ assert_invariants( self );
+}
+
+void string_append_range( string* self, char const* start, char const* finish )
+{
+ char* p = self->value + self->size;
+ char* end = self->value + self->capacity;
+ assert_invariants( self );
+
+ while ( p != end && start != finish )
+ *p++ = *start++;
+
+ if ( p != end )
+ {
+ *p = 0;
+ self->size = p - self->value;
+ }
+ else
+ {
+ extend_full( self, start, finish );
+ }
+ assert_invariants( self );
+}
+
+void string_copy( string* s, char const* rhs )
+{
+ string_new( s );
+ string_append( s, rhs );
+}
+
+void string_truncate( string* self, size_t n )
+{
+ assert_invariants( self );
+ assert( n <= self->capacity );
+ self->value[self->size = n] = 0;
+ assert_invariants( self );
+}
+
+void string_pop_back( string* self )
+{
+ string_truncate( self, self->size - 1 );
+}
+
+void string_push_back( string* self, char x )
+{
+ string_append_range( self, &x, &x + 1 );
+}
+
+char string_back( string* self )
+{
+ assert_invariants( self );
+ return self->value[self->size - 1];
+}
+
+#ifndef NDEBUG
+void string_unit_test()
+{
+ string s[1];
+ int i;
+ char buffer[sizeof(s->opt) * 2 + 2];
+ int limit = sizeof(buffer) > 254 ? 254 : sizeof(buffer);
+
+ string_new(s);
+
+ for (i = 0; i < limit; ++i)
+ {
+ string_push_back( s, (char)(i + 1) );
+ };
+
+ for (i = 0; i < limit; ++i)
+ {
+ assert( i < s->size );
+ assert( s->value[i] == (char)(i + 1));
+ }
+
+ string_free(s);
+
+}
+#endif
+
diff --git a/jam-files/engine/strings.h b/jam-files/engine/strings.h
new file mode 100644
index 00000000..33c77bd7
--- /dev/null
+++ b/jam-files/engine/strings.h
@@ -0,0 +1,34 @@
+#ifndef STRINGS_DWA20011024_H
+# define STRINGS_DWA20011024_H
+
+/* Copyright David Abrahams 2004. Distributed under the Boost */
+/* Software License, Version 1.0. (See accompanying */
+/* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */
+
+# include <stddef.h>
+
+typedef struct string
+{
+ char* value;
+ unsigned long size;
+ unsigned long capacity;
+ char opt[32];
+#ifndef NDEBUG
+ char magic[4];
+#endif
+} string;
+
+void string_new( string* );
+void string_copy( string*, char const* );
+void string_free( string* );
+void string_append( string*, char const* );
+void string_append_range( string*, char const*, char const* );
+void string_push_back( string* s, char x );
+void string_reserve( string*, size_t );
+void string_truncate( string*, size_t );
+void string_pop_back( string* );
+char string_back( string* );
+void string_unit_test();
+
+#endif
+
diff --git a/jam-files/engine/subst.c b/jam-files/engine/subst.c
new file mode 100644
index 00000000..75524ecc
--- /dev/null
+++ b/jam-files/engine/subst.c
@@ -0,0 +1,94 @@
+#include <stddef.h>
+#include "jam.h"
+#include "regexp.h"
+#include "hash.h"
+
+#include "newstr.h"
+#include "lists.h"
+#include "parse.h"
+#include "compile.h"
+#include "frames.h"
+
+struct regex_entry
+{
+ const char* pattern;
+ regexp* regex;
+};
+typedef struct regex_entry regex_entry;
+
+static struct hash* regex_hash;
+
+regexp* regex_compile( const char* pattern )
+{
+ regex_entry entry, *e = &entry;
+ entry.pattern = pattern;
+
+ if ( !regex_hash )
+ regex_hash = hashinit(sizeof(regex_entry), "regex");
+
+ if ( hashenter( regex_hash, (HASHDATA **)&e ) )
+ e->regex = regcomp( (char*)pattern );
+
+ return e->regex;
+}
+
+LIST*
+builtin_subst(
+ PARSE *parse,
+ FRAME *frame )
+{
+ LIST* result = L0;
+ LIST* arg1 = lol_get( frame->args, 0 );
+
+ if ( arg1 && list_next(arg1) && list_next(list_next(arg1)) )
+ {
+
+ const char* source = arg1->string;
+ const char* pattern = list_next(arg1)->string;
+ regexp* repat = regex_compile( pattern );
+
+ if ( regexec( repat, (char*)source) )
+ {
+ LIST* subst = list_next(arg1);
+
+ while ((subst = list_next(subst)) != L0)
+ {
+# define BUFLEN 4096
+ char buf[BUFLEN + 1];
+ const char* in = subst->string;
+ char* out = buf;
+
+ for ( in = subst->string; *in && out < buf + BUFLEN; ++in )
+ {
+ if ( *in == '\\' || *in == '$' )
+ {
+ ++in;
+ if ( *in == 0 )
+ {
+ break;
+ }
+ else if ( *in >= '0' && *in <= '9' )
+ {
+ unsigned n = *in - '0';
+ const size_t srclen = repat->endp[n] - repat->startp[n];
+ const size_t remaining = buf + BUFLEN - out;
+ const size_t len = srclen < remaining ? srclen : remaining;
+ memcpy( out, repat->startp[n], len );
+ out += len;
+ continue;
+ }
+ /* fall through and copy the next character */
+ }
+ *out++ = *in;
+ }
+ *out = 0;
+
+ result = list_new( result, newstr( buf ) );
+#undef BUFLEN
+ }
+ }
+ }
+
+ return result;
+}
+
diff --git a/jam-files/engine/timestamp.c b/jam-files/engine/timestamp.c
new file mode 100644
index 00000000..8a59c8c0
--- /dev/null
+++ b/jam-files/engine/timestamp.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/* This file is ALSO:
+ * Copyright 2001-2004 David Abrahams.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+# include "jam.h"
+
+# include "hash.h"
+# include "filesys.h"
+# include "pathsys.h"
+# include "timestamp.h"
+# include "newstr.h"
+# include "strings.h"
+
+/*
+ * timestamp.c - get the timestamp of a file or archive member
+ *
+ * 09/22/00 (seiwald) - downshift names on OS2, too
+ */
+
+/*
+ * BINDING - all known files
+ */
+
+typedef struct _binding BINDING;
+
+struct _binding {
+ char *name;
+ short flags;
+
+# define BIND_SCANNED 0x01 /* if directory or arch, has been scanned */
+
+ short progress;
+
+# define BIND_INIT 0 /* never seen */
+# define BIND_NOENTRY 1 /* timestamp requested but file never found */
+# define BIND_SPOTTED 2 /* file found but not timed yet */
+# define BIND_MISSING 3 /* file found but can't get timestamp */
+# define BIND_FOUND 4 /* file found and time stamped */
+
+ time_t time; /* update time - 0 if not exist */
+};
+
+static struct hash * bindhash = 0;
+static void time_enter( void *, char *, int, time_t );
+
+static char * time_progress[] =
+{
+ "INIT",
+ "NOENTRY",
+ "SPOTTED",
+ "MISSING",
+ "FOUND"
+};
+
+
+/*
+ * timestamp() - return timestamp on a file, if present.
+ */
+
+void timestamp( char * target, time_t * time )
+{
+ PROFILE_ENTER( timestamp );
+
+ PATHNAME f1;
+ PATHNAME f2;
+ BINDING binding;
+ BINDING * b = &binding;
+ string buf[ 1 ];
+#ifdef DOWNSHIFT_PATHS
+ string path;
+ char * p;
+#endif
+
+#ifdef DOWNSHIFT_PATHS
+ string_copy( &path, target );
+ p = path.value;
+
+ do
+ {
+ *p = tolower( *p );
+#ifdef NT
+ /* On NT, we must use backslashes or the file will not be found. */
+ if ( *p == '/' )
+ *p = PATH_DELIM;
+#endif
+ }
+ while ( *p++ );
+
+ target = path.value;
+#endif /* #ifdef DOWNSHIFT_PATHS */
+ string_new( buf );
+
+ if ( !bindhash )
+ bindhash = hashinit( sizeof( BINDING ), "bindings" );
+
+ /* Quick path - is it there? */
+ b->name = target;
+ b->time = b->flags = 0;
+ b->progress = BIND_INIT;
+
+ if ( hashenter( bindhash, (HASHDATA * *)&b ) )
+ b->name = newstr( target ); /* never freed */
+
+ if ( b->progress != BIND_INIT )
+ goto afterscanning;
+
+ b->progress = BIND_NOENTRY;
+
+ /* Not found - have to scan for it. */
+ path_parse( target, &f1 );
+
+ /* Scan directory if not already done so. */
+ {
+ BINDING binding;
+ BINDING * b = &binding;
+
+ f2 = f1;
+ f2.f_grist.len = 0;
+ path_parent( &f2 );
+ path_build( &f2, buf, 0 );
+
+ b->name = buf->value;
+ b->time = b->flags = 0;
+ b->progress = BIND_INIT;
+
+ if ( hashenter( bindhash, (HASHDATA * *)&b ) )
+ b->name = newstr( buf->value ); /* never freed */
+
+ if ( !( b->flags & BIND_SCANNED ) )
+ {
+ file_dirscan( buf->value, time_enter, bindhash );
+ b->flags |= BIND_SCANNED;
+ }
+ }
+
+ /* Scan archive if not already done so. */
+ if ( f1.f_member.len )
+ {
+ BINDING binding;
+ BINDING * b = &binding;
+
+ f2 = f1;
+ f2.f_grist.len = 0;
+ f2.f_member.len = 0;
+ string_truncate( buf, 0 );
+ path_build( &f2, buf, 0 );
+
+ b->name = buf->value;
+ b->time = b->flags = 0;
+ b->progress = BIND_INIT;
+
+ if ( hashenter( bindhash, (HASHDATA * *)&b ) )
+ b->name = newstr( buf->value ); /* never freed */
+
+ if ( !( b->flags & BIND_SCANNED ) )
+ {
+ file_archscan( buf->value, time_enter, bindhash );
+ b->flags |= BIND_SCANNED;
+ }
+ }
+
+ afterscanning:
+
+ if ( b->progress == BIND_SPOTTED )
+ {
+ b->progress = file_time( b->name, &b->time ) < 0
+ ? BIND_MISSING
+ : BIND_FOUND;
+ }
+
+ *time = b->progress == BIND_FOUND ? b->time : 0;
+ string_free( buf );
+#ifdef DOWNSHIFT_PATHS
+ string_free( &path );
+#endif
+
+ PROFILE_EXIT( timestamp );
+}
+
+
+static void time_enter( void * closure, char * target, int found, time_t time )
+{
+ BINDING binding;
+ BINDING * b = &binding;
+ struct hash * bindhash = (struct hash *)closure;
+
+#ifdef DOWNSHIFT_PATHS
+ char path[ MAXJPATH ];
+ char * p = path;
+
+ do *p++ = tolower( *target );
+ while ( *target++ );
+
+ target = path;
+#endif
+
+ b->name = target;
+ b->flags = 0;
+
+ if ( hashenter( bindhash, (HASHDATA * *)&b ) )
+ b->name = newstr( target ); /* never freed */
+
+ b->time = time;
+ b->progress = found ? BIND_FOUND : BIND_SPOTTED;
+
+ if ( DEBUG_BINDSCAN )
+ printf( "time ( %s ) : %s\n", target, time_progress[ b->progress ] );
+}
+
+
+/*
+ * stamps_done() - free timestamp tables.
+ */
+
+void stamps_done()
+{
+ hashdone( bindhash );
+}
diff --git a/jam-files/engine/timestamp.h b/jam-files/engine/timestamp.h
new file mode 100644
index 00000000..f5752763
--- /dev/null
+++ b/jam-files/engine/timestamp.h
@@ -0,0 +1,12 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * timestamp.h - get the timestamp of a file or archive member
+ */
+
+void timestamp( char * target, time_t * time );
+void stamps_done();
diff --git a/jam-files/engine/variable.c b/jam-files/engine/variable.c
new file mode 100644
index 00000000..795f3458
--- /dev/null
+++ b/jam-files/engine/variable.c
@@ -0,0 +1,631 @@
+/*
+ * Copyright 1993, 2000 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/* This file is ALSO:
+ * Copyright 2001-2004 David Abrahams.
+ * Copyright 2005 Reece H. Dunn.
+ * Copyright 2005 Rene Rivera.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+#include "jam.h"
+#include "lists.h"
+#include "parse.h"
+#include "variable.h"
+#include "expand.h"
+#include "hash.h"
+#include "filesys.h"
+#include "newstr.h"
+#include "strings.h"
+#include "pathsys.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+/*
+ * variable.c - handle Jam multi-element variables.
+ *
+ * External routines:
+ *
+ * var_defines() - load a bunch of variable=value settings.
+ * var_string() - expand a string with variables in it.
+ * var_get() - get value of a user defined symbol.
+ * var_set() - set a variable in jam's user defined symbol table.
+ * var_swap() - swap a variable's value with the given one.
+ * var_done() - free variable tables.
+ *
+ * Internal routines:
+ *
+ * var_enter() - make new var symbol table entry, returning var ptr.
+ * var_dump() - dump a variable to stdout.
+ *
+ * 04/13/94 (seiwald) - added shorthand L0 for null list pointer
+ * 08/23/94 (seiwald) - Support for '+=' (append to variable)
+ * 01/22/95 (seiwald) - split environment variables at blanks or :'s
+ * 05/10/95 (seiwald) - split path variables at SPLITPATH (not :)
+ * 09/11/00 (seiwald) - defunct var_list() removed
+ */
+
+static struct hash *varhash = 0;
+
+/*
+ * VARIABLE - a user defined multi-value variable
+ */
+
+typedef struct _variable VARIABLE ;
+
+struct _variable
+{
+ char * symbol;
+ LIST * value;
+};
+
+static VARIABLE * var_enter( char * symbol );
+static void var_dump( char * symbol, LIST * value, char * what );
+
+
+/*
+ * var_hash_swap() - swap all variable settings with those passed
+ *
+ * Used to implement separate settings spaces for modules
+ */
+
+void var_hash_swap( struct hash * * new_vars )
+{
+ struct hash * old = varhash;
+ varhash = *new_vars;
+ *new_vars = old;
+}
+
+
+/*
+ * var_defines() - load a bunch of variable=value settings
+ *
+ * If preprocess is false, take the value verbatim.
+ *
+ * Otherwise, if the variable value is enclosed in quotes, strip the
+ * quotes.
+ *
+ * Otherwise, if variable name ends in PATH, split value at :'s.
+ *
+ * Otherwise, split the value at blanks.
+ */
+
+void var_defines( char * const * e, int preprocess )
+{
+ string buf[1];
+
+ string_new( buf );
+
+ for ( ; *e; ++e )
+ {
+ char * val;
+
+# ifdef OS_MAC
+ /* On the mac (MPW), the var=val is actually var\0val */
+ /* Think different. */
+
+ if ( ( val = strchr( *e, '=' ) ) || ( val = *e + strlen( *e ) ) )
+# else
+ if ( ( val = strchr( *e, '=' ) ) )
+# endif
+ {
+ LIST * l = L0;
+ char * pp;
+ char * p;
+# ifdef OPT_NO_EXTERNAL_VARIABLE_SPLIT
+ char split = '\0';
+# else
+ # ifdef OS_MAC
+ char split = ',';
+ # else
+ char split = ' ';
+ # endif
+# endif
+ size_t len = strlen( val + 1 );
+
+ int quoted = ( val[1] == '"' ) && ( val[len] == '"' ) &&
+ ( len > 1 );
+
+ if ( quoted && preprocess )
+ {
+ string_append_range( buf, val + 2, val + len );
+ l = list_new( l, newstr( buf->value ) );
+ string_truncate( buf, 0 );
+ }
+ else
+ {
+ /* Split *PATH at :'s, not spaces. */
+ if ( val - 4 >= *e )
+ {
+ if ( !strncmp( val - 4, "PATH", 4 ) ||
+ !strncmp( val - 4, "Path", 4 ) ||
+ !strncmp( val - 4, "path", 4 ) )
+ split = SPLITPATH;
+ }
+
+ /* Do the split. */
+ for
+ (
+ pp = val + 1;
+ preprocess && ( ( p = strchr( pp, split ) ) != 0 );
+ pp = p + 1
+ )
+ {
+ string_append_range( buf, pp, p );
+ l = list_new( l, newstr( buf->value ) );
+ string_truncate( buf, 0 );
+ }
+
+ l = list_new( l, newstr( pp ) );
+ }
+
+ /* Get name. */
+ string_append_range( buf, *e, val );
+ var_set( buf->value, l, VAR_SET );
+ string_truncate( buf, 0 );
+ }
+ }
+ string_free( buf );
+}
+
+
+/*
+ * var_string() - expand a string with variables in it
+ *
+ * Copies in to out; doesn't modify targets & sources.
+ */
+
+int var_string( char * in, char * out, int outsize, LOL * lol )
+{
+ char * out0 = out;
+ char * oute = out + outsize - 1;
+
+ while ( *in )
+ {
+ char * lastword;
+ int dollar = 0;
+
+ /* Copy white space. */
+ while ( isspace( *in ) )
+ {
+ if ( out >= oute )
+ return -1;
+ *out++ = *in++;
+ }
+
+ lastword = out;
+
+ /* Copy non-white space, watching for variables. */
+ while ( *in && !isspace( *in ) )
+ {
+ if ( out >= oute )
+ return -1;
+
+ if ( ( in[ 0 ] == '$' ) && ( in[ 1 ] == '(' ) )
+ {
+ ++dollar;
+ *out++ = *in++;
+ }
+ #ifdef OPT_AT_FILES
+ else if ( ( in[ 0 ] == '@' ) && ( in[ 1 ] == '(' ) )
+ {
+ int depth = 1;
+ char * ine = in + 2;
+ char * split = 0;
+
+ /* Scan the content of the response file @() section. */
+ while ( *ine && ( depth > 0 ) )
+ {
+ switch ( *ine )
+ {
+ case '(': ++depth; break;
+ case ')': --depth; break;
+ case ':':
+ if ( ( depth == 1 ) && ( ine[ 1 ] == 'E' ) && ( ine[ 2 ] == '=' ) )
+ split = ine;
+ break;
+ }
+ ++ine;
+ }
+
+ if ( !split )
+ {
+ /* the @() reference doesn't match the @(foo:E=bar) format.
+ hence we leave it alone by copying directly to output. */
+ int l = 0;
+ if ( out + 2 >= oute ) return -1;
+ *( out++ ) = '@';
+ *( out++ ) = '(';
+ l = var_string( in + 2, out, oute - out, lol );
+ if ( l < 0 ) return -1;
+ out += l;
+ if ( out + 1 >= oute ) return -1;
+ *( out++ ) = ')';
+ }
+ else if ( depth == 0 )
+ {
+ string file_name_v;
+ int file_name_l = 0;
+ const char * file_name_s = 0;
+
+ /* Expand the temporary file name var inline. */
+ #if 0
+ string_copy( &file_name_v, "$(" );
+ string_append_range( &file_name_v, in + 2, split );
+ string_push_back( &file_name_v, ')' );
+ #else
+ string_new( &file_name_v );
+ string_append_range( &file_name_v, in + 2, split );
+ #endif
+ file_name_l = var_string( file_name_v.value, out, oute - out + 1, lol );
+ string_free( &file_name_v );
+ if ( file_name_l < 0 ) return -1;
+ file_name_s = out;
+
+ /* For stdout/stderr we will create a temp file and generate
+ * a command that outputs the content as needed.
+ */
+ if ( ( strcmp( "STDOUT", out ) == 0 ) ||
+ ( strcmp( "STDERR", out ) == 0 ) )
+ {
+ int err_redir = strcmp( "STDERR", out ) == 0;
+ out[ 0 ] = '\0';
+ file_name_s = path_tmpfile();
+ file_name_l = strlen(file_name_s);
+ #ifdef OS_NT
+ if ( ( out + 7 + file_name_l + ( err_redir ? 5 : 0 ) ) >= oute )
+ return -1;
+ sprintf( out,"type \"%s\"%s", file_name_s,
+ err_redir ? " 1>&2" : "" );
+ #else
+ if ( ( out + 6 + file_name_l + ( err_redir ? 5 : 0 ) ) >= oute )
+ return -1;
+ sprintf( out,"cat \"%s\"%s", file_name_s,
+ err_redir ? " 1>&2" : "" );
+ #endif
+ /* We also make sure that the temp files created by this
+ * get nuked eventually.
+ */
+ file_remove_atexit( file_name_s );
+ }
+
+ /* Expand the file value into the file reference. */
+ var_string_to_file( split + 3, ine - split - 4, file_name_s,
+ lol );
+
+ /* Continue on with the expansion. */
+ out += strlen( out );
+ }
+
+ /* And continue with the parsing just past the @() reference. */
+ in = ine;
+ }
+ #endif
+ else
+ {
+ *out++ = *in++;
+ }
+ }
+
+ /* Add zero to 'out' so that 'lastword' is correctly zero-terminated. */
+ if ( out >= oute )
+ return -1;
+ /* Do not increment, intentionally. */
+ *out = '\0';
+
+ /* If a variable encountered, expand it and and embed the
+ * space-separated members of the list in the output.
+ */
+ if ( dollar )
+ {
+ LIST * l = var_expand( L0, lastword, out, lol, 0 );
+
+ out = lastword;
+
+ while ( l )
+ {
+ int so = strlen( l->string );
+
+ if ( out + so >= oute )
+ return -1;
+
+ strcpy( out, l->string );
+ out += so;
+ l = list_next( l );
+ if ( l ) *out++ = ' ';
+ }
+
+ list_free( l );
+ }
+ }
+
+ if ( out >= oute )
+ return -1;
+
+ *out++ = '\0';
+
+ return out - out0;
+}
+
+
+void var_string_to_file( const char * in, int insize, const char * out, LOL * lol )
+{
+ char const * ine = in + insize;
+ FILE * out_file = 0;
+ int out_debug = DEBUG_EXEC ? 1 : 0;
+ if ( globs.noexec )
+ {
+ /* out_debug = 1; */
+ }
+ else if ( strcmp( out, "STDOUT" ) == 0 )
+ {
+ out_file = stdout;
+ }
+ else if ( strcmp( out, "STDERR" ) == 0 )
+ {
+ out_file = stderr;
+ }
+ else
+ {
+ /* Handle "path to file" filenames. */
+ string out_name;
+ if ( ( out[ 0 ] == '"' ) && ( out[ strlen( out ) - 1 ] == '"' ) )
+ {
+ string_copy( &out_name, out + 1 );
+ string_truncate( &out_name, out_name.size - 1 );
+ }
+ else
+ {
+ string_copy( &out_name,out );
+ }
+ out_file = fopen( out_name.value, "w" );
+ if ( !out_file )
+ {
+ printf( "failed to write output file '%s'!\n", out_name.value );
+ exit( EXITBAD );
+ }
+ string_free( &out_name );
+ }
+
+ if ( out_debug ) printf( "\nfile %s\n", out );
+
+ while ( *in && ( in < ine ) )
+ {
+ int dollar = 0;
+ const char * output_0 = in;
+ const char * output_1 = in;
+
+ /* Copy white space. */
+ while ( ( output_1 < ine ) && isspace( *output_1 ) )
+ ++output_1;
+
+ if ( output_0 < output_1 )
+ {
+ if ( out_file ) fwrite( output_0, output_1 - output_0, 1, out_file );
+ if ( out_debug ) fwrite( output_0, output_1 - output_0, 1, stdout );
+ }
+ output_0 = output_1;
+
+ /* Copy non-white space, watching for variables. */
+ while ( ( output_1 < ine ) && *output_1 && !isspace( *output_1 ) )
+ {
+ if ( ( output_1[ 0 ] == '$' ) && ( output_1[ 1 ] == '(' ) )
+ ++dollar;
+ ++output_1;
+ }
+
+ /* If a variable encountered, expand it and embed the space-separated
+ * members of the list in the output.
+ */
+ if ( dollar )
+ {
+ LIST * l = var_expand( L0, (char *)output_0, (char *)output_1, lol, 0 );
+
+ while ( l )
+ {
+ if ( out_file ) fputs( l->string, out_file );
+ if ( out_debug ) puts( l->string );
+ l = list_next( l );
+ if ( l )
+ {
+ if ( out_file ) fputc( ' ', out_file );
+ if ( out_debug ) fputc( ' ', stdout );
+ }
+ }
+
+ list_free( l );
+ }
+ else if ( output_0 < output_1 )
+ {
+ if ( out_file )
+ {
+ const char * output_n = output_0;
+ while ( output_n < output_1 )
+ {
+ output_n += fwrite( output_n, 1, output_1-output_n, out_file );
+ }
+ }
+ if ( out_debug )
+ {
+ const char * output_n = output_0;
+ while ( output_n < output_1 )
+ {
+ output_n += fwrite( output_n, 1, output_1-output_n, stdout );
+ }
+ }
+ }
+
+ in = output_1;
+ }
+
+ if ( out_file && ( out_file != stdout ) && ( out_file != stderr ) )
+ {
+ fflush( out_file );
+ fclose( out_file );
+ }
+
+ if ( out_debug ) fputc( '\n', stdout );
+}
+
+
+/*
+ * var_get() - get value of a user defined symbol.
+ *
+ * Returns NULL if symbol unset.
+ */
+
+LIST * var_get( char * symbol )
+{
+ LIST * result = 0;
+#ifdef OPT_AT_FILES
+ /* Some "fixed" variables... */
+ if ( strcmp( "TMPDIR", symbol ) == 0 )
+ {
+ result = list_new( L0, newstr( (char *)path_tmpdir() ) );
+ }
+ else if ( strcmp( "TMPNAME", symbol ) == 0 )
+ {
+ result = list_new( L0, newstr( (char *)path_tmpnam() ) );
+ }
+ else if ( strcmp( "TMPFILE", symbol ) == 0 )
+ {
+ result = list_new( L0, newstr( (char *)path_tmpfile() ) );
+ }
+ else if ( strcmp( "STDOUT", symbol ) == 0 )
+ {
+ result = list_new( L0, newstr( "STDOUT" ) );
+ }
+ else if ( strcmp( "STDERR", symbol ) == 0 )
+ {
+ result = list_new( L0, newstr( "STDERR" ) );
+ }
+ else
+#endif
+ {
+ VARIABLE var;
+ VARIABLE * v = &var;
+
+ v->symbol = symbol;
+
+ if ( varhash && hashcheck( varhash, (HASHDATA * *)&v ) )
+ {
+ if ( DEBUG_VARGET )
+ var_dump( v->symbol, v->value, "get" );
+ result = v->value;
+ }
+ }
+ return result;
+}
+
+
+/*
+ * var_set() - set a variable in Jam's user defined symbol table.
+ *
+ * 'flag' controls the relationship between new and old values of the variable:
+ * SET replaces the old with the new; APPEND appends the new to the old; DEFAULT
+ * only uses the new if the variable was previously unset.
+ *
+ * Copies symbol. Takes ownership of value.
+ */
+
+void var_set( char * symbol, LIST * value, int flag )
+{
+ VARIABLE * v = var_enter( symbol );
+
+ if ( DEBUG_VARSET )
+ var_dump( symbol, value, "set" );
+
+ switch ( flag )
+ {
+ case VAR_SET:
+ /* Replace value */
+ list_free( v->value );
+ v->value = value;
+ break;
+
+ case VAR_APPEND:
+ /* Append value */
+ v->value = list_append( v->value, value );
+ break;
+
+ case VAR_DEFAULT:
+ /* Set only if unset */
+ if ( !v->value )
+ v->value = value;
+ else
+ list_free( value );
+ break;
+ }
+}
+
+
+/*
+ * var_swap() - swap a variable's value with the given one.
+ */
+
+LIST * var_swap( char * symbol, LIST * value )
+{
+ VARIABLE * v = var_enter( symbol );
+ LIST * oldvalue = v->value;
+ if ( DEBUG_VARSET )
+ var_dump( symbol, value, "set" );
+ v->value = value;
+ return oldvalue;
+}
+
+
+/*
+ * var_enter() - make new var symbol table entry, returning var ptr.
+ */
+
+static VARIABLE * var_enter( char * symbol )
+{
+ VARIABLE var;
+ VARIABLE * v = &var;
+
+ if ( !varhash )
+ varhash = hashinit( sizeof( VARIABLE ), "variables" );
+
+ v->symbol = symbol;
+ v->value = 0;
+
+ if ( hashenter( varhash, (HASHDATA * *)&v ) )
+ v->symbol = newstr( symbol ); /* never freed */
+
+ return v;
+}
+
+
+/*
+ * var_dump() - dump a variable to stdout.
+ */
+
+static void var_dump( char * symbol, LIST * value, char * what )
+{
+ printf( "%s %s = ", what, symbol );
+ list_print( value );
+ printf( "\n" );
+}
+
+
+/*
+ * var_done() - free variable tables.
+ */
+
+static void delete_var_( void * xvar, void * data )
+{
+ VARIABLE * v = (VARIABLE *)xvar;
+ freestr( v->symbol );
+ list_free( v-> value );
+}
+
+
+void var_done()
+{
+ hashenumerate( varhash, delete_var_, (void *)0 );
+ hashdone( varhash );
+}
diff --git a/jam-files/engine/variable.h b/jam-files/engine/variable.h
new file mode 100644
index 00000000..5c49e3ca
--- /dev/null
+++ b/jam-files/engine/variable.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 1993, 2000 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * variable.h - handle jam multi-element variables
+ */
+
+struct hash;
+
+void var_defines( char* const *e, int preprocess );
+int var_string( char *in, char *out, int outsize, LOL *lol );
+LIST * var_get( char *symbol );
+void var_set( char *symbol, LIST *value, int flag );
+LIST * var_swap( char *symbol, LIST *value );
+void var_done();
+void var_hash_swap( struct hash** );
+
+/** Expands the "in" expression directly into the "out" file.
+ The file can be one of: a path, STDOUT, or STDERR to send
+ the output to a file overwriting previous content, to
+ the console, or to the error output respectively.
+*/
+void var_string_to_file( const char * in, int insize, const char * out, LOL * lol );
+
+/*
+ * Defines for var_set().
+ */
+
+# define VAR_SET 0 /* override previous value */
+# define VAR_APPEND 1 /* append to previous value */
+# define VAR_DEFAULT 2 /* set only if no previous value */
+
diff --git a/jam-files/engine/w32_getreg.c b/jam-files/engine/w32_getreg.c
new file mode 100644
index 00000000..5a06f43e
--- /dev/null
+++ b/jam-files/engine/w32_getreg.c
@@ -0,0 +1,207 @@
+/*
+Copyright Paul Lin 2003. Copyright 2006 Bojan Resnik.
+Distributed under the Boost Software License, Version 1.0. (See accompanying
+file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+*/
+
+# include "jam.h"
+
+# if defined( OS_NT ) || defined( OS_CYGWIN )
+
+# include "lists.h"
+# include "newstr.h"
+# include "parse.h"
+# include "frames.h"
+# include "strings.h"
+
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+
+# define MAX_REGISTRY_DATA_LENGTH 4096
+# define MAX_REGISTRY_KEYNAME_LENGTH 256
+# define MAX_REGISTRY_VALUENAME_LENGTH 16384
+
+typedef struct
+{
+ LPCSTR name;
+ HKEY value;
+} KeyMap;
+
+static const KeyMap dlRootKeys[] = {
+ { "HKLM", HKEY_LOCAL_MACHINE },
+ { "HKCU", HKEY_CURRENT_USER },
+ { "HKCR", HKEY_CLASSES_ROOT },
+ { "HKEY_LOCAL_MACHINE", HKEY_LOCAL_MACHINE },
+ { "HKEY_CURRENT_USER", HKEY_CURRENT_USER },
+ { "HKEY_CLASSES_ROOT", HKEY_CLASSES_ROOT },
+ { 0, 0 }
+};
+
+static HKEY get_key(char const** path)
+{
+ const KeyMap *p;
+
+ for (p = dlRootKeys; p->name; ++p)
+ {
+ int n = strlen(p->name);
+ if (!strncmp(*path,p->name,n))
+ {
+ if ((*path)[n] == '\\' || (*path)[n] == 0)
+ {
+ *path += n + 1;
+ break;
+ }
+ }
+ }
+
+ return p->value;
+}
+
+LIST*
+builtin_system_registry(
+ PARSE *parse,
+ FRAME *frame )
+{
+ char const* path = lol_get(frame->args, 0)->string;
+ LIST* result = L0;
+ HKEY key = get_key(&path);
+
+ if (
+ key != 0
+ && ERROR_SUCCESS == RegOpenKeyEx(key, path, 0, KEY_QUERY_VALUE, &key)
+ )
+ {
+ DWORD type;
+ BYTE data[MAX_REGISTRY_DATA_LENGTH];
+ DWORD len = sizeof(data);
+ LIST const* const field = lol_get(frame->args, 1);
+
+ if ( ERROR_SUCCESS ==
+ RegQueryValueEx(key, field ? field->string : 0, 0, &type, data, &len) )
+ {
+ switch (type)
+ {
+
+ case REG_EXPAND_SZ:
+ {
+ long len;
+ string expanded[1];
+ string_new(expanded);
+
+ while (
+ (len = ExpandEnvironmentStrings(
+ (LPCSTR)data, expanded->value, expanded->capacity))
+ > expanded->capacity
+ )
+ string_reserve(expanded, len);
+
+ expanded->size = len - 1;
+
+ result = list_new( result, newstr(expanded->value) );
+ string_free( expanded );
+ }
+ break;
+
+ case REG_MULTI_SZ:
+ {
+ char* s;
+
+ for (s = (char*)data; *s; s += strlen(s) + 1)
+ result = list_new( result, newstr(s) );
+
+ }
+ break;
+
+ case REG_DWORD:
+ {
+ char buf[100];
+ sprintf( buf, "%u", *(PDWORD)data );
+ result = list_new( result, newstr(buf) );
+ }
+ break;
+
+ case REG_SZ:
+ result = list_new( result, newstr((char*)data) );
+ break;
+ }
+ }
+ RegCloseKey(key);
+ }
+ return result;
+}
+
+static LIST* get_subkey_names(HKEY key, char const* path)
+{
+ LIST* result = 0;
+
+ if ( ERROR_SUCCESS ==
+ RegOpenKeyEx(key, path, 0, KEY_ENUMERATE_SUB_KEYS, &key)
+ )
+ {
+ char name[MAX_REGISTRY_KEYNAME_LENGTH];
+ DWORD name_size = sizeof(name);
+ DWORD index;
+ FILETIME last_write_time;
+
+ for ( index = 0;
+ ERROR_SUCCESS == RegEnumKeyEx(
+ key, index, name, &name_size, 0, 0, 0, &last_write_time);
+ ++index,
+ name_size = sizeof(name)
+ )
+ {
+ name[name_size] = 0;
+ result = list_append(result, list_new(0, newstr(name)));
+ }
+
+ RegCloseKey(key);
+ }
+
+ return result;
+}
+
+static LIST* get_value_names(HKEY key, char const* path)
+{
+ LIST* result = 0;
+
+ if ( ERROR_SUCCESS == RegOpenKeyEx(key, path, 0, KEY_QUERY_VALUE, &key) )
+ {
+ char name[MAX_REGISTRY_VALUENAME_LENGTH];
+ DWORD name_size = sizeof(name);
+ DWORD index;
+
+ for ( index = 0;
+ ERROR_SUCCESS == RegEnumValue(
+ key, index, name, &name_size, 0, 0, 0, 0);
+ ++index,
+ name_size = sizeof(name)
+ )
+ {
+ name[name_size] = 0;
+ result = list_append(result, list_new(0, newstr(name)));
+ }
+
+ RegCloseKey(key);
+ }
+
+ return result;
+}
+
+LIST*
+builtin_system_registry_names(
+ PARSE *parse,
+ FRAME *frame )
+{
+ char const* path = lol_get(frame->args, 0)->string;
+ char const* result_type = lol_get(frame->args, 1)->string;
+
+ HKEY key = get_key(&path);
+
+ if ( !strcmp(result_type, "subkeys") )
+ return get_subkey_names(key, path);
+ if ( !strcmp(result_type, "values") )
+ return get_value_names(key, path);
+ return 0;
+}
+
+# endif
diff --git a/jam-files/engine/yyacc.c b/jam-files/engine/yyacc.c
new file mode 100644
index 00000000..b5efc96b
--- /dev/null
+++ b/jam-files/engine/yyacc.c
@@ -0,0 +1,268 @@
+/* Copyright 2002 Rene Rivera.
+** Distributed under the Boost Software License, Version 1.0.
+** (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+/*
+# yyacc - yacc wrapper
+#
+# Allows tokens to be written as `literal` and then automatically
+# substituted with #defined tokens.
+#
+# Usage:
+# yyacc file.y filetab.h file.yy
+#
+# inputs:
+# file.yy yacc grammar with ` literals
+#
+# outputs:
+# file.y yacc grammar
+# filetab.h array of string <-> token mappings
+#
+# 3-13-93
+# Documented and p moved in sed command (for some reason,
+# s/x/y/p doesn't work).
+# 10-12-93
+# Take basename as second argument.
+# 12-31-96
+# reversed order of args to be compatible with GenFile rule
+# 11-20-2002
+# Reimplemented as a C program for portability. (Rene Rivera)
+*/
+
+void print_usage();
+char * copy_string(char * s, int l);
+char * tokenize_string(char * s);
+int cmp_literal(const void * a, const void * b);
+
+typedef struct
+{
+ char * string;
+ char * token;
+} literal;
+
+int main(int argc, char ** argv)
+{
+ int result = 0;
+ if (argc != 4)
+ {
+ print_usage();
+ result = 1;
+ }
+ else
+ {
+ FILE * token_output_f = 0;
+ FILE * grammar_output_f = 0;
+ FILE * grammar_source_f = 0;
+
+ grammar_source_f = fopen(argv[3],"r");
+ if (grammar_source_f == 0) { result = 1; }
+ if (result == 0)
+ {
+ literal literals[1024];
+ int t = 0;
+ char l[2048];
+ while (1)
+ {
+ if (fgets(l,2048,grammar_source_f) != 0)
+ {
+ char * c = l;
+ while (1)
+ {
+ char * c1 = strchr(c,'`');
+ if (c1 != 0)
+ {
+ char * c2 = strchr(c1+1,'`');
+ if (c2 != 0)
+ {
+ literals[t].string = copy_string(c1+1,c2-c1-1);
+ literals[t].token = tokenize_string(literals[t].string);
+ t += 1;
+ c = c2+1;
+ }
+ else
+ break;
+ }
+ else
+ break;
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+ literals[t].string = 0;
+ literals[t].token = 0;
+ qsort(literals,t,sizeof(literal),cmp_literal);
+ {
+ int p = 1;
+ int i = 1;
+ while (literals[i].string != 0)
+ {
+ if (strcmp(literals[p-1].string,literals[i].string) != 0)
+ {
+ literals[p] = literals[i];
+ p += 1;
+ }
+ i += 1;
+ }
+ literals[p].string = 0;
+ literals[p].token = 0;
+ t = p;
+ }
+ token_output_f = fopen(argv[2],"w");
+ if (token_output_f != 0)
+ {
+ int i = 0;
+ while (literals[i].string != 0)
+ {
+ fprintf(token_output_f," { \"%s\", %s },\n",literals[i].string,literals[i].token);
+ i += 1;
+ }
+ fclose(token_output_f);
+ }
+ else
+ result = 1;
+ if (result == 0)
+ {
+ grammar_output_f = fopen(argv[1],"w");
+ if (grammar_output_f != 0)
+ {
+ int i = 0;
+ while (literals[i].string != 0)
+ {
+ fprintf(grammar_output_f,"%%token %s\n",literals[i].token);
+ i += 1;
+ }
+ rewind(grammar_source_f);
+ while (1)
+ {
+ if (fgets(l,2048,grammar_source_f) != 0)
+ {
+ char * c = l;
+ while (1)
+ {
+ char * c1 = strchr(c,'`');
+ if (c1 != 0)
+ {
+ char * c2 = strchr(c1+1,'`');
+ if (c2 != 0)
+ {
+ literal key;
+ literal * replacement = 0;
+ key.string = copy_string(c1+1,c2-c1-1);
+ key.token = 0;
+ replacement = (literal*)bsearch(
+ &key,literals,t,sizeof(literal),cmp_literal);
+ *c1 = 0;
+ fprintf(grammar_output_f,"%s%s",c,replacement->token);
+ c = c2+1;
+ }
+ else
+ {
+ fprintf(grammar_output_f,"%s",c);
+ break;
+ }
+ }
+ else
+ {
+ fprintf(grammar_output_f,"%s",c);
+ break;
+ }
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+ fclose(grammar_output_f);
+ }
+ else
+ result = 1;
+ }
+ }
+ if (result != 0)
+ {
+ perror("yyacc");
+ }
+ }
+ return result;
+}
+
+static char * usage[] = {
+ "yyacc <grammar output.y> <token table output.h> <grammar source.yy>",
+ 0 };
+
+void print_usage()
+{
+ char ** u;
+ for (u = usage; *u != 0; ++u)
+ {
+ fputs(*u,stderr); putc('\n',stderr);
+ }
+}
+
+char * copy_string(char * s, int l)
+{
+ char * result = (char*)malloc(l+1);
+ strncpy(result,s,l);
+ result[l] = 0;
+ return result;
+}
+
+char * tokenize_string(char * s)
+{
+ char * result;
+ char * literal = s;
+ int l;
+ int c;
+
+ if (strcmp(s,":") == 0) literal = "_colon";
+ else if (strcmp(s,"!") == 0) literal = "_bang";
+ else if (strcmp(s,"!=") == 0) literal = "_bang_equals";
+ else if (strcmp(s,"&&") == 0) literal = "_amperamper";
+ else if (strcmp(s,"&") == 0) literal = "_amper";
+ else if (strcmp(s,"+") == 0) literal = "_plus";
+ else if (strcmp(s,"+=") == 0) literal = "_plus_equals";
+ else if (strcmp(s,"||") == 0) literal = "_barbar";
+ else if (strcmp(s,"|") == 0) literal = "_bar";
+ else if (strcmp(s,";") == 0) literal = "_semic";
+ else if (strcmp(s,"-") == 0) literal = "_minus";
+ else if (strcmp(s,"<") == 0) literal = "_langle";
+ else if (strcmp(s,"<=") == 0) literal = "_langle_equals";
+ else if (strcmp(s,">") == 0) literal = "_rangle";
+ else if (strcmp(s,">=") == 0) literal = "_rangle_equals";
+ else if (strcmp(s,".") == 0) literal = "_period";
+ else if (strcmp(s,"?") == 0) literal = "_question";
+ else if (strcmp(s,"?=") == 0) literal = "_question_equals";
+ else if (strcmp(s,"=") == 0) literal = "_equals";
+ else if (strcmp(s,",") == 0) literal = "_comma";
+ else if (strcmp(s,"[") == 0) literal = "_lbracket";
+ else if (strcmp(s,"]") == 0) literal = "_rbracket";
+ else if (strcmp(s,"{") == 0) literal = "_lbrace";
+ else if (strcmp(s,"}") == 0) literal = "_rbrace";
+ else if (strcmp(s,"(") == 0) literal = "_lparen";
+ else if (strcmp(s,")") == 0) literal = "_rparen";
+ l = strlen(literal)+2;
+ result = (char*)malloc(l+1);
+ for (c = 0; literal[c] != 0; ++c)
+ {
+ result[c] = toupper(literal[c]);
+ }
+ result[l-2] = '_';
+ result[l-1] = 't';
+ result[l] = 0;
+ return result;
+}
+
+int cmp_literal(const void * a, const void * b)
+{
+ return strcmp(((const literal *)a)->string,((const literal *)b)->string);
+}