+ The SubDir rule initializes a set of variables
+ that are used by Main and other rules to
+ uniquely identify the source files in this
+ directory and assign locations to the targets
+ built from files in this directory.
+
+ When you have set a root variable, e.g., $(TOP),
+ SubDir constructs path names rooted with $(TOP),
+ e.g., $(TOP)/src/util.
+ Otherwise, SubDir constructs relative pathnames
+ to the root directory, computed from the number
+ of arguments to the first SubDir rule, e.g.,
+ ../../src/util. In either case, the SubDir
+ rule constructs the path names that locate source
+ files.
+ You'll see how this is useful later.
+
+
+
+
+ The SubDir rule takes as its first argument the root
+ variable's name and takes as subsequent arguments the
+ directory names leading from the root to the directory of
+ the current Jamfile. Note that the name of the subdirectory
+ is given as individual elements: the SubDir rule
+ does not use system-specific directory name syntax.
+
+
+
+ SubInclude Rule
+
+ The SubInclude rule is used in a Jamfile to cause another
+ Jamfile to be read in.
+ Its arguments are in the same format as
+ SubDir's.
+
+ The recommended practice is only to include one level of
+ subdirectories at a time, and let the Jamfile in each subdirectory
+ include its own subdirectories. This allows a
+ user to sit in any arbitrary directory of the source tree
+ and build that subtree. For example:
+
+ # This is $(TOP)/Jamfile, top level Jamfile for mondo project.
+
+ SubInclude TOP src ;
+ SubInclude TOP man ;
+ SubInclude TOP misc ;
+ SubInclude TOP util ;
+
+ If a directory has both subdirectories of its own as well
+ as files that need building, the SubIncludes should be
+ either before the SubDir rule or be at the end of the Jamfile
+ - not between the SubDir and other rule invocations.
+ For example:
+
+ # This is $(TOP)/src/Jamfile:
+
+ SubDir TOP src ;
+
+ Main mondo : mondo.c ;
+ LinkLibraries mondo : libmisc libutil ;
+
+ SubInclude TOP src misc ;
+ SubInclude TOP src util ;
+
+
+ (jam processes all the Jamfiles it reads as if
+ it were reading one single, large Jamfile.
+ Build rules like Main and LinkLibraries rely on the
+ preceding SubDir rule to set up source file and
+ output file locations, and SubIncludes rules read in
+ Jamfiles that contain SubDir rules. So if you put
+ a SubIncludes rule between a SubDir and a Main
+ rule, jam will try to find the source files
+ for the Main rule in the wrong directory.)
+
+
+ Variables Used to Handle Directory Trees
+
+ The following variables are set by the SubDir rule
+ and used by the Jambase rules that define file targets:
+
+
+
+|
+ SEARCH_SOURCE
+ | | The SubDir targets (e.g., "TOP src util")
+ are used to construct a pathname (e.g., $(TOP)/src/util),
+ and that pathname is assigned to $(SEARCH_SOURCE).
+ Rules like Main and Library use $(SEARCH_SOURCE)
+ to set search paths on source files.
+ |
|
+ LOCATE_SOURCE
+ | | Initialized by the SubDir rule to the same
+ value as $(SEARCH_SOURCE), unless ALL_LOCATE_TARGET
+ is set.
+ $(LOCATE_SOURCE) is used by rules that build
+ generated source files (e.g., Yacc and Lex) to
+ set location of output files.
+ Thus the default location of built source files
+ is the directory of the Jamfile that defines them.
+ |
|
+ LOCATE_TARGET
+ | | Initalized by the SubDir rule to the same
+ value as $(SEARCH_SOURCE), unless ALL_LOCATE_TARGET
+ is set.
+ $(LOCATE_TARGET) is used by rules that build
+ binary objects (e.g., Main and Library) to
+ set location of output files.
+ Thus the default location of built binaray files
+ is the directory of the Jamfile that defines them.
+ |
|
+ ALL_LOCATE_TARGET
+ | |
+ If $(ALL_LOCATE_TARGET) is set, LOCATE_SOURCE
+ and and LOCATE_TARGET are set to $(ALL_LOCATE_TARGET)
+ instead of to $(SEARCH_SOURCE). This can be used to
+ direct built files to be written to a location outside
+ of the source tree, and enables building from read-only
+ source trees.
+ |
|
+ SOURCE_GRIST
+ | | The SubDir targets are formed into a string
+ like "src!util" and that string is assigned to
+ SOURCE_GRIST. Rules that define file targets
+ use $(SOURCE_GRIST) to set the "grist" attribute
+ on targets. This is used to assure uniqueness
+ of target identifiers where filenames themselves
+ are not unique.
+ For example, the target identifiers of
+ $(TOP)/src/client/main.c and $(TOP)/src/server/main.c
+ would be <src!client>main.c and <src!server>main.c.
+ |
+
+
+ The $(LOCATE_TARGET) and $(SEARCH_SOURCE) variables are used
+ extensively by rules in Jambase: most rules that generate
+ targets (like Main, Object, etc.) set $(LOCATE) to
+ $(LOCATE_TARGET) for the targets they generate, and rules
+ that use sources (most all of them) set $(SEARCH) to be
+ $(SEARCH_SOURCE) for the sources they use.
+
+ $(LOCATE) and $(SEARCH) are better explained in
+ The Jam Executable Program
+ but in brief they tell jam where to create new targets and
+ where to find existing ones, respectively.
+
+ Note that you can reset these variables
+ after SubDir sets them. For example, this Jamfile builds
+ a program called gensrc, then runs it to create a source file
+ called new.c:
+
+ SubDir TOP src util ;
+ Main gensrc : gensrc.c ;
+ LOCATE_SOURCE = $(NEWSRC) ;
+ GenFile new.c : gensrc ;
+
+ By default, new.c would be written into the
+ $(TOP)/src/util directory, but resetting LOCATE_SOURCE causes
+ it to be written to the $(NEWSRC) directory. ($(NEWSRC) is assumed
+ to have been set elsewhere, e.g., in Jamrules.)
+
+
+ VMS Notes
+
+ On VMS, the logical name table is not imported as is the
+ environment on UNIX. To use the SubDir and related rules,
+ you must set the value of the variable that names the root
+ directory. For example:
+
+ TOP = USR_DISK:[JONES.SRC] ;
+
+ SubInclude TOP util ;
+
+ The variable must have a value that looks like a directory
+ or device. If you choose, you can use a concealed logical.
+ For example:
+
+ TOP = TOP: ;
+
+ SubInclude TOP util ;
+
+ The : at the end of TOP makes the value of $(TOP) look
+ like a device name, which jam respects as a directory name
+ and will use when trying to access files. TOP must then
+ be defined from DCL:
+
+ $ define/job/translation=concealed TOP DK100:[USERS.JONES.SRC.]
+
+ Note three things: the concealed translation allows the
+ logical to be used as a device name; the device name in
+ the logical (here DK100) cannot itself be concealed logical
+ (VMS rules, man); and the directory component of the
+ definition must end in a period (more VMS rules).
+
+
+Building Executables and Libraries
+
+
+The rules that build executables and libraries are: Main, Library,
+and LinkLibraries.
+
+ Main Rule
+
+ The Main rule compiles source files and links the resulting
+ objects into an executable. For example:
+
+ Main myprog : main.c util.c ;
+
+ This compiles main.c and util.c and links main.o and
+ util.o into myprog. The object files and resulting
+ executable are named appropriately for the platform.
+
+ Main can also be used to build shared libraries and/or
+ dynamic link libraries, since those are also linked
+ objects. E.g.:
+
+ Main driver$(SUFSHR) : driver.c ;
+
+ Normally, Main uses $(SUFEXE) to determine the suffix on
+ the filename of the built target. To override it,
+ you can supply a suffix explicity.
+ In this case,
+ $(SUFSHR) is assumed to be the OS-specific shared library
+ suffix, defined in Jamrules with something
+ like:
+
+ if $(UNIX) { SUFSHR = .so ; }
+ else if $(NT) { SUFSHR = .dll ; }
+
+
+ Main uses the Objects rule to compile source targets.
+
+
+ Library Rule
+
+ The Library rule compiles source files, archives the
+ resulting object files into a library, and then deletes
+ the object files. For example:
+
+ Library libstring : strcmp.c strcpy.c strlen.c ;
+ Library libtree : treemake.c treetrav.c ;
+
+ This compiles five source files, archives three of the
+ object files into libstring and the other two into libtree.
+ Actual library filenames are formed with the $(SUFLIB) suffix.
+ Once the objects are safely in the libraries, the
+ objects are deleted.
+
+ Library uses the Objects rule to compile source files.
+
+
+ LinkLibraries Rule
+
+ To link executables with built libraries, use
+ the LinkLibraries rule. For example:
+
+ Main myprog : main.c util.c ;
+ LinkLibraries myprog : libstring libtree ;
+
+ The LinkLibraries rule does two things: it makes the
+ libraries dependencies of the executable, so that they get
+ built first; and it makes the libraries show up on the
+ command line that links the executable. The ordering of
+ the lines above is not important, because jam builds targets
+ in the order that they are needed.
+
+ You can put multiple libraries on a single invocation of
+ the LinkLibraries rule, or you can provide them in multiple
+ invocations. In both cases, the libraries appear on
+ the link command line in the order in which they were
+ encountered. You can also provide multiple executables to
+ the LinkLibraries rule, if they need the same libraries,
+ e.g.:
+
+ LinkLibraries prog1 prog2 prog3 : libstring libtree ;
+
+
+
+ Variables Used in Building Executables and Libraries
+
+
+
+|
+ AR
+ | | Archive command, used for Library targets.
+ |
|
+ SUFEXE
+ | * | Suffix on filenames of executables referenced
+ by Main and LinkLibraries.
+ |
|
+ LINK
+ | | Link command, used for Main targets.
+ |
|
+ LINKFLAGS
+ | | Linker flags.
+ |
|
+ LINKLIBS
+ | | Link libraries that aren't dependencies. (See note
+ below.)
+ |
|
+ EXEMODE
+ | * | File permissions on Main targets.
+ |
|
+ MODE
+ | | Target-specific file permissions on Main targets
+ (set from $(EXEMODE))
+ |
|
+ RANLIB
+ | | Name of ranlib program, if any.
+ |
+
+
+
+ Variables above marked with "*" are used by the Main,
+ Library, and LinkLibraries rules. Their values at the
+ time the rules are invoked are used to set target-specific
+ variables.
+
+ All other variables listed above are globally defined,
+ and are used in actions that update Main and Library
+ targets. This means that the global values of those
+ variables are used, uness target-specific values have
+ been set.
+ (For instance, a target-specific MODE value is set by
+ the Main rule.)
+ The target-specific values always override
+ global values.
+
+ Note that there are two ways to specify link libraries for
+ executables:
+
+ - Use the LinkLibraries rule
+ to specify built libraries; i.e., libraries
+ that are built by Library rules. This assures that
+ these libraries are built first, and that Main targets are
+ rebuilt when the libraries are updated.
+
+
- Use the LINKLIBS variable to specify external
+ libraries; e.g., system libraries or third-party libraries.
+ The LINKLIBS variable must be set to the the actual
+ link command flag that specifies the libraries.
+
+
+
+ For example:
+
+ #In Jamrules:
+ if $(UNIX) { X11LINKLIBS = -lXext -lX11 ; }
+ if $(NT) { X11LINKLIBS = libext.lib libX11.lib ; }
+
+ #In Jamfile:
+ Main xprog : xprog.c ;
+ LINKLIBS on xprog$(SUFEXE) = $(X11LINKLIBS) ;
+ LinkLibraries xprog : libxutil ;
+ Library libxutil : xtop.c xbottom.c xutil.c ;
+
+ This example uses the Jam syntax "variable on target" to
+ set a target-specific variable. In this way, only xprog
+ will be linked with this special $(X11LINKLIBS),
+ even if other executables were going to be built
+ by the same Jamfile. Note that when you set a variable
+ on a target, you have to specify the target identifer
+ exactly, which in this case is the suffixed filename of
+ the executable.
+ The actual link command line on Unix, for example, would
+ look something like this:
+
+ cc -o xprog xprog.o libxutil.a -lXext -lX11
+
+
+Compiling
+
+ Compiling of source files occurs normally as a byproduct
+ of the Main or Library rules, which call the rules
+ described here. These rules may also be called explicitly
+ if the Main and Library behavior doesn't satisfy your
+ requirements.
+
+
+ Objects Rule
+
+ The Main and Library rules call the Objects rule on source files.
+ Compiled object files built by
+ the Objects rule are a dependency of the obj
+ pseudotarget, so "jam obj" will build object files used in
+ Main and Library rules.
+
+ Target identifiers created by the Objects rule have grist
+ set to $(SOURCE_GRIST). So given this Jamfile:
+
+ SubDir TOP src lock ;
+ Main locker : lock.c ;
+
+ the object file created is lock.o (or lock.obj) and
+ its target identifier is <src!lock>lock.o
+ (or <src!lock>lock.obj).
+
+
+ You can also call Objects directly. For example:
+
+ Objects a.c b.c c.c ;
+
+ This compiles a.c into a.o, b.c into b.o, etc. The object
+ file suffix is supplied by the Objects rule.
+
+
+ Object Rule
+
+ Objects gets its work done by calling the Object rule on
+ each of the source files.
+ You could use the Object rule directly.
+ For example, on Unix, you could use:
+
+ Object foo.o : foo.c ;
+
+ However, the Object rule does not provide suffixes, and
+ it does not provide the grist needed to construct target
+ identifiers if you are using the SubDir* rules.
+ A portable and robust Jamfile would need to invoke Object thus:
+
+ Object <src!util>foo$(SUFOBJ) : <src!util>foo.c ;
+
+ which is inelegant and clearly shows why using Objects
+ is better than using Object.
+
+ If there's any advantage to the Object rule, it's
+ that it doesn't require that the object name bear
+ any relationship to the source. It is thus possible to
+ compile the same file into different objects. For example:
+
+
+ Object a.o : foo.c ;
+ Object b.o : foo.c ;
+ Object c.o : foo.c ;
+
+ This compiles foo.c (three times) into a.o, b.o, and c.o.
+ Later examples show how this is useful.
+
+ The Object rule looks at the suffix of the source file and
+ calls the appropriate rules to do the actual preprocessing
+ (if any) and compiling needed to produce the output object file.
+ The Object rule is
+ capable of the generating of an object file from any
+ type of source. For example:
+
+ Object grammar$(SUFOBJ) : grammar.y ;
+ Object scanner$(SUFOBJ) : scanner.l ;
+ Object fastf$(SUFOBJ) : fastf.f ;
+ Object util$(SUFOBJ) : util.c ;
+
+ An even more elegant way to get the same result is to let the
+ Objects rule call Object:
+
+ Objects grammar.y scanner.l fastf.f util.c ;
+
+
+ In addition to calling the compile rules, Object sets up
+ a bunch of variables specific to the source and target
+ files. (See Variables Used in Compiling, below.)
+
+
+ Cc, C++, Yacc, Lex, Fortran, As, etc. Rules
+
+
+ The Object rule calls compile rules specific to the suffix of
+ the source file. (You can see which suffixes are supported
+ by looking at the Object rule definition in Jambase.)
+ Because the extra work done by the
+ Object rule, it is not always useful to call the compile
+ rules directly. But the adventurous user might attempt
+ it. For example:
+
+ Yacc grammar.c : grammar.y ;
+ Lex scan.c : scan.l ;
+ Cc prog.o : prog.c ;
+
+ These examples individually run yacc(1), lex(1), and the C
+ compiler on their sources.
+
+
+ UserObject Rule
+
+ Any files with suffixes not understood by the Object rule
+ are passed to the UserObject rule. The default definition
+ of UserObject simply emits a warning that the suffix is
+ not understood. This Jambase rule definition is intended to be
+ overridden in Jamrules with one that recognizes the project-specific
+ source file suffixes. For example:
+
+
+ #In Jamrules:
+
+ rule UserObject
+ {
+ switch $(>)
+ {
+ case *.rc : ResourceCompiler $(<) : $(>) ;
+ case * : ECHO "unknown suffix on" $(>) ;
+ }
+ }
+
+ rule ResourceCompiler
+ {
+ DEPENDS $(<) : $(>) ;
+ Clean clean : $(<) ;
+ }
+
+ actions ResourceCompiler
+ {
+ rc /fo $(<) $(RCFLAGS) $(>)
+ }
+
+
+ #In Jamfile:
+
+ Library liblock : lockmgr.c ;
+ if $(NT) { Library liblock : lock.rc ; }
+
+
+ In this example, the UserObject definition in Jamrules
+ allows *.rc files to be handle as regular Main and Library
+ sources. The lock.rc file is compiled into lock.obj
+ by the "rc" command, and lock.obj is archived into a library
+ with other compiled objects.
+
+ LibraryFromObjects Rule
+
+ Sometimes the Library rule's straightforward compiling of
+ source into object modules to be archived isn't flexible
+ enough. The LibraryFromObjects rule does the archiving
+ (and deleting) job of the Library rule, but not the compiling.
+ The user can make use of the Objects or Object
+ rule for that. For example:
+
+ LibraryFromObjects libfoo.a : max.o min.o ;
+ Object max.o : maxmin.c ;
+ Object min.o : maxmin.c ;
+ ObjectCcFlags max.o : -DUSEMAX ;
+ ObjectCcFlags min.o : -DUSEMIN ;
+
+ This Unix-specific example compiles the same source file into
+ two different
+ objects, with different compile flags, and archives them.
+ (The ObjectCcFlags rule is described shortly.)
+ Unfortunately, the portable and robust implementation of the
+ above example is not as pleasant to read:
+
+ SubDir TOP foo bar ;
+ LibraryFromObjects libfoo$(SUFLIB) : <foo!bar>max$(SUFOBJ)
+ <foo!bar>min$(SUFOBJ) ;
+ Object <foo!bar>min$(SUFOBJ) : <foo!bar>maxmin.c ;
+ Object <foo!bar>max$(SUFOBJ) : <foo!bar>maxmin.c ;
+ ObjectCcFlags <foo!bar>min$(SUFOBJ) : -DUSEMIN ;
+ ObjectCcFlags <foo!bar>max$(SUFOBJ) : -DUSEMAX ;
+
+ Note that, among other things, you must supply the library
+ file suffix when using the LibraryFromObjects rule.
+
+
+ MainFromObjects Rule
+
+ Similar to LibraryFromObjects, MainFromObjects does the
+ linking part of the Main rule, but not the compiling.
+ MainFromObjects can be used when there are no
+ objects at all, and everything is to be loaded from
+ libraries. For example:
+
+ MainFromObjects testprog ;
+ LinkLibraries testprog : libprog ;
+ Library libprog : main.c util.c ;
+
+ On Unix, say, this generates a link command that looks like:
+
+ cc -o testprog libprog.a
+
+ Linking purely from libraries is something that doesn't
+ work everywhere: it depends on the symbol "main" being
+ undefined when the linker encounters the library that contains
+ the definition of "main".
+
+
+ Variables Used in Compiling
+
+ The following variables control the compiling of source
+ files:
+
+
+
+|
+ C++
+ | | The C++ compiler command
+ |
|
+ CC
+ | | The C compiler command
+ |
+ C++FLAGS
+
+ CCFLAGS
+ | | Compile flags, used to
+ create or update compiled objects
+ |
+ SUBDIRC++FLAGS
+
+ SUBDIRCCFLAGS
+ | | Additonal compile flags
+ for source files in this directory.
+ |
|
+ OPTIM
+ | | Compiler optimization flag. The Cc and C++
+ actions use this as well as C++FLAGS or CCFLAGS.
+ |
|
+ HDRS
+ | | Non-standard header directories; i.e.,
+ the directories the compiler will not look in
+ by default and which therefore must be supplied
+ to the compile command. These directories are
+ also used by jam to scan for include files.
+ |
|
+ STDHDRS
+ | | Standard header directories, i.e., the
+ directories the compiler searches automatically.
+ These are not passed to the compiler, but they
+ are used by jam to scan for include files.
+ |
|
+ SUBDIRHDRS
+ | | Additional paths to add to HDRS for source files
+ in this directory.
+ |
|
+ LEX
+ | | The lex(1) command
+ |
|
+ YACC
+ | | The yacc(1) command
+ |
+
+
+ The Cc rule sets a target-specific $(CCFLAGS) to the current
+ value of $(CCFLAGS) and $(SUBDIRCCFLAGS). Similarly
+ for the C++ rule. The Object rule sets a target-specific
+ $(HDRS) to the current value of $(HDRS) and $(SUBDDIRHDRS).
+
+
+ $(CC), $(C++), $(CCFLAGS), $(C++FLAGS), $(OPTIM), and
+ $(HDRS) all affect the compiling of C and C++ files.
+ $(OPTIM) is separate from $(CCFLAGS) and $(C++FLAGS) so
+ they can be set independently.
+
+ $(HDRS) lists the directories to search for header files,
+ and it is used in two ways: first, it is passed to the C
+ compiler (with the flag -I prepended); second, it is used
+ by HdrRule to locate the header files whose names were
+ found when scanning source files. $(STDHDRS) lists the
+ header directories that the C compiler already knows
+ about. It does not need passing to the C compiler, but is
+ used by HdrRule.
+
+ Note that these variables, if set as target-specific variables,
+ must be set on the target, not the source file.
+ The target file in this case is the object file to be generated.
+ For example:
+
+ Library libximage : xtiff.c xjpeg.c xgif.c ;
+
+ HDRS on xjpeg$(SUFOBJ) = /usr/local/src/jpeg ;
+ CCFLAGS on xtiff$(SUFOBJ) = -DHAVE_TIFF ;
+
+ This can be done more easily with the rules that follow.
+
+
+ ObjectCcFlags, ObjectC++Flags, ObjectHdrs Rules
+
+ $(CCFLAGS), $(C++FLAGS) and $(HDRS) can be set on object file
+ targets
+ directly, but there are rules that allow these variables
+ to be set by referring to the original source file name,
+ rather than to the derived object file name. ObjectCcFlags
+ adds object-specific flags to the $(CCFLAGS) variable,
+ ObjectC++Flags adds object-specific flags to the
+ $(C++FLAGS) variable, and ObjectHdrs add object-specific
+ directories to the $(HDRS) variable. For example:
+
+ #In Jamrules:
+ if $(NT) { CCFLAGS_X = /DXVERSION ;
+ HDRS_X = \\\\SPARKY\\X11\\INCLUDE\\X11 ;
+ }
+
+ #In Jamfile:
+ Main xviewer : viewer.c ;
+ ObjectCcFlags viewer.c : $(CCFLAGS_X) ;
+ ObjectHdrs viewer.c : $(HDRS_X) ;
+
+ The ObjectCcFlags and ObjectHdrs rules take .c files
+ as targets, but actually set $(CCFLAGS) and $(HDRS) values
+ on the .obj (or .o) files. As a result, the action
+ that updates the target .obj file uses the target-specific
+ values of $(CCFLAGS) and $(HDRS).
+
+
+ SubDirCcFlags, SubDirC++Flags, SubDirHdrs Rules
+
+ These rules set the values of $(SUBDIRCCFLAGS), $(SUBDIRC++FLAGS)
+ and $(SUBDIRHDRS), which are used by the Cc,
+ C++, and Object rules when setting the target-specific
+ values for $(CCFLAGS), $(C++FLAGS) and $(HDRS). The SubDir
+ rule clears these variables out, and thus they provide
+ directory-specific values of $(CCFLAGS), $(C++FLAGS) and
+ $(HDRS). For example:
+
+ #In Jamrules:
+ GZHDRS = $(TOP)/src/gz/include ;
+ GZFLAG = -DGZ ;
+
+ #In Jamfile:
+ SubDir TOP src gz utils ;
+
+ SubDirHdrs $(GZHDRS) ;
+ SubDirCcFlags $(GZFLAG) ;
+
+ Library libgz : gizmo.c ;
+ Main gizmo : main.c ;
+ LinkLibraries gizmo : libgz ;
+
+ All .c files in this directory files will be compiled with
+ $(GZFLAG) as well as the default $(CCFLAG), and the include
+ paths used on the compile command will be $(GZHDRS) as well
+ as the default $(HDRS).
+
+Header File Processing
+
+ One of the functions of the Object rule is set up
+ scanning of source
+ files for (C style) header file inclusions. To do so, it
+ sets the special variables $(HDRSCAN) and $(HDRRULE)
+ as target-specific variables on the source file. The
+ presence of these variables triggers a special mechanism
+ in jam for scanning a file for header file inclusions and
+ invoking a rule with the results of the scan. The
+ $(HDRSCAN) variable is set to an egrep(1) pattern that
+ matches "#include" statements in C source files, and the
+ $(HDRRULE) variable is set to the name of the rule that
+ gets invoked as such:
+
+ $(HDRRULE) source-file : included-files ;
+
+ This rule is supposed to set up the dependencies between
+ the source file and the included files. The Object rule
+ uses HdrRule to do the job. HdrRule itself expects
+ another variable, $(HDRSEARCH), to be set to the list of
+ directories where the included files can be found. Object
+ does this as well, setting $(HDRSEARCH) to $(HDRS) and
+ $(STDHDRS).
+
+ The header file scanning occurs during the "file binding"
+ phase of jam, which means that the target-specific
+ variables (for the source file) are in effect. To accomodate
+ nested includes, one of the HdrRule's jobs is to pass
+ the target-specific values of $(HDRRULE), $(HDRSCAN), and
+ $(HDRSEARCH) onto the included files, so that they will be
+ scanned as well.
+
+
+ HdrRule Rule
+
+ Normally, HdrRule is not invoked directly; the Object rule
+ (called by Main and Library) invokes it.
+
+ If there are special dependencies that need to be set,
+ and which are not set by HdrRule itself, you can define
+ another rule and let it invoke HdrRule. For example:
+
+
+ #In Jamrules:
+ rule BuiltHeaders
+ {
+ DEPENDS $(>) : mkhdr$(SUFEXE) ;
+ HdrRule $(<) : $(>) ;
+ }
+
+ #In Jamfile:
+ Main mkhdr : mkhdr.c ;
+ Main ugly : ugly.c ;
+
+ HDRRULE on ugly.c = BuiltHeaders ;
+
+
+ This example just says that the files included by "ugly.c"
+ are generated by the program "mkhdr", which can be built
+ from "mkhdr.c". During the binding phase, jam will
+ scan ugly.c, and if it finds an include file, ughdr.h,
+ for example, it will automatically invoke the rule:
+
+ BuiltHeaders ugly.c : ughdr.h ;
+
+ By calling HdrRule at the end of BuiltHeaders,
+ all the gadgetry of HdrRule takes effect and it
+ doesn't need to be duplicated.
+
+
+ Variables Used for Header Scanning
+
+
+
+|
+ HDRPATTERN
+ | | Default scan pattern for "include" lines.
+ |
|
+ HDRSCAN
+ | | Scan pattern to use.
+ This is a special variable: during binding, if
+ both HDRSCAN and HDRRULE are set, scanning is activated
+ on the target being bound.
+ The HdrRule and Object rules sets this
+ to $(HDRPATTERN) on their source targets.
+ |
|
+ HDRRULE
+ | | Name of rule to invoked on files found in header
+ scan. The HdrRule and Object rules set this to "HdrRule"
+ on their source targets. This is also a special variable;
+ it's the only jam variable that can hold the
+ name of a rule to be invoked.
+ |
|
+ HDRSEARCH
+ | | Search paths for files found during header scanning.
+ This is set from $(HDRS) and $(STDHDRS), which are
+ described in the Compiling section.
+ jam will search $(HDRSEARCH) directories for
+ the files found by header scans.
+ |
+
+
+ The Object rule sets HDRRULE and HDRSCAN specifically for
+ the source files to be scanned, rather than globally. If
+ they were set globally, jam would attempt to scan all
+ files, even library archives and executables, for header
+ file inclusions. That would be slow and probably not
+ yield desirable results.
+
+
+Copying Files
+
+
+ File Rule
+
+ The File rule copies one file to another. The target name
+ needn't be the same as the source name. For
+ example:
+
+ switch $(OS)
+ {
+ case NT* : File config.h : confignt.h ;
+ case * : File config.h : configunix.h ;
+ }
+ LOCATE on config.h = $(LOCATE_SOURCE) ;
+
+ This creates a config.h file from either confignt.h or
+ configunix.h, depending on the current build platform.
+
+ The File rule does not
+ use the LOCATE_SOURCE variable set by the
+ SubDir rule (although it does use SEARCH_SOURCE), which
+ means you have to set the copied file's output directory
+ yourself. That's done by setting the special
+ LOCATE variable on the target, as shown above,
+ or with the MakeLocate rule described below.
+
+ Bulk Rule
+
+ The Bulk rule is a shorthand for many invocations of the
+ File rule when all files are going to the same directory.
+ For example:
+
+ #In Jamrules:
+ DISTRIB_GROB = d:\\distrib\\grob ;
+
+ #In Jamfile:
+ Bulk $(DISTRIB_GROB) : grobvals.txt grobvars.txt ;
+
+ This causes gobvals.txt and grobvars.txt to be copied
+ into the $(DISTRIB_GROB) directory.
+
+ HardLink Rule
+
+ The Unix-only HardLink rule makes a hard link (using ln(1)) from the
+ source to the target, if there isn't one already. For
+ example:
+
+ HardLink config.h : configunix.h ;
+
+
+ Shell Rule
+
+ The Shell rule is like the File rule, except that on Unix it makes
+ sure the first line of the target is "#!/bin/sh" and sets
+ the permission to make the file executable. For example:
+
+ Shell /usr/local/bin/add : add.sh ;
+
+
+ You can also use $(SHELLHEADER) to dictate
+ what the first line of the copied file will be.
+ For
+ example:
+
+ Shell /usr/local/bin/add : add.awk ;
+ SHELLHEADER on /usr/local/bin/add = "#!/bin/awk -f" ;
+
+ This installs an awk(1) script.
+
+
+ Variables Used When Copying Files
+
+
+
+|
+ FILEMODE
+ | | Default file permissions for copied files
+ |
|
+ SHELLMODE
+ | | Default file permissions for Shell rule targets
+ |
|
+ MODE
+ | | File permissions set on files copied by
+ File, Bulk, and Shell rules.
+ File and Shell sets a target-specific MODE to the current
+ value of $(FILEMODE) or $(SHELLMODE), respectively.
+ |
|
+ SHELLHEADER
+ | | String to write in first line of Shell targets
+ (default is #!/bin/sh).
+
+ |
+
+
+
+
+Installing Files
+
+Jambase provides a set of Install* rules to copy files
+into an destination directory and set permissions on them.
+On Unix, the install(1) program is used.
+If the destination directory does not exist, jam
+creates it first.
+
+All files copied with the Install* rules are dependencies
+of the install pseudotarget, which means that the
+command "jam install" will cause the installed copies to
+be updated. Also, "jam uninstall" will cause the installed
+copies to be removed.
+
+The Install* rules are:
+
+
+| InstallBin
+ | Copies file and sets its permission to $(EXEMODE).
+ You must specify the suffixed executable name. E.g.:
+ InstallBin $(BINDIR) : thing$(SUFEXE) ;
+
+
+ |
| InstallFile
+ | Copies file and sets its permission to $(FILEMODE). E.g.:
+ InstallFile $(DESTDIR) : readme.txt ;
+
+
+ |
| InstallLib
+ | Copies file and sets its permission to $(FILEMODE).
+ You must specify the suffixed library name. E.g.:
+ InstallLib $(LIBDIR) : libzoo$(SUFLIB) ;
+
+
+ |
| InstallMan
+ | Copies file into the mann
+ subdirectory of the target directory
+ and sets its permission to $(FILEMODE). E.g.,
+ this copies foo.5 into the $(DESTDIR)/man5 directory:
+ InstallMan $(DESTDIR) : foo.5 ;
+
+
+ |
| InstallShell
+ | Copies file and sets its permission to $(SHELLMODE). E.g.:
+ InstallShell $(DESTDIR) : startup ;
+
+
+ |
+
+
+
+
+ Variables
+
+ The following variables control the installation rules:
+
+
+
+|
+ INSTALL
+ | | The install program (Unix only)
+ |
|
+ FILEMODE
+ | | Default file permissions on readable files.
+ |
|
+ EXEMODE
+ | | Default file permission executable files.
+ |
|
+ SHELLMODE
+ | | Default file permission on shell script files.
+ |
|
+ MODE
+ | | Target-specific file permissions
+ |
+
+
+
+ The Install rules set a target-specific MODE to the current
+ value of $(FILEMODE), $(EXEMODE), or $(SHELLMODE),
+ depending on which Install rule was invoked.
+
+ The directory variables are just defined for convenience:
+ they must be passed as the target to the appropriate
+ Install rule. The $(INSTALL) and mode variables must be
+ set (globally) before calling the Install rules in order
+ to take effect.
+
+
+Miscellaneous Rules
+
+
+Clean Rule
+
+
+The Clean rule defines files to be removed when you run "jam clean".
+Any site-specific build rules defined in your Jamrules should invoke
+Clean so that outputs can be removed. E.g.,
+
+ rule ResourceCompiler
+ {
+ DEPENDS $(<) : $(>) ;
+ Clean clean : $(<) ;
+ }
+
+
+
+Most Jambase rules invoke the Clean rule on their built targets,
+so "jam clean" will remove all compiled objects, libraries,
+executables, etc.
+
+
+MakeLocate Rule
+
+ MakeLocate is a single convenient rule that creates a directory,
+ sets LOCATE on a target to that directory, and makes the directory
+ a dependency of the target. It is used by many Jambase rules,
+ and can be invoked directly, e.g.:
+
+ GenFile data.tbl : hxtract data.h ;
+ MakeLocate data.tbl : $(TABLEDIR) ;
+
+ In this example, the File rule creates data.tbl from data.h.
+ The MakeLocate causes data.tbl to be written into the $(TABLEDIR)
+ directory; and if the directory doesn't exist, it is created first.
+
+ The MakeLocate rule invokes another Jambase rule, MkDir,
+ to (recursively) create
+ directories. MkDir uses the $(MKDIR) variable to determine the
+ platform-specific command that creates directories.
+
+
+RmTemps Rule
+
+ Some intermediate files are meant to be temporary.
+ The RmTemps rule can be used to cause
+ jam to delete them after they are used.
+
+ RmTemps must be:
+
+ -
+ the last rule
+ invoked on the permanent file that uses
+ the temporary file(s)
+
-
+ invoked with the permanent file as the output
+ target and the temporary file(s) as the input target
+
-
+ invoked with the exact target identifiers of
+ the permanent file and the temporary file(s)
+
+ For
+ example:
+
+ SubDir TOP src big ;
+ GenFile big.y : joinfiles part1.y part2.y part3.y ;
+ Main bigworld : main.c big.y ;
+ RmTemps bigworld$(SUFEXE) : <src!big>big.y ;
+
+ This causes big.y to be deleted after it has been used to create
+ the bigworld executable.
+ The exact target identifier of big.y is <src!big>big.y
+ (the GenFile and Main rules tack on the grist automatically);
+ the exact target identifier of the bigworld executable
+ is bigworld$(SUFEXE).
+
+
+Back to top.
+
+ Copyright 1997, 2000 Perforce Software, Inc.
+
+ Comments to info@perforce.com
+
+ Last updated: Dec 31, 2000
+
+ $Id: //public/jam/src/Jamfile.html#5 $
+
+
diff --git a/src/tools/jam/Makefile b/src/tools/jam/Makefile
new file mode 100644
index 0000000000..ec8b3caf8a
--- /dev/null
+++ b/src/tools/jam/Makefile
@@ -0,0 +1,71 @@
+# Makefile for jam
+
+CC = cc
+TARGET = -o jam0
+CFLAGS =
+
+# Special flavors - uncomment appropriate lines
+
+# NCR seems to have a broken readdir() -- use gnu
+#CC = gcc
+
+# AIX needs -lbsd, and has no identifying cpp symbol
+# Use _AIX41 if you're not on 3.2 anymore.
+#LINKLIBS = -lbsd
+#CFLAGS = -D_AIX
+
+# NT (with Microsoft compiler)
+# Use FATFS if building on a DOS FAT file system
+#Lib = $(MSVCNT)/lib
+#Include = $(MSVCNT)/include
+#CC = cl /nologo
+#CFLAGS = -I $(Include) -DNT
+#TARGET = /Fejam0
+#LINKLIBS = $(Lib)/oldnames.lib $(Lib)/kernel32.lib $(Lib)/libc.lib
+
+# NT (with Microsoft compiler)
+# People with DevStudio settings already in shell environment.
+#CC = cl /nologo
+#CFLAGS = -DNT
+#TARGET = /Fejam0
+
+# BeOS - Metroworks CodeWarrior
+#CC = mwcc
+#Include = /NewDisk/develop/headers/posix
+#CFLAGS = -I $(Include)
+
+# BeOS - gcc
+#CC = gcc
+#LINKLIBS = -lnet
+
+# Interix - gcc
+#CC = gcc
+
+# Cygwin - gcc & cygwin
+#CC = gcc
+#CFLAGS = -D__cygwin__
+
+# MingW32
+#CC = gcc
+#CFLAGS = -DMINGW
+
+# MPEIX
+#CC = gcc
+#CFLAGS = -I/usr/include -D_POSIX_SOURCE
+
+# QNX rtp (neutrino)
+#CC = gcc
+
+SOURCES = \
+ builtins.c \
+ command.c compile.c execunix.c execvms.c expand.c \
+ filent.c fileos2.c fileunix.c filevms.c glob.c hash.c \
+ headers.c jam.c jambase.c jamgram.c lists.c make.c make1.c \
+ newstr.c option.c parse.c pathunix.c pathvms.c regexp.c \
+ rules.c scan.c search.c timestamp.c variable.c
+
+all: jam0
+ jam0
+
+jam0:
+ $(CC) $(TARGET) $(CFLAGS) $(SOURCES) $(LINKLIBS)
diff --git a/src/tools/jam/Porting b/src/tools/jam/Porting
new file mode 100644
index 0000000000..7505565720
--- /dev/null
+++ b/src/tools/jam/Porting
@@ -0,0 +1,68 @@
+Notes on porting Jam - revised 12/31/2000
+
+1) Working out system dependencies in the Jam code.
+
+ Jam's OS footprint is fairly small. For OS independent work Jam
+ liberally uses standard libc functions like stdio, malloc, and
+ string. The OS dependent interfaces are:
+
+ From filesys.h:
+
+ file_parse() - split a file name into dir/base/suffix/member
+ file_build() - build a filename given dir/base/suffix/member
+ file_dirscan() - scan a directory for files
+ file_archscan() - scan an archive for files
+ file_time() - get the timestamp of a file, if not already
+ done by file_dirscan().
+
+ From execcmd.h:
+
+ execcmd() - execute a shell script
+ execwait() - wait for any outstanding execcmd()'s.
+
+ The current implementations are:
+
+ filemac.c - mac MPW
+ filent.c - NT
+ fileos2.c - OS/2
+ fileunix.c - all UNIX
+ filevms.c - VMS
+
+ execmac.c - mac MPW
+ execunix.c - UNIX, OS/2, NT
+ execvms.c - VMS
+
+2) Defining OSMAJOR, OSMINOR in jam.h
+
+ So that the Jambase and Jamfile know their host, Jam defines $(OS)
+ to be something useful for each platform. Make sure that there is
+ code in jam.h to generate a useful value for $(OS), and key it off
+ the platform specific C-preprocessor symbol. If the C-preprocessor
+ doesn't itself defines such a symbol, add a define to the Makefile.
+
+ In addition to $(OS), you can also set $(OSPLAT) if the OS runs on
+ multiple platforms (like Linux or NT).
+
+3) Working out system dependencies in the Jambase
+
+ With the value of $(OS) available, the Jambase can be extended to
+ support special variables or rules for new platforms. See the
+ current support for VMS, NT, and Mac.
+
+4) Yacc troubles
+
+ The generated files jamgram.h and jamgram.c are distributed for the
+ poor souls without yacc.
+
+5) Known problematic systems:
+
+ - Pyramid has no malloc.h, memory.h
+
+ - Encore has no stdlib.h
+
+ - Bull DPX has sys/file.h problems
+
+6) Send the results back.
+
+ If you do porting work, the result can be integrated into future
+ releases if you send it back to the author's address in the README.
diff --git a/src/tools/jam/README b/src/tools/jam/README
new file mode 100644
index 0000000000..97777f09fd
--- /dev/null
+++ b/src/tools/jam/README
@@ -0,0 +1,149 @@
+Jam/MR (aka "jam - make(1) redux")
+
+ /+\
+ +\ 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.
+
+FEATURES
+
+ -> Jam is a make(1) replacement that makes building simple things
+ simple and building complicated things manageable.
+
+ -> Jam's language is expressive, making Jamfiles (c.f. Makefiles)
+ compact. Here's a sample:
+
+ Main smail : main.c map.c resolve.c deliver.c
+ misc.c parser.y alias.c pw.c headers.c
+ scanner.l getpath.c str.c ;
+
+ This builds "smail" from a dozen source files. Jam handles
+ header file dependencies automatically and on-the-fly.
+
+ -> Jam is very portable: it runs on UNIX, VMS, Mac, and NT.
+ Most Jamfiles themselves are portable, like the sample above.
+
+ -> Jam is unintrusive: it is small, it has negligible CPU
+ overhead, and it doesn't create any of its own funny files
+ (c.f. Odin, nmake, SunOS make).
+
+ -> Jam can build large projects spread across many directories
+ in one pass, without recursing, tracking the relationships
+ among all files. Jam can do this with multiple, concurrent
+ processes.
+
+ -> Jam isn't under the blinkin GNU copyright, so you can
+ incorporate it into commercial products.
+
+
+INFORMATION GUIDE
+
+ Jam.html jam and language reference.
+
+ Jambase.html Reference for the Jambase boilerplate file.
+
+ Jamfile.html Easy reading on creating a Jamfile and using jam.
+
+ RELNOTES Release 2.3 release notes.
+
+ Porting Notes on porting jam to wildcat platforms.
+
+ README This file. Includes installation instructions.
+
+ jam.c Contains the jam command's main() as well as an
+ introduction to the code, for serious hackers.
+
+
+INSTALLING
+
+ The Makefile (UNIX, NT), build.com (VMS), Build.mpw (Mac MPW) are
+ for bootstrapping. Once jam is built, it can rebuild itself.
+
+ UNIX
+
+ Build jam with make(1) on:
+
+ Platform $(OS)
+ -------------------------
+ AIX AIX *
+ BSD/386 1.0 BSDI
+ COHERENT/386 COHERENT
+ DGUX 5.4 DGUX
+ FreeBSD FREEBSD
+ HPUX 9.0 HPUX
+ IRIX 5.0 IRIX
+ Linux LINUX
+ NEXTSTEP 3.2 NEXT
+ OSF/1 OSF
+ PTX V2.1.0 PTX
+ Solaris 2 SOLARIS *
+ SunOS4.1 SUNOS
+ Ultrix 4.2 ULTRIX
+ BeOS BEOS *
+
+ * requires editing Makefile
+
+ Windows
+
+ Build jam with nmake on:
+
+ Platform $(OS)
+ -------------------------
+ NT NT *
+ OS/2 OS2 *
+
+ The NT MAXLINE (command line length) is still set in jam.h to
+ 996, which was apparently the NT 3.5 limit. On 4.0, the limit
+ is somewhere around 10K. For now, you can increase MAXLINE in
+ jam.h so that a jam running on 4.0 will use the full command
+ line length, but that jam.exe will fail miserably on the older OS.
+
+ On NT, a variable must be set before invoking jam to tell
+ it where the C compiler lives. The name of this variable
+ depends on which compiler you are using:
+
+ BCCROOT: The Borland C compiler
+ MSVCNT: The Microsoft Compiler 5.0 (for NT)
+ MSVC: The Microsoft Compiler 1.5 (for Windows)
+
+ Only MSVCNT has really been tested and is known to work.
+
+ Macintosh
+
+ Build jam with Build.mpw on:
+
+ Platform $(OS)
+ -------------------------
+ Macintosh MAC
+
+ You'll need to edit Build.mpw to set CW.
+
+ VMS
+
+ Build jam with @build.com on:
+
+ Platform $(OS)
+ -------------------------
+ VMS 5.4 VMS
+ OPENVMS OPENVMS
+
+Comments to the author!
+
+November, 1993 - release 1.0
+March, 1995 - release 2.0
+February, 1996 - release 2.1
+November, 1997 - release 2.2
+December, 2000 - release 2.3
+March, 2002 - release 2.4
+
+
+Christopher Seiwald
+
+seiwald@perforce.com
diff --git a/src/tools/jam/RELNOTES b/src/tools/jam/RELNOTES
new file mode 100644
index 0000000000..c5743caab8
--- /dev/null
+++ b/src/tools/jam/RELNOTES
@@ -0,0 +1,924 @@
+Release notes for Jam/MR 2.4
+(aka Jam - make(1) redux)
+
+1. Release info:
+
+ Jam/MR 2.4
+ March, 21, 2002
+ VERSION 2.4
+
+2. Compatibility
+
+ Jam 2.4 is upward compatible with Jam 2.3
+
+ The Jam 2.4 language is a superset of the 2.3 language;
+ Jamfiles, Jambase, and other rulesets used in 2.3 can be used
+ with the 2.4 language support.
+
+3. Changes since 2.3.
+
+3.1. Changes to Jam Language
+
+ The mechanism for calling rules that return values - "[ rule
+ args ...]", (and 'return' in the rule body), is now a
+ documented part of the language.
+
+ Add "on ..." syntax, to invoke a
+ rule under the influence of a target's specific variables.
+
+ Add "[ on targ rule ... ]" to call a rule returning a value,
+ under the influence of a target's specific variables.
+
+ New 'Glob' builtin that returns a list of files, given a list
+ of directories, and a list of filename patterns.
+
+ New 'while expr { block }' construct.
+
+ New :E=value modifier provides default value if variable unset.
+
+ New :J=joinval modifier concatenates list elements into single
+ element, separated by joinval.
+
+ \ can now be used to escape a space (or any single whitespace
+ character), so that you don't have to resort to quotes.
+
+ New 'Match regexp : string' rule matches regexp against string
+ and returns list of results.
+
+ Rules can now be invoked indirectly, through variable names.
+ If the variable expands to an empty list, no rule is run.
+ If the variable expands to multiple entries, each rule is
+ run with the same arguments. The result of the rule invocation
+ is the concatenation of the results of the rules invoked.
+
+ 'Echo' and 'Exit' now have aliases 'echo' and 'exit', since it
+ is really hard to tell that these are built-in rules and not
+ part of the language, like 'include'. Real rules continue to
+ start with a capital.
+
+3.2. Jambase Changes
+
+ Support for YACCGEN, the suffix used on generated yacc output.
+
+ Fix ups to have jam and p4 build with borland C 5.5,
+ and minor win98 jam support for jam clean
+
+ SubDirHdrs now takes directory names in the same format as
+ SubInclude : one directory element per word.
+
+ More portable support for specifying includes and #defines:
+ New ASHDRS, CCHDRS, CCDEFS, DEFINES, ObjectDefines, FQuote,
+ FIncludes, FDefines. Ordering of cc and c++ flags grossly
+ rearranged.
+
+ Jambase has been compacted by applying the new E: and J:
+ expansion modifiers.
+
+ New SoftLink rule, courtesy of David Lindes. It currently
+ assumes you can pass a -s flag to $(LN).
+
+3.3 'jam' Changes (See Jam.html)
+
+ Added '-q' (quit quick) option; jam will exit promptly (as if it
+ received an interrupt), as soon as any target fails.
+
+ Added experimental '-g' (build newest sources first) option:
+ all things being equal, normally targets are simply built in
+ the order they appear in the Jamfiles. With this flag, targets
+ with the newest sources are built first. From an idea by Arnt
+ Gulbrandsen. Undocumented (outside this note).
+
+3.4. Jam internal code changes
+
+ jamgram.yy now defines YYMAXDEPTH to 10000, what it is on
+ FreeBSD, for older yaccs that left it at 150 or so. This is
+ needed for the right-recursion now used in the grammar.
+
+ Optimize rule compilation, with right-recursion instead of left.
+
+ Split jam's built-in rules out to builtins.c from compile.c,
+ so that compile.c only deals with the language.
+
+ Split jam's pathsys.h from filesys.h, since they are really
+ two different pieces.
+
+ evaluate_if(), which evaluated the condition tree for 'if' and
+ returned an int, has been replaced with compile_eval(), which does
+ essentially the same but returns a LIST.
+
+4. Fixed bugs
+
+ Missing TEMPORARY targets with multiple parents no longer spoil one
+ parent's time with another. The parents' time is used for comparison
+ with dependents, but no longer taken on as the target's own time.
+
+ 'actions updated', not 'actions together', now protects targets
+ from being deleted on failed/interrupted updates.
+
+ Fixed broken $(v[1-]), which always returned an empty expansion.
+ Thanks to Ian Godin .
+
+ Defining a rule within another rule, and invoking the enclosing
+ rule more than once, would result in giving the first rule a
+ null definition. Fixed.
+
+ $(d:P) now works properly on the mac, climbing up directories.
+ Thanks to Miklos Fazekas .
+
+ No longer (sometimes) treat \ as a directory separator on
+ UNIX. It isn't supposed to be, but was due to bungled ifdefs.
+
+ Applying just :U or :D (or :E, :J) mods no longer causes the
+ variable value to be treated as a filename (parsed and rebuilt
+ using the OS specific pathsys routines). Previously, if _any_
+ mods were present then the value was parsed and rebuilt as if
+ a filename, and that could in certain cases munge the value.
+ Only the file modifiers (:GDBSM) treat the value as a
+ filename.
+
+ Four rules makeCommon, makeGrist, makeString, makeSubDir from
+ jam 2.2 missing in 2.3 have been re-added, with apologies to
+ dtb@cisco.com.
+
+ Return status more likely to be correct when using -d0, now that
+ targets are could as being built even with no debugging output.
+ Thanks to Miklos Fazekas .
+
+ yyacc now suffixes all terminals it defines with _t, so that they
+ don't conflict with other symbols (like RULE with the typedef
+ in rules.h). Thanks to Michael Allard.
+
+ InstallInto now handles multiple sources properly, rather than
+ acting as if each installed target depended on all sources to
+ be installed. $(INSTALLGRIST) is now the default grist for
+ installed targets, rather than the hardcoded 'installed'. Thanks
+ to Stephen Goodson.
+
+5. Porting
+
+ [MACINTOSH] Paths are now downshifted (internally) so as to
+ handle its case insensitivity. Thanks to Miklos Fazekas
+ .
+
+ [NT] MS changed the macro for the IA64 Windows NT 64bit
+ compiler.
+
+ [CYGWIN] Cygwin jam porting: dance around bison and yyacc.
+ Use bison's -y flag to use yacc's output file naming
+ conventions, and don't use yyacc on systems whose SUFEXE is
+ set.
+
+ [VMS] The Jambase itself was not formatting the CCHDRS and
+ CCDEFS properly: on VMS they can't be appended to, because
+ multiple /define or /include directives don't work. Instead
+ now CCHDRS and CCDEFS is reformatted from HDRS and DEFINES
+ anytime those latter two change. This requires the recent
+ change to jam to allow access to target-specific variables
+ when setting other variables.
+
+ [VMS] Remove exception call when file_dirscan() can't, for
+ some reason, scan a directory. Use a better set of #ifdefs to
+ determine if we're on a vax, rather than relying on the C
+ compiler being a specific version: we're able to build with
+ the C++ compiler now.
+
+ [VMS] Port new jam to run with just cxx compiler.
+ (The C compiler being a extra-cost item).
+
+ [NT] Add entry for DevStudio when the settings are already in the
+ system environment.
+
+ [NT] default $(MV) to "move /y" in Jambase.
+
+ [MINGW] Mingw port by Max Blagai.
+
+===============================================================================
+===============================================================================
+
+
+Release notes for Jam/MR 2.3
+(aka Jam - make(1) redux)
+
+0. Bugs fixed since 2.3.1
+
+ PATCHLEVEL 2 - 3/12/2001
+
+ NOCARE changed back: it once again does not applies to targets
+ with sources and/or actions. In 2.3 it was changed to apply to
+ such targets, but that broke header file builds: files that are
+ #included get marked with NOCARE, but if they have source or
+ actions, they still should get built.
+
+1. Release info:
+
+ Jam/MR 2.3
+ November 16, 2000
+ VERSION 2.3
+ PATCHLEVEL 1
+
+2. Compatibility
+
+ Jam 2.3 is upward compatible with Jam 2.2.
+
+ The Jam 2.3 language is a superset of the 2.2 language;
+ Jamfiles, Jambase, and other rulesets used in 2.2 can be used
+ with the 2.3 language support.
+
+3. Changes since 2.2
+
+3.1. Changes to Jam Language
+
+ Rules now can have values, which can expanded into a list with
+ the new "[ rule args ... ]" syntax. A rule's value is the value
+ of its last statement, though only the following statements have
+ values: if (value of the leg chosen), switch (ditto), set (value
+ of the resulting variable), return (its arguments). Note that
+ 'return' doesn't actually return. This support is EXPERIEMENTAL
+ and otherwise undocumented. (2.3.1)
+
+ Because of the new way lists are processed, if a rule has no
+ targets a warning message is no longer issued.
+
+ NOCARE now applies to targets with sources and/or actions,
+ rather than just those without.
+
+3.2. Jambase Changes
+
+ The HDRPATTERN variable now allows for leading blanks before
+ the #include, to keep up with ANSI. By john@nanaon-sha.co.jp
+ (John Belmonte) (2.2.3).
+
+ HDRPATTERN has been adjusted to avoid mistaking cases like:
+
+ # include /* could be */
+
+ MkDir now NOUPDATE's $(DOT), so that there are no dependencies
+ on the current directory's timestamp. By john@nanaon-sha.co.jp
+ (John Belmonte).
+
+ The old mock functions like makeDirName, which assigned their
+ results to the variable named as their first argument, have
+ been replaced with real functions using the new [] synxtax.
+ E.g. "makeDirName foo : bar ola" is now "foo = [ fDirName bar ]"
+
+ Install now always does a cp/chmod/etc, rather than using
+ the system's install(1), which invariably seems broken.
+
+3.3. Jam internal code changes
+
+ $JAMUNAME is set on UNIX. (2.2.4).
+
+ Jam ANSI-fied (2.3.0).
+
+ jam.h now defines a bunch of symbols used by the other source
+ files, so as minimize compiler- and platform-specific ifdefs.
+
+ OSVER is no longer set by jam.h (it was only set for AIX).
+ Jam does not depend on this variable at all, except to set
+ $(OSFULL), which is used to determine jam's build directory.
+ If the user needs to distinguish between various revs of
+ OSs, he must set OSVER in the environment.
+
+4. Fixed bugs
+
+ Redefining a rule while it was executing could cause jam to
+ crash. Reference counts are now used to prevent that, thanks
+ to Matt Armstrong.
+
+ Logic for computing chunk size when executing PIECEMEAL rules
+ has been reworked to be a little more accurate, without danger
+ of overflow, at the cost of being a little more compute intensive.
+ Instead of computing an estimate chunksize in the (now gone)
+ make1chunk(), make1cmds() now just goes full bore and tries to
+ use all args. When that fails, it backs off by 10% of the source
+ args until the command fits. It takes a little bit more compute
+ time compared to the old logic, but when you're executing actions
+ to build all of Shinola it's still pretty small in the scheme
+ of things.
+
+ The NT handle leak in execunix.c has been fixed, thanks to
+ Gurusamy Sarathy. (2.2.1).
+
+5. Porting
+
+ Platforms newly supported or updated:
+
+ AmigaOS (with gcc), courtesy of Alain Penders (2.2.2).
+
+ Beos
+
+ CYGWIN 1.1.4, courtesy of John Belmonte .
+
+ IBM AS400 via Visual Age on NT (primitive)
+
+ IBM OS/390 Unix System Services
+
+ Linux SuSE on OS390
+
+ Linux Mips, ARM
+
+ Lynx
+
+ HPUX 11, IA64
+
+ Mac OS X Server, courtesy of Jeff_Sickel@sickel.com (2.2.5).
+
+ Mac Rhapsody
+
+ MPE IX 6.0
+
+ NetBSD
+
+ QNX RTP (QNX 6.0)
+
+ Siemens Sinix
+
+ UNICOS
+
+ VMS 6.2, 7.1
+
+ Windows NT IA64
+
+5.1. NT Porting Notes
+
+ Always create tmp .bat file for actions if JAMSHELL is set.
+ That way, if JAMSHELL is a .bat file itself, it can handle
+ single-command actions with more than 9 cmd line args.
+
+ COMSPEC is no longer examined: cmd.exe is always used
+ instead. Only cmd.exe can execute the Jambase rules anyhow.
+
+ Jam can be built with Borland C++ 5.5.
+
+ OS2 fixes: InstallBin now works. Filenames are now downshifted,
+ so mixed case works better there, too. file_dirscan() can now scan
+ the root ("c:\" or "\") directory, which it couldn't handle before.
+
+ var_defines now ignores OS=Windows_NT, because it conflicts
+ with Jam's setting of OS (to NT).
+
+5.2. Mac OS 8/9 Notes
+
+ The support for Mac is curious at best. It runs under MPW.
+
+ It requires CodeWarrior Pro 5, but no longer requires GUSI.
+
+ Use Build.mpw to bootstrap the build.
+
+ The Mac specific definitions in the Jambase are not intended
+ to be of general purpose, but are sufficient to have Jam build
+ itself.
+
+===============================================================================
+===============================================================================
+
+
+Release Notes for Jam 2.2
+
+1. Release info:
+
+ Jam 2.2
+ October 22, 1997
+ VERSION 2.2
+ PATCHLEVEL 1
+
+2. Compatibility
+
+ Jam 2.2 is a roll-up of 'Jam - make(1) redux' release 2.1+.
+ Most of the changes described below were available before this,
+ in the jam.2.1.plus.tar ball.
+
+ The Jam 2.2 language is a superset of the 2.1 language;
+ Jamfiles, Jambase, and other rulesets used in 2.1 can be used
+ with the 2.2 language support.
+
+ See 'Jambase Changes', below, to see if your Jamfiles need any
+ changes to work with the 2.2 Jambase.
+
+
+3. Changes Since 2.1
+
+ New product name: Jam. (Executable program is still named 'jam'.)
+
+ Documentation rewritten; HTML versions supplied.
+
+
+3.1 Changes to Jam Language
+
+ Rules may now have more fields than just $(<) and $(>).
+
+ Local variables are now supported.
+
+ The expression 'if $(A) in $(B)' is now supported.
+
+ New variable modifiers :U and :L result in uppercased or lowercased
+ values.
+
+ New variable modifier :P reliably results in parent directory
+ of either a file or directory. (Previously, :D was used, but on VMS
+ :D of a directory name is just the directory name.)
+
+ The :S variable modifier now results in the _last_ suffix if a
+ filename has more than one dot (.) in it.
+
+ New predefined $(JAMDATE) variable is initialized at runtime for
+ simple date stamping.
+
+ New predefined variables $(OSVER) and $(OSPLAT) are used to
+ distinguish among operating system versions and hardware platforms,
+ when possible.
+
+ New 'bind' qualifier on action definitions allows variables
+ other than $(<) and $(>) to be bound with SEARCH and LOCATE paths.
+
+ Action buffer size is no longer limited by MAXCMD. Instead, each
+ line in an action is limited by MAXLINE, defined for each OS, and
+ the entire action size is limited by CMDBUF.
+
+
+3.2 Jambase Changes (See Jamfile.html)
+
+ Jambase has been reworked to incorporate new language features.
+
+ A handful of new utility rules has been added: makeString,
+ makeDirName, etc.
+
+ New HDRGRIST variable in Jambase allows for headers with the same
+ name to be distinguished.
+
+ LOCATE_TARGET now has a new flavor, LOCATE_SOURCE, that is used by
+ rules that generate source files (e.g., Yacc and Lex).
+
+ Header file includes now happen in the proper order. The limit of
+ 10 include files has been eliminated.
+
+ The old "Install" rule is no longer available. Use InstallBin,
+ InstallFile, InstallLib, InstallMan, or InstallShell instead.
+
+
+3.3 'jam' Changes (See Jam.html)
+
+ 'jam' can now be built as a stand-alone program, with Jambase
+ compiled into the executable. An external or alternate Jambase can
+ still be referenced explicitly with -f.
+
+ On command failure, 'jam' now emits the text of the command that
+ failed. This is a compromise between the normal -d1 behavior (where
+ commands were never seen) and -d2 (where commands are always seen).
+
+ 'jam' now exits non-zero if it doesn't have a total success. A parse
+ error, sources that can't be found, and targets that can't be built
+ all generate non-zero exit status.
+
+ The debugging levels (-d flags) have been slightly redefined.
+
+ The supplied Jamfile now builds 'jam' into a platform specific
+ subdirectory. This lets you use the same source directory to
+ build 'jam' for more than one platform.
+
+ The supplied Jamfile does not rebuild generated source files by
+ default. (They are supplied with the distribution.) See Jamfile
+ for more information.
+
+
+4. Fixed Bugs
+
+ The 'include' bug has finally been fixed, so that include
+ statements take effect exactly when they are executed,
+ rather than after the current statement block. This also
+ corrects the problem where an 'include' within an 'if'
+ block would wind up including the file one token after the
+ 'if' block's closing brace. Credit goes to Thomas Woods
+ for suggesting that the parse tree generation and parse
+ tree execution be paired in their own loop, rather than
+ having the parser execute the tree directly.
+
+ The setting and extracting of grist has been regularized:
+ normally, if you set a component of a filename (using the
+ :DBSMG= modifiers), you are supposed to include the delimiters
+ that set off the component: that is, you say "$(x:S=.suffix)",
+ including the ".". But with grist it was inconsistent
+ between setting and getting: setting grist required no
+ <>'s, while getting grist included them. Getting grist
+ continues to return the <>'s, but now setting grist can
+ either include them (the new way) or not (the old way).
+
+ 'actions together' now suppresses duplicate sources from
+ showing up in $(>).
+
+ Accessing variables whose names contained ['s (as happens with
+ MkDir on VMS) wasn't working, because it treated the [ as an
+ array subscript. Now [ and ] are, like :, handled specially so
+ that they can appear in variable values.
+
+ The 'if' statement now compares all elements in expressions;
+ previously, it only compared the first element of each list.
+
+ If a command line in an action is longer than MAXLINE (formerly
+ MAXCMD), 'jam' now issues an error and exits rather than dumping
+ core.
+
+ If a Jamfile ended without a trailing newline, jam dumped core.
+ This has been fixed.
+
+
+5. Porting
+
+ See jam.h for the definitive list of supported platforms.
+ Since 2.1, support has been added for:
+
+ Macintosh MPW
+ Alpha VMS
+ Alpha NT
+ NT PowerPC
+ BeOS
+ MVS OE
+ UNIXWARE
+ QNX
+ SINIX (Nixdorf)
+ OS/2
+ Interactive UNIX (ISC), courtesy of Matthew Newhook
+
+
+5.1 NT Support Fixes
+
+ The NT command executor now handles multiple line actions, by writing
+ multi-line actions to a batch file and executing that.
+
+ Targets are universally lowercased on NT. (Matthew Newhook)
+
+ Concurrent process support is fully enabled for NT.
+ (Gurusamy Sarathy )
+
+ Path handling: Jam now knows that the directory component of "D:\"
+ is "D:\", just as on unix it knows that the directory component of
+ "/" is "/". It also now successfully gets the timestamp for "D:\"
+ or just plain "\".
+
+
+5.2 VMS Support Fixes
+
+ VMS support is much, much better now. The path name manipulation
+ routines (in pathvms.c) were more or less rewritten, and they now
+ handle the vagaries of combining directory and file names properly.
+
+ Targets are universally lowercased on VMS.
+
+ Multi-line command blocks on VMS are now executed in a single system()
+ call rather than separate ones for each line, so that actions can
+ be DCL scripts.
+
+===============================================================================
+===============================================================================
+
+
+Release notes for Jam 2.1.
+
+1. Release info:
+ Jam 2.1
+ February 1, 1996
+ VERSION 2.1
+ PATCHLEVEL 0
+
+2. Porting
+
+ Linux is now supported.
+
+ FREEBSD is now supported.
+
+ SCO ("M_XENIX") now supported.
+
+ NCR now supported.
+
+ NEXT support from karthy@dannug.dk (Karsten Thygesen)
+
+ DECC support from zinser@axp614.gsi.de (Martin P.J. Zinser)
+
+ I have changes for OS/2, but no way to test them. Volunteers?
+ I have VMS multiprocess support, but no way to test it. Volunteers?
+
+2.1. NT Support fixes.
+
+ The NT support is considerably more real than it was in 2.0.
+ Filent.c had its syntax error corrected, it no longer skips the
+ first entry when scanning directories, and it handles string
+ tables in archives (for long object file names).
+
+ The Jambase was changed a bit to support the various C/C++
+ compilers on NT, although it has only been thorougly tested
+ with MSVC20.
+
+ You still need to set MSVCNT or BCCROOT to the root of the
+ the compiler's directory tree, and you'll get an error if you
+ don't set it (rather than getting a pile of mysterious errors).
+
+2.2. Other porting fixes.
+
+ SPLITPATH now set up for UNIX (:), NT (;), VMS (,)
+
+ Jambase support for Solaris works better now: the location of
+ AR is hardwired to /usr/ccs/bin/ar and it knowns "install"
+ doesn't take -c. Solaris -- how the mighty have fallen.
+
+ To handle Linux's wacko yacc, jamgram.h is now included after
+ scan.h so that YYSTYPE is define.
+
+3. Jambase Changes (see Jamfile.html)
+
+ SubDir now computes the root directory for the source tree, if
+ the variable naming the root directory isn't set in the environment.
+ It counts the number of directory elements leading from the root
+ to the current directory (as passed to SubDir) and uses that many
+ "../"'s to identify the root. This means that to use SubDir you
+ no longer have to have anything special set in the environment.
+
+ InstallFile is now an alias for InstallLib.
+
+ 'first' is now dependency of all pseudo-targets (all, files,
+ exe, lib, shell), so that jamming any of these pseudo-targets
+ also builds any dependencies of 'first'.
+
+ The File rule definition in the Jambase was missing an &.
+
+ The File rule now calls the Clean rule, so that installed files
+ get cleaned.
+
+4. Jam changes (see Jam.html)
+
+ Variables may now be set on the command line with -svar=value.
+
+ Targets marked with NOUPDATE are now immune to the -a (anyhow)
+ flag. Previously, the MkDir rule would try to recreate directories
+ that already exist when jam was invoked with -a.
+
+ A new variable, $(JAMVERSION), joins the small list of built-in
+ variables. It it set to the release of jam, currently "2.1".
+
+ If an actions fails, jam now deletes the target(s). It won't
+ delete libraries or other targets that are composites. This is
+ now consistent with jam's behavior on interrupts (it deletes the
+ targets).
+
+ Jam had a nasty bug when setting multiple variables to the same
+ value: if the first two variable names were the same, the variable
+ value got trashed. This also affected "on target" variables if
+ the first two targets were the same. For example:
+
+ FOO on bar.c bar.c foo.c = a b c ;
+
+ This would mangle the value of FOO for bar.c and foo.c. This has
+ been fixed.
+
+ Jam would generate bogus numbers when reporting the number of
+ targets updated after an interrupt. It now is more careful about
+ counting.
+
+ The debugging flag -d has been extended. In addition to supporting
+ -dx (turn on debugging for all levels up to x) there is also now
+ -d+x (turn on debugging at only level x). The default output
+ level is -d1 (-or d2 if -n is given); this can be turned off with
+ -d0. The debug levels are listed in jam.1 and jam.h.
+
+ The parsing debug output now uses indenting to indicate when
+ one rule invokes another.
+
+===============================================================================
+===============================================================================
+
+
+Release notes for Jam 2.0.
+
+1. Release info:
+ Jam 2.0
+ March 10, 1994
+ VERSION 2.0
+ PATCHLEVEL 5
+
+2. Porting
+
+ Windows/NT is now (crudely) supported, courtesy of Brett Taylor
+ and Laura Wingerd.
+
+ COHERENT/386 is now supported, courtesy of Fred Smith.
+
+ Solaris archive string table for long archive names is now
+ supported, thanks to Mike Matrigali.
+
+3. Compatibility
+
+ Jam 2.0 syntax is a superset of Jam 1.0 syntax, and thus it can
+ interpret a Jam 1.0 Jambase.
+
+ The Jam 2.0 Jambase is a superset of the Jam 1.0 Jambase, and
+ thus it can include a Jamfile written for Jam 1.0.
+
+4. Changes from Jam 1.0 to Jam 2.0
+
+4.1. Documentation changes
+
+ New Jamfile.5 manual page, with lots of examples and easy
+ reading. It replaces both the old "Examples" file as well as
+ the old Jambase.5 manual page.
+
+ jam.1 edited by Stephen W. Liddle and Diane Holt.
+
+4.2. Jambase Changes (see Jamfile.5)
+
+4.2.1. New rules:
+
+ There are new rules to make handling subdirectories easier:
+ SubDir, SubInclude, SubDirCcFlags, SubDirHdrs.
+
+ There are new rules to handle file-specific CCFLAGS and HDRS:
+ ObjectCcFlags and ObjectHdrs.
+
+ Misc new rules: HardLink, InstallShell, MkDir.
+
+ New rule "clean" that deletes exactly what jam has built, and
+ "uninstall" that deletes exactly what was installed.
+
+ New rules for handling suffixes .s, .f, .cc, .cpp, .C.
+
+4.2.2. Old rules:
+
+ The InstallBin, Lib, Man, and the new Shell rules now take the
+ destination directory as the target and the files to be copied
+ as sources. These rules formerly took the files to be copied
+ as targets, and used built-in destination directories of
+ $(BINDIR), $(LIBDIR), $(MANDIR), and $(BINDIR).
+
+ The InstallBin, Lib, Man, and Shell rules use the install(1)
+ program now, instead of doing their own copying.
+
+ The Cc rule now uses -o when possible, rather than moving the
+ result. Some platforms (Pyramid?) have a broken -o.
+
+ Jambase rules taking libraries, objects, and executables now
+ all ignore the suffixes provided and use the one defined in the
+ Jambase for the platform.
+
+ Stupid yyacc support moved out of Jambase, as jam is its only
+ likely user.
+
+ Jambase now purturbs library sources with a "grist" of
+ SOURCE_GRIST.
+
+4.2.3. Misc:
+
+ The names of the default rules defined in Jambase have been
+ lowercased and un-abbreviated, to be more imake(1) like.
+
+ The Jambase has been reorganized and sorted, with VMS and NT
+ support moved in from their own files.
+
+ The Jambase has been relocated on UNIX from /usr/local/lib/jam
+ to /usr/local/lib.
+
+4.3. Jam changes (see jam.1)
+
+4.3.1. Flags:
+
+ New -a (anyhow) flag: means build everything.
+
+ New -j flag: run jobs in parallel.
+
+ Old -t now rebuilds the touched target, rather that just the
+ target's parents.
+
+ -n now implies -d2, so that you see what's happening. The
+ debug level can be subsequently overridden.
+
+ New -v to dump version.
+
+4.3.2. Rules:
+
+ New ALWAYS rule behaves like -t: always builds target.
+
+ New EXIT rule makes it possible to raise a fatal error.
+
+ New LEAVES rule which say target depends only on the update
+ times of the leaf sources.
+
+ New NOUPDATE rule says built targets only if they don't exist.
+
+ NOTIME has been renamed NOTFILE, to more accurately reflect its
+ meaning (it says a target is not to be bound to a file).
+
+4.3.3. Variables:
+
+ New special variable JAMSHELL: argv template for command execution
+ shell.
+
+ Variables, both normal and target-specific, can have their
+ value appended with the syntax "var += value" or "var on target
+ += value".
+
+ "?=" is now synonymous with "default =".
+
+ Imported enviroment variable values are now split at blanks
+ (:'s if the variable name ends in PATH), so that they become
+ proper list values.
+
+4.3.4. Misc:
+
+ Files to be sourced with "include" are now bound first, so
+ $(SEARCH) and $(LOCATE) affect them. They still can't be
+ built, though.
+
+ New modifier on "actions": "existing" causes $(>) to expand
+ only those files that currently exist.
+
+4.3.5. Bug fixes:
+
+ When scanning tokens known to be argument lists (such as the
+ arguments to rule invocations and variable assignment), the
+ parser now tells the scanner to ignore alphabetic keywords, as
+ all such lists terminate with punctuation keywords (like : or
+ ;). This way, alphabetic keywords don't need to be quoted when
+ they appear as arguments.
+
+ The scanner has been fixed to handle oversized tokens,
+ unterminated quotes, unterminated action blocks, and tokens
+ abutting EOF (i.e. a token with no white space before EOF).
+
+ The progress report "...on xth target..." used to count all
+ targets, rather than just those with updating actions. Since
+ the original pronouncement of targets to be udpated included
+ only those with updating actions, the progress report has been
+ changed to match.
+
+ 'If' conditionals now must be single arguments. Previously,
+ they could be zero or more arguments, which didn't make much
+ sense, and made things like 'foo == bar' true. The comparison
+ operator is '=', and '==' just looked like the second of three
+ arguments in the unary "non-empty argument list" conditional.
+
+ Header files indirectly including themselves were mistakenly
+ reported as being dependent on themselves. Recursing through
+ header file dependencies is now done after determining the fate
+ of the target.
+
+ The variable expansion support was expanding $(X)$(UNDEF) as if
+ it were $(X). It now expands to an empty list, like it
+ should.
+
+ The UNIX version of file_build() didn't handle "dir/.suffix"
+ right. Now it does.
+
+ The VMS command buffer was assumed to be as large as 1024 bytes,
+ which isn't the case everywhere as it is related to some weird
+ quota. It has been lowered to 256.
+
+ $(>) and $(<) wouldn't expand in action blocks if the targets
+ were marked with NOTIME. Now they expand properly.
+
+ Malloc() return values are now checked.
+
+ The variable expansion routine var_expand() is now a little
+ faster, by taking a few often needed shortcuts.
+
+ The VMS version of file_build() used the wrong length when
+ re-rooting file names that already had directory compoents.
+ This was fixed.
+
+ Various tracing adjustments were made.
+
+5. Limitations/Known Bugs
+
+ The new Windows/NT support has only been marginally tested. It
+ is dependent on certain variables being set depending on which
+ compiler you are using. You'll need to look in the file
+ Jambase and see what variables are expected to be set.
+
+ The VMS support has been tested, courtesy of the DEC guest
+ machine, but has not been hammered fully in release 2.0. It
+ was used quite a bit in Jam 1.0.
+
+ Jam clean when there is nothing to clean claims it is updating
+ a target.
+
+ Because the include statement works by pushing a new file in
+ the input stream of the scanner rather than recursively
+ invoking the parser on the new file, multiple include
+ statements in a rule's procedure causes the files to be
+ included in reverse order.
+
+ If the include statement appears inside an if block, the
+ parser's attempt to find the else will cause the text of the
+ included file to appear after the first token following the
+ statement block. This is rarely what is intended.
+
+ In a rule's actions, only $(<) and $(>) refer to the bound file
+ names: all other variable references get the unbound names.
+ This is a pain for $(NEEDLIBS), because it means that library
+ path can't be bound using $(SEARCH) and $(LOCATE).
+
+ With the -j flag, errors from failed commands can get
+ staggeringly mixed up. Also, because targets tend to get built
+ in a quickest-first ordering, dependency information must be
+ quite exact. Finally, beware of parallelizing commands that
+ drop fixed-named files into the current directory, like yacc(1)
+ does.
+
+ A poorly set $(JAMSHELL) is likely to result in silent
+ failure.
diff --git a/src/tools/jam/builtins.c b/src/tools/jam/builtins.c
new file mode 100644
index 0000000000..b40e9ce753
--- /dev/null
+++ b/src/tools/jam/builtins.c
@@ -0,0 +1,293 @@
+/*
+ * 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 "pathsys.h"
+
+/*
+ * 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
+
+LIST *builtin_depends( PARSE *parse, LOL *args );
+LIST *builtin_echo( PARSE *parse, LOL *args );
+LIST *builtin_exit( PARSE *parse, LOL *args );
+LIST *builtin_flags( PARSE *parse, LOL *args );
+LIST *builtin_glob( PARSE *parse, LOL *args );
+LIST *builtin_match( PARSE *parse, LOL *args );
+
+int glob( char *s, char *c );
+
+void
+load_builtins()
+{
+ bindrule( "Always" )->procedure =
+ bindrule( "ALWAYS" )->procedure =
+ parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_TOUCHED );
+
+ bindrule( "Depends" )->procedure =
+ bindrule( "DEPENDS" )->procedure =
+ parse_make( builtin_depends, P0, P0, P0, C0, C0, T_DEPS_DEPENDS );
+
+ bindrule( "echo" )->procedure =
+ bindrule( "Echo" )->procedure =
+ bindrule( "ECHO" )->procedure =
+ parse_make( builtin_echo, P0, P0, P0, C0, C0, 0 );
+
+ bindrule( "exit" )->procedure =
+ bindrule( "Exit" )->procedure =
+ bindrule( "EXIT" )->procedure =
+ parse_make( builtin_exit, P0, P0, P0, C0, C0, 0 );
+
+ bindrule( "Glob" )->procedure =
+ bindrule( "GLOB" )->procedure =
+ parse_make( builtin_glob, P0, P0, P0, C0, C0, 0 );
+
+ bindrule( "Includes" )->procedure =
+ bindrule( "INCLUDES" )->procedure =
+ parse_make( builtin_depends, P0, P0, P0, C0, C0, T_DEPS_INCLUDES );
+
+ bindrule( "Leaves" )->procedure =
+ bindrule( "LEAVES" )->procedure =
+ parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_LEAVES );
+
+ bindrule( "Match" )->procedure =
+ bindrule( "MATCH" )->procedure =
+ parse_make( builtin_match, P0, P0, P0, C0, C0, 0 );
+
+ bindrule( "NoCare" )->procedure =
+ bindrule( "NOCARE" )->procedure =
+ parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOCARE );
+
+ bindrule( "NOTIME" )->procedure =
+ bindrule( "NotFile" )->procedure =
+ bindrule( "NOTFILE" )->procedure =
+ parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOTFILE );
+
+ bindrule( "NoUpdate" )->procedure =
+ bindrule( "NOUPDATE" )->procedure =
+ parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOUPDATE );
+
+ bindrule( "Temporary" )->procedure =
+ bindrule( "TEMPORARY" )->procedure =
+ parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_TEMP );
+}
+
+/*
+ * builtin_depends() - DEPENDS/INCLUDES rule
+ *
+ * The DEPENDS builtin rule appends each of the listed sources on the
+ * dependency list of each of the listed targets. It binds both the
+ * targets and sources as TARGETs.
+ */
+
+LIST *
+builtin_depends(
+ PARSE *parse,
+ LOL *args )
+{
+ LIST *targets = lol_get( args, 0 );
+ LIST *sources = lol_get( args, 1 );
+ int which = parse->num;
+ LIST *l;
+
+ for( l = targets; l; l = list_next( l ) )
+ {
+ TARGET *t = bindtarget( l->string );
+ t->deps[ which ] = targetlist( t->deps[ which ], sources );
+ }
+
+ 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,
+ LOL *args )
+{
+ list_print( lol_get( args, 0 ) );
+ printf( "\n" );
+ 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,
+ LOL *args )
+{
+ list_print( lol_get( args, 0 ) );
+ printf( "\n" );
+ 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,
+ LOL *args )
+{
+ LIST *l = lol_get( 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;
+} ;
+
+static void
+builtin_glob_back(
+ void *closure,
+ char *file,
+ int status,
+ time_t time )
+{
+ struct globbing *globbing = (struct globbing *)closure;
+ LIST *l;
+ PATHNAME f;
+ char buf[ MAXJPATH ];
+
+ /* Null out directory for matching. */
+ /* We wish we had file_dirscan() pass up a PATHNAME. */
+
+ path_parse( file, &f );
+ f.f_dir.len = 0;
+ path_build( &f, buf, 0 );
+
+ for( l = globbing->patterns; l; l = l->next )
+ if( !glob( l->string, buf ) )
+ {
+ globbing->results = list_new( globbing->results, newstr( file ) );
+ break;
+ }
+}
+
+LIST *
+builtin_glob(
+ PARSE *parse,
+ LOL *args )
+{
+ LIST *l = lol_get( args, 0 );
+ LIST *r = lol_get( args, 1 );
+
+ struct globbing globbing;
+
+ globbing.results = L0;
+ globbing.patterns = r;
+
+ for( ; l; l = list_next( l ) )
+ file_dirscan( l->string, builtin_glob_back, &globbing );
+
+ return globbing.results;
+}
+
+/*
+ * builtin_match() - MATCH rule, regexp matching
+ */
+
+LIST *
+builtin_match(
+ PARSE *parse,
+ LOL *args )
+{
+ LIST *l, *r;
+ LIST *result = 0;
+
+ /* For each pattern */
+
+ for( l = lol_get( args, 0 ); l; l = l->next )
+ {
+ regexp *re = regcomp( l->string );
+
+ /* For each string to match against */
+
+ for( r = lol_get( args, 1 ); r; r = r->next )
+ if( regexec( re, r->string ) )
+ {
+ int i, 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++ )
+ {
+ char buf[ MAXSYM ];
+ int l = re->endp[i] - re->startp[i];
+ memcpy( buf, re->startp[i], l );
+ buf[ l ] = 0;
+ result = list_new( result, newstr( buf ) );
+ }
+ }
+
+ free( (char *)re );
+ }
+
+ return result;
+}
diff --git a/src/tools/jam/builtins.h b/src/tools/jam/builtins.h
new file mode 100644
index 0000000000..e4320b7b18
--- /dev/null
+++ b/src/tools/jam/builtins.h
@@ -0,0 +1,12 @@
+/*
+ * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * builtins.h - compile parsed jam statements
+ */
+
+void load_builtins();
+
diff --git a/src/tools/jam/command.c b/src/tools/jam/command.c
new file mode 100644
index 0000000000..60c3eb20a2
--- /dev/null
+++ b/src/tools/jam/command.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * command.c - maintain lists of commands
+ */
+
+# include "jam.h"
+
+# include "lists.h"
+# include "parse.h"
+# include "variable.h"
+# include "rules.h"
+
+# include "command.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 *)malloc( sizeof( CMD ) );
+
+ cmd->rule = rule;
+ cmd->shell = shell;
+ cmd->next = 0;
+
+ lol_init( &cmd->args );
+ lol_add( &cmd->args, targets );
+ lol_add( &cmd->args, sources );
+
+ /* Bail if the result won't fit in MAXLINE */
+ /* We don't free targets/sources/shell if bailing. */
+
+ if( var_string( rule->actions, cmd->buf, MAXLINE, &cmd->args ) < 0 )
+ {
+ cmd_free( cmd );
+ return 0;
+ }
+
+ return cmd;
+}
+
+/*
+ * cmd_free() - free a CMD
+ */
+
+void
+cmd_free( CMD *cmd )
+{
+ lol_free( &cmd->args );
+ list_free( cmd->shell );
+ free( (char *)cmd );
+}
diff --git a/src/tools/jam/command.h b/src/tools/jam/command.h
new file mode 100644
index 0000000000..9044cb4ea4
--- /dev/null
+++ b/src/tools/jam/command.h
@@ -0,0 +1,58 @@
+/*
+ * 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[ MAXLINE ]; /* actual commands */
+} ;
+
+CMD *cmd_new(
+ RULE *rule, /* rule (referenced) */
+ LIST *targets, /* $(<) (freed) */
+ LIST *sources, /* $(>) (freed) */
+ LIST *shell ); /* $(SHELL) (freed) */
+
+void cmd_free( CMD *cmd );
+
+# define cmd_next( c ) ((c)->next)
diff --git a/src/tools/jam/compile.c b/src/tools/jam/compile.c
new file mode 100644
index 0000000000..c0096111e2
--- /dev/null
+++ b/src/tools/jam/compile.c
@@ -0,0 +1,808 @@
+/*
+ * 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 "compile.h"
+# include "variable.h"
+# include "expand.h"
+# include "rules.h"
+# include "newstr.h"
+# include "search.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_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
+ *
+ * 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 );
+int glob( char *s, char *c );
+
+
+
+/*
+ * 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,
+ LOL *args )
+{
+ /* Append right to left. */
+
+ return list_append(
+ (*parse->left->func)( parse->left, args ),
+ (*parse->right->func)( parse->right, args ) );
+}
+
+/*
+ * 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,
+ LOL *args )
+{
+ LIST *ll, *lr, *s, *t;
+ int status = 0;
+
+ /* Short circuit lr eval for &&, ||, and 'in' */
+
+ ll = (*parse->left->func)( parse->left, args );
+ 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->right->func)( parse->right, args );
+ }
+
+ /* 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" );
+ 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,
+ LOL *args )
+{
+ LIST *nv = (*parse->left->func)( parse->left, args );
+ LIST *l;
+
+ /* 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->right->func)( parse->right, args ) );
+ }
+
+ 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,
+ LOL *args )
+{
+ LIST *l = (*p->left->func)( p->left, args );
+
+ if( l )
+ {
+ list_free( l );
+ return (*p->right->func)( p->right, args );
+ }
+ else
+ {
+ return (*p->third->func)( p->third, args );
+ }
+}
+
+/*
+ * 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,
+ LOL *args )
+{
+ LIST *nt = (*parse->left->func)( parse->left, args );
+
+ if( DEBUG_COMPILE )
+ {
+ debug_compile( 0, "include" );
+ list_print( nt );
+ printf( "\n" );
+ }
+
+ if( nt )
+ {
+ TARGET *t = bindtarget( nt->string );
+
+ /* 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 );
+ t->boundname = search( t->name, &t->time );
+ popsettings( t->settings );
+
+ parse_file( t->boundname );
+ }
+
+ list_free( nt );
+
+ return L0;
+}
+
+/*
+ * compile_list() - expand and return a list
+ *
+ * parse->string - character string to expand
+ */
+
+LIST *
+compile_list(
+ PARSE *parse,
+ LOL *args )
+{
+ /* voodoo 1 means: s is a copyable string */
+ char *s = parse->string;
+ return var_expand( L0, s, s + strlen( s ), 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,
+ LOL *args )
+{
+ LIST *l;
+ SETTINGS *s = 0;
+ LIST *nt = (*parse->left->func)( parse->left, args );
+ LIST *ns = (*parse->right->func)( parse->right, args );
+ LIST *result;
+
+ if( DEBUG_COMPILE )
+ {
+ debug_compile( 0, "local" );
+ list_print( nt );
+ printf( " = " );
+ list_print( ns );
+ printf( "\n" );
+ }
+
+ /* Initial value is ns */
+
+ for( l = nt; l; l = list_next( l ) )
+ s = addsettings( s, 0, 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->third->func)( parse->third, args );
+ popsettings( s );
+
+ freesettings( s );
+
+ return result;
+}
+
+/*
+ * compile_null() - do nothing -- a stub for parsing
+ */
+
+LIST *
+compile_null(
+ PARSE *parse,
+ LOL *args )
+{
+ 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,
+ LOL *args )
+{
+ LIST *nt = (*parse->left->func)( parse->left, args );
+ LIST *result = 0;
+
+ if( DEBUG_COMPILE )
+ {
+ debug_compile( 0, "on" );
+ list_print( nt );
+ printf( "\n" );
+ }
+
+ if( nt )
+ {
+ TARGET *t = bindtarget( nt->string );
+
+ pushsettings( t->settings );
+ result = (*parse->right->func)( parse->right, args );
+ t->boundname = search( t->name, &t->time );
+ popsettings( t->settings );
+
+ }
+
+ list_free( nt );
+
+ return result;
+}
+
+
+/*
+ * compile_rule() - compile a single user defined rule
+ *
+ * parse->left list of rules to run
+ * parse->right parameters (list of lists) to rule, recursing left
+ *
+ * Wrapped around evaluate_rule() so that headers() can share it.
+ */
+
+LIST *
+compile_rule(
+ PARSE *parse,
+ LOL *args )
+{
+ LOL nargs[1];
+ LIST *result = 0;
+ LIST *ll, *l;
+ PARSE *p;
+
+ /* list of rules to run -- normally 1! */
+
+ ll = (*parse->left->func)( parse->left, args );
+
+ /* Build up the list of arg lists */
+
+ lol_init( nargs );
+
+ for( p = parse->right; p; p = p->left )
+ lol_add( nargs, (*p->right->func)( p->right, args ) );
+
+ /* Run rules, appending results from each */
+
+ for( l = ll; l; l = list_next( l ) )
+ result = evaluate_rule( l->string, nargs, result );
+
+ list_free( ll );
+ lol_free( nargs );
+
+ return result;
+}
+
+/*
+ * evaluate_rule() - execute a rule invocation
+ */
+
+LIST *
+evaluate_rule(
+ char *rulename,
+ LOL *args,
+ LIST *result )
+{
+ RULE *rule = bindrule( rulename );
+
+ if( DEBUG_COMPILE )
+ {
+ debug_compile( 1, rulename );
+ lol_print( args );
+ printf( "\n" );
+ }
+
+ /* Check traditional targets $(<) and sources $(>) */
+
+ if( !rule->actions && !rule->procedure )
+ printf( "warning: unknown rule %s\n", rule->name );
+
+ /* 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 *)malloc( sizeof( ACTION ) );
+ memset( (char *)action, '\0', sizeof( *action ) );
+
+ action->rule = rule;
+ action->targets = targetlist( (TARGETS *)0, lol_get( args, 0 ) );
+ action->sources = targetlist( (TARGETS *)0, lol_get( args, 1 ) );
+
+ /* 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 */
+ /* refer/free to ensure rule not freed during use */
+
+ if( rule->procedure )
+ {
+ PARSE *parse = rule->procedure;
+ parse_refer( parse );
+ result = list_append( result, (*parse->func)( parse, args ) );
+ parse_free( parse );
+ }
+
+ if( DEBUG_COMPILE )
+ debug_compile( -1, 0 );
+
+ 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,
+ LOL *args )
+{
+ /* Ignore result from first statement; return the 2nd. */
+ /* Optimize recursion on the right by looping. */
+
+ do list_free( (*parse->left->func)( parse->left, args ) );
+ while( (parse = parse->right)->func == compile_rules );
+
+ return (*parse->func)( parse, args );
+}
+
+/*
+ * 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,
+ LOL *args )
+{
+ LIST *nt = (*parse->left->func)( parse->left, args );
+ LIST *ns = (*parse->right->func)( parse->right, args );
+ LIST *l;
+ int setflag;
+ char *trace;
+
+ switch( parse->num )
+ {
+ 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( DEBUG_COMPILE )
+ {
+ debug_compile( 0, "set" );
+ 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
+ */
+
+LIST *
+compile_setcomp(
+ PARSE *parse,
+ LOL *args )
+{
+ RULE *rule = bindrule( parse->string );
+
+ if( DEBUG_COMPILE )
+ {
+ debug_compile( 0, "rule" );
+ printf( " %s\n", parse->string );
+ }
+
+ /* Free old one, if present */
+
+ if( rule->procedure )
+ parse_free( rule->procedure );
+
+ rule->procedure = parse->left;
+
+ /* we now own this parse tree */
+ /* don't let parse_free() release it */
+
+ parse_refer( parse->left );
+
+ 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 transfered
+ * directly to the rule flags (as defined in rules.h).
+ */
+
+LIST *
+compile_setexec(
+ PARSE *parse,
+ LOL *args )
+{
+ RULE *rule = bindrule( parse->string );
+ LIST *bindlist = (*parse->left->func)( parse->left, args );
+
+ /* Free old one, if present */
+
+ if( rule->actions )
+ {
+ freestr( rule->actions );
+ list_free( rule->bindlist );
+ }
+
+ rule->actions = copystr( parse->string1 );
+ rule->bindlist = bindlist;
+ rule->flags = 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,
+ LOL *args )
+{
+ LIST *nt = (*parse->left->func)( parse->left, args );
+ LIST *ns = (*parse->third->func)( parse->third, args );
+ LIST *targets = (*parse->right->func)( parse->right, args );
+ LIST *ts;
+ int append = parse->num == ASSIGN_APPEND;
+
+ if( DEBUG_COMPILE )
+ {
+ debug_compile( 0, "set" );
+ list_print( nt );
+ printf( "on " );
+ list_print( targets );
+ printf( " %s ", append ? "+=" : "=" );
+ 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, append,
+ 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,
+ LOL *args )
+{
+ LIST *nt = (*parse->left->func)( parse->left, args );
+ LIST *result = 0;
+
+ if( DEBUG_COMPILE )
+ {
+ debug_compile( 0, "switch" );
+ 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->func)( parse, args );
+ break;
+ }
+ }
+
+ list_free( nt );
+
+ return result;
+}
+
+/*
+ * compile_while() - compile 'while' rule
+ *
+ * parse->left condition tree
+ * parse->right execution tree
+ */
+
+LIST *
+compile_while(
+ PARSE *p,
+ LOL *args )
+{
+ LIST *r = 0;
+ LIST *l;
+
+ /* Returns the value from the last execution of the block */
+
+ while( l = (*p->left->func)( p->left, args ) )
+ {
+ list_free( l );
+ if( r ) list_free( r );
+ r = (*p->right->func)( p->right, args );
+ }
+
+ return r;
+}
+
+/*
+ * debug_compile() - printf with indent to show rule expansion.
+ */
+
+static void
+debug_compile( int which, char *s )
+{
+ static int level = 0;
+ static char indent[36] = ">>>>|>>>>|>>>>|>>>>|>>>>|>>>>|>>>>|";
+ int i = ((1+level) * 2) % 35;
+
+ if( which >= 0 )
+ printf( "%*.*s ", i, i, indent );
+
+ if( s )
+ printf( "%s ", s );
+
+ level += which;
+}
diff --git a/src/tools/jam/compile.h b/src/tools/jam/compile.h
new file mode 100644
index 0000000000..ea8301269a
--- /dev/null
+++ b/src/tools/jam/compile.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * compile.h - compile parsed jam statements
+ */
+
+void compile_builtins();
+
+LIST *compile_append( PARSE *parse, LOL *args );
+LIST *compile_foreach( PARSE *parse, LOL *args );
+LIST *compile_if( PARSE *parse, LOL *args );
+LIST *compile_eval( PARSE *parse, LOL *args );
+LIST *compile_include( PARSE *parse, LOL *args );
+LIST *compile_list( PARSE *parse, LOL *args );
+LIST *compile_local( PARSE *parse, LOL *args );
+LIST *compile_null( PARSE *parse, LOL *args );
+LIST *compile_on( PARSE *parse, LOL *args );
+LIST *compile_rule( PARSE *parse, LOL *args );
+LIST *compile_rules( PARSE *parse, LOL *args );
+LIST *compile_set( PARSE *parse, LOL *args );
+LIST *compile_setcomp( PARSE *parse, LOL *args );
+LIST *compile_setexec( PARSE *parse, LOL *args );
+LIST *compile_settings( PARSE *parse, LOL *args );
+LIST *compile_switch( PARSE *parse, LOL *args );
+LIST *compile_while( PARSE *parse, LOL *args );
+
+LIST *evaluate_rule( char *rulename, LOL *args, LIST *result );
+
+/* 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 */
+
+/* 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 */
diff --git a/src/tools/jam/execcmd.h b/src/tools/jam/execcmd.h
new file mode 100644
index 0000000000..1e7a608351
--- /dev/null
+++ b/src/tools/jam/execcmd.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * execcmd.h - execute a shell script
+ *
+ * 05/04/94 (seiwald) - async multiprocess interface
+ */
+
+void execcmd(
+ char *string,
+ void (*func)( void *closure, int status ),
+ void *closure,
+ LIST *shell );
+
+int execwait();
+
+# define EXEC_CMD_OK 0
+# define EXEC_CMD_FAIL 1
+# define EXEC_CMD_INTR 2
diff --git a/src/tools/jam/execmac.c b/src/tools/jam/execmac.c
new file mode 100644
index 0000000000..20217dab19
--- /dev/null
+++ b/src/tools/jam/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
+
+# 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:
+ * execcmd() - launch an async command execution
+ * execwait() - 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
+ */
+
+/*
+ * execcmd() - launch an async command execution
+ */
+
+void
+execcmd(
+ char *string,
+ void (*func)( void *closure, int status ),
+ void *closure,
+ LIST *shell )
+{
+
+ printf( "%s", string );
+ (*func)( closure, EXEC_CMD_OK );
+}
+
+/*
+ * execwait() - wait and drive at most one execution completion
+ */
+
+int
+execwait()
+{
+ return 0;
+}
+
+# endif /* OS_MAC */
diff --git a/src/tools/jam/execunix.c b/src/tools/jam/execunix.c
new file mode 100644
index 0000000000..0eb03ca3ae
--- /dev/null
+++ b/src/tools/jam/execunix.c
@@ -0,0 +1,370 @@
+/*
+ * 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
+
+# ifdef USE_EXECUNIX
+
+# ifdef NO_VFORK
+# define vfork() fork()
+# endif
+
+# if defined( OS_NT ) || defined( OS_OS2 )
+
+# define USE_EXECNT
+
+# include
+
+# if !defined( __BORLANDC__ ) && !defined( OS_OS2 )
+# define wait my_wait
+static int my_wait( int *status );
+# endif
+
+# 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) 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:
+ * execcmd() - launch an async command execution
+ * execwait() - 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 int intr = 0;
+static int cmdsrunning = 0;
+static void (*istat)( int );
+
+static struct
+{
+ int pid; /* on win32, a real process handle */
+ void (*func)( void *closure, int status );
+ void *closure;
+
+# ifdef USE_EXECNT
+ char *tempfile;
+# endif
+
+} cmdtab[ MAXJOBS ] = {{0}};
+
+/*
+ * onintr() - bump intr to note command interruption
+ */
+
+void
+onintr( int disp )
+{
+ intr++;
+ printf( "...interrupted\n" );
+}
+
+/*
+ * execcmd() - launch an async command execution
+ */
+
+void
+execcmd(
+ char *string,
+ void (*func)( void *closure, int status ),
+ void *closure,
+ LIST *shell )
+{
+ int pid;
+ int slot;
+ char *argv[ MAXARGC + 1 ]; /* +1 for NULL */
+
+# ifdef USE_EXECNT
+ char *p;
+# endif
+
+ /* 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 );
+ }
+
+# ifdef USE_EXECNT
+ if( !cmdtab[ slot ].tempfile )
+ {
+ char *tempdir;
+
+ if( !( tempdir = getenv( "TEMP" ) ) &&
+ !( tempdir = getenv( "TMP" ) ) )
+ tempdir = "\\temp";
+
+ cmdtab[ slot ].tempfile = malloc( strlen( tempdir ) + 14 );
+
+ sprintf( cmdtab[ slot ].tempfile, "%s\\jamtmp%02d.bat",
+ tempdir, slot );
+ }
+
+ /* Trim leading, ending white space */
+
+ while( isspace( *string ) )
+ ++string;
+
+ p = strchr( string, '\n' );
+
+ while( p && isspace( *p ) )
+ ++p;
+
+ /* If multi line, or too long, or JAMSHELL is set, write to bat file. */
+ /* Otherwise, exec directly. */
+ /* Frankly, if it is a single long line I don't think the */
+ /* command interpreter will do any better -- it will fail. */
+
+ if( p && *p || strlen( string ) > MAXLINE || shell )
+ {
+ FILE *f;
+
+ /* Write command to bat file. */
+
+ f = fopen( cmdtab[ slot ].tempfile, "w" );
+ fputs( string, f );
+ fclose( f );
+
+ string = cmdtab[ slot ].tempfile;
+ }
+# endif
+
+ /* 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
+ {
+# ifdef USE_EXECNT
+ argv[0] = "cmd.exe";
+ argv[1] = "/Q/C"; /* anything more is non-portable */
+# else
+ argv[0] = "/bin/sh";
+ argv[1] = "-c";
+# endif
+ argv[2] = string;
+ argv[3] = 0;
+ }
+
+ /* Catch interrupts whenever commands are running. */
+
+ if( !cmdsrunning++ )
+ istat = signal( SIGINT, onintr );
+
+ /* Start the command */
+
+# ifdef USE_EXECNT
+ if( ( pid = spawnvp( P_NOWAIT, argv[0], argv ) ) == -1 )
+ {
+ perror( "spawn" );
+ exit( EXITBAD );
+ }
+# else
+ if ((pid = vfork()) == 0)
+ {
+ execvp( argv[0], argv );
+ _exit(127);
+ }
+
+ if( pid == -1 )
+ {
+ perror( "vfork" );
+ exit( EXITBAD );
+ }
+# endif
+ /* Save the operation for execwait() to find. */
+
+ cmdtab[ slot ].pid = pid;
+ cmdtab[ slot ].func = func;
+ cmdtab[ slot ].closure = closure;
+
+ /* Wait until we're under the limit of concurrent commands. */
+ /* Don't trust globs.jobs alone. */
+
+ while( cmdsrunning >= MAXJOBS || cmdsrunning >= globs.jobs )
+ if( !execwait() )
+ break;
+}
+
+/*
+ * execwait() - wait and drive at most one execution completion
+ */
+
+int
+execwait()
+{
+ int i;
+ int status, w;
+ int rstat;
+
+ /* Handle naive make1() which doesn't know if cmds are running. */
+
+ if( !cmdsrunning )
+ return 0;
+
+ /* Pick up process pid and status */
+
+ while( ( w = wait( &status ) ) == -1 && errno == EINTR )
+ ;
+
+ if( w == -1 )
+ {
+ printf( "child process(es) lost!\n" );
+ perror("wait");
+ exit( EXITBAD );
+ }
+
+ /* Find the process in the cmdtab. */
+
+ for( i = 0; i < MAXJOBS; i++ )
+ if( w == cmdtab[ i ].pid )
+ break;
+
+ if( i == MAXJOBS )
+ {
+ printf( "waif child found!\n" );
+ exit( EXITBAD );
+ }
+
+ /* Drive the completion */
+
+ if( !--cmdsrunning )
+ signal( SIGINT, istat );
+
+ if( intr )
+ rstat = EXEC_CMD_INTR;
+ else if( w == -1 || status != 0 )
+ rstat = EXEC_CMD_FAIL;
+ else
+ rstat = EXEC_CMD_OK;
+
+ cmdtab[ i ].pid = 0;
+
+ (*cmdtab[ i ].func)( cmdtab[ i ].closure, rstat );
+
+ return 1;
+}
+
+# if defined( OS_NT ) && !defined( __BORLANDC__ )
+
+# define WIN32_LEAN_AND_MEAN
+
+# include /* do the ugly deed */
+
+static int
+my_wait( int *status )
+{
+ int i, num_active = 0;
+ DWORD exitcode, waitcode;
+ static HANDLE *active_handles = 0;
+
+ if (!active_handles)
+ active_handles = (HANDLE *)malloc(globs.jobs * sizeof(HANDLE) );
+
+ /* first see if any non-waited-for processes are dead,
+ * and return if so.
+ */
+ for ( i = 0; i < globs.jobs; i++ ) {
+ if ( cmdtab[i].pid ) {
+ if ( GetExitCodeProcess((HANDLE)cmdtab[i].pid, &exitcode) ) {
+ if ( exitcode == STILL_ACTIVE )
+ active_handles[num_active++] = (HANDLE)cmdtab[i].pid;
+ else {
+ CloseHandle((HANDLE)cmdtab[i].pid);
+ *status = (int)((exitcode & 0xff) << 8);
+ return cmdtab[i].pid;
+ }
+ }
+ else
+ goto FAILED;
+ }
+ }
+
+ /* if a child exists, wait for it to die */
+ if ( !num_active ) {
+ errno = ECHILD;
+ return -1;
+ }
+ waitcode = WaitForMultipleObjects( num_active,
+ active_handles,
+ FALSE,
+ INFINITE );
+ if ( waitcode != WAIT_FAILED ) {
+ if ( waitcode >= WAIT_ABANDONED_0
+ && waitcode < WAIT_ABANDONED_0 + num_active )
+ i = waitcode - WAIT_ABANDONED_0;
+ else
+ i = waitcode - WAIT_OBJECT_0;
+ if ( GetExitCodeProcess(active_handles[i], &exitcode) ) {
+ CloseHandle(active_handles[i]);
+ *status = (int)((exitcode & 0xff) << 8);
+ return (int)active_handles[i];
+ }
+ }
+
+FAILED:
+ errno = GetLastError();
+ return -1;
+
+}
+
+# endif /* NT && !__BORLANDC__ */
+
+# endif /* USE_EXECUNIX */
diff --git a/src/tools/jam/execvms.c b/src/tools/jam/execvms.c
new file mode 100644
index 0000000000..0bf5486f2f
--- /dev/null
+++ b/src/tools/jam/execvms.c
@@ -0,0 +1,167 @@
+/*
+ * 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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/*
+ * 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 don't 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) - don't 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
+execcmd(
+ char *string,
+ void (*func)( void *closure, int status ),
+ void *closure,
+ LIST *shell )
+{
+ char *s, *e, *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
+execwait()
+{
+ return 0;
+}
+
+# endif /* VMS */
diff --git a/src/tools/jam/expand.c b/src/tools/jam/expand.c
new file mode 100644
index 0000000000..706c18379c
--- /dev/null
+++ b/src/tools/jam/expand.c
@@ -0,0 +1,527 @@
+/*
+ * 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"
+
+/*
+ * 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 */
+ 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, char *out, VAR_EDITS *edits );
+static void var_edit_shift( char *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 ];
+ char *out = out_buf;
+ 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 alot of cases: $(<) and $(>) */
+
+ if( in[0] == '$' && in[1] == '(' && in[3] == ')' && !in[4] )
+ {
+ switch( in[2] )
+ {
+ case '1':
+ case '<':
+ return list_copy( l, lol_get( lol, 0 ) );
+
+ case '2':
+ case '>':
+ return list_copy( l, lol_get( lol, 1 ) );
+ }
+ }
+
+ /* Just try simple copy of in to out. */
+
+ while( in < end )
+ if( ( *out++ = *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 the copystr() to put it on the new list. */
+ /* Otherwise, we use the slower newstr(). */
+
+ *out = '\0';
+
+ if( cancopyin )
+ return list_new( l, copystr( inp ) );
+ else
+ return list_new( l, newstr( out_buf ) );
+
+ expand:
+ /*
+ * 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;
+ out--, in++;
+ ov = out;
+
+ while( in < end && depth )
+ {
+ switch( *ov++ = *in++ )
+ {
+ case '(': depth++; break;
+ case ')': depth--; break;
+ case ':': ov[-1] = MAGIC_COLON; break;
+ case '[': ov[-1] = MAGIC_LEFT; break;
+ case ']': ov[-1] = MAGIC_RIGHT; break;
+ }
+ }
+
+ /* Copied ) - back up. */
+
+ ov--;
+
+ /*
+ * 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'll 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, *evalue = 0;
+ char *colon;
+ char *bracket;
+ char varname[ MAXSYM ];
+ int sub1 = 0, sub2 = -1;
+ VAR_EDITS edits;
+
+ /* Look for a : modifier in the variable name */
+ /* Must copy into varname so we can modify it */
+
+ strcpy( varname, vars->string );
+
+ if( colon = strchr( varname, MAGIC_COLON ) )
+ {
+ *colon = '\0';
+ var_edit_parse( colon + 1, &edits );
+ }
+
+ /* Look for [x-y] subscripting */
+ /* sub1 is x (0 default) */
+ /* sub2 is length (-1 means forever) */
+
+ if( bracket = strchr( varname, MAGIC_LEFT ) )
+ {
+ char *dash;
+
+ if( dash = strchr( bracket + 1, '-' ) )
+ *dash = '\0';
+
+ sub1 = atoi( bracket + 1 ) - 1;
+
+ if( !dash )
+ sub2 = 1;
+ else if( !dash[1] || dash[1] == MAGIC_RIGHT )
+ sub2 = -1;
+ else
+ sub2 = atoi( dash + 1 ) - sub1;
+
+ *bracket = '\0';
+ }
+
+ /* Get variable value, specially handling $(<), $(>), $(n) */
+
+ if( varname[0] == '<' && !varname[1] )
+ value = lol_get( lol, 0 );
+ else if( varname[0] == '>' && !varname[1] )
+ value = lol_get( lol, 1 );
+ else if( varname[0] >= '1' && varname[0] <= '9' && !varname[1] )
+ value = lol_get( lol, varname[0] - '1' );
+ else
+ value = var_get( varname );
+
+ /* The fast path: $(x) - just copy the variable value. */
+ /* This is only an optimization */
+
+ if( out == out_buf && !bracket && !colon && in == end )
+ {
+ 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 */
+
+ for( ; value; value = list_next( value ) )
+ {
+ LIST *rem;
+ char *out1;
+
+ /* Handle end subscript (length actually) */
+
+ if( sub2 >= 0 && --sub2 < 0 )
+ break;
+
+ /* Apply : mods, if present */
+
+ if( colon && edits.filemods )
+ var_edit_file( value->string, out, &edits );
+ else
+ strcpy( out, value->string );
+
+ if( colon && ( edits.upshift || edits.downshift ) )
+ var_edit_shift( out, &edits );
+
+ /* Handle :J=joinval */
+ /* If we have more values for this var, just */
+ /* keep appending them (with the join value) */
+ /* rather than creating separate LIST elements. */
+
+ if( colon && edits.join.ptr &&
+ ( list_next( value ) || list_next( vars ) ) )
+ {
+ out += strlen( out );
+ strcpy( out, edits.join.ptr );
+ out += strlen( out );
+ continue;
+ }
+
+ /* If no remainder, append result to output chain. */
+
+ if( in == end )
+ {
+ l = list_new( l, newstr( out_buf ) );
+ 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' */
+
+ out1 = out + strlen( out );
+
+ for( rem = remainder; rem; rem = list_next( rem ) )
+ {
+ strcpy( out1, rem->string );
+ l = list_new( l, newstr( out_buf ) );
+ }
+ }
+
+ /* Toss used empty */
+
+ if( evalue )
+ list_free( evalue );
+ }
+
+ /* 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" );
+ }
+
+ 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
+ * 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;
+
+ 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 != '=' )
+ {
+ int i;
+
+ if( !havezeroed++ )
+ 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,
+ char *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(
+ char *out,
+ VAR_EDITS *edits )
+{
+ /* Handle upshifting, downshifting now */
+
+ if( edits->upshift )
+ {
+ for( ; *out; ++out )
+ *out = toupper( *out );
+ }
+ else if( edits->downshift )
+ {
+ for( ; *out; ++out )
+ *out = tolower( *out );
+ }
+}
diff --git a/src/tools/jam/expand.h b/src/tools/jam/expand.h
new file mode 100644
index 0000000000..c0644b150a
--- /dev/null
+++ b/src/tools/jam/expand.h
@@ -0,0 +1,11 @@
+/*
+ * 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
+ */
+
+LIST *var_expand( LIST *l, char *in, char *end, LOL *lol, int cancopyin );
diff --git a/src/tools/jam/filemac.c b/src/tools/jam/filemac.c
new file mode 100644
index 0000000000..fcfa93cf9d
--- /dev/null
+++ b/src/tools/jam/filemac.c
@@ -0,0 +1,165 @@
+/*
+ * 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 "filesys.h"
+# include "pathsys.h"
+
+# ifdef OS_MAC
+
+#include
+#include
+
+# 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(const char * 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;
+ char filename[ MAXJPATH ];
+ 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;
+
+ 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;
+
+ path_build( &f, filename, 0 );
+
+ (*func)( closure, filename, 0 /* not stat()'ed */, (time_t)0 );
+ }
+}
+
+/*
+ * 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;
+}
+
+/*
+ * file_archscan() - scan an archive for files
+ */
+
+void
+file_archscan(
+ char *archive,
+ scanback func,
+ void *closure )
+{
+}
+
+
+# endif /* macintosh */
+
diff --git a/src/tools/jam/filent.c b/src/tools/jam/filent.c
new file mode 100644
index 0000000000..d061d4b3a8
--- /dev/null
+++ b/src/tools/jam/filent.c
@@ -0,0 +1,265 @@
+/*
+ * 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 "filesys.h"
+# include "pathsys.h"
+
+# ifdef OS_NT
+
+# ifdef __BORLANDC__
+# if __BORLANDC__ < 0x550
+# include
+# include
+# endif
+# undef PATHNAME /* cpp namespace collision */
+# define _finddata_t ffblk
+# endif
+
+# include
+# include
+
+/*
+ * 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 )
+{
+ PATHNAME f;
+ char filespec[ MAXJPATH ];
+ char filename[ MAXJPATH ];
+ long handle;
+ int ret;
+ struct _finddata_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 */
+
+ 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 );
+
+ /* Now enter contents of directory */
+
+ sprintf( filespec, "%s/*", dir );
+
+ if( DEBUG_BINDSCAN )
+ printf( "scan directory %s\n", dir );
+
+# if defined(__BORLANDC__) && __BORLANDC__ < 0x550
+ if ( ret = findfirst( filespec, finfo, FA_NORMAL | FA_DIREC ) )
+ return;
+
+ while( !ret )
+ {
+ time_t time_write = finfo->ff_fdate;
+
+ time_write = (time_write << 16) | finfo->ff_ftime;
+ f.f_base.ptr = finfo->ff_name;
+ f.f_base.len = strlen( finfo->ff_name );
+
+ path_build( &f, filename );
+
+ (*func)( closure, filename, 1 /* stat()'ed */, time_write );
+
+ ret = findnext( finfo );
+ }
+# else
+ handle = _findfirst( filespec, finfo );
+
+ if( ret = ( handle < 0L ) )
+ return;
+
+ while( !ret )
+ {
+ f.f_base.ptr = finfo->name;
+ f.f_base.len = strlen( finfo->name );
+
+ path_build( &f, filename, 0 );
+
+ (*func)( closure, filename, 1 /* stat()'ed */, finfo->time_write );
+
+ ret = _findnext( handle, finfo );
+ }
+
+ _findclose( handle );
+# endif
+
+}
+
+/*
+ * file_time() - get timestamp of file, if not done by file_dirscan()
+ */
+
+int
+file_time(
+ char *filename,
+ time_t *time )
+{
+ /* On NT this is called only for C:/ */
+
+ struct stat statbuf;
+
+ if( stat( filename, &statbuf ) < 0 )
+ return -1;
+
+ *time = statbuf.st_mtime;
+
+ return 0;
+}
+
+/*
+ * file_archscan() - scan an archive for files
+ */
+
+/* Straight from SunOS */
+
+#define ARMAG "!\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 = malloc(lar_size);
+ if (read(fd, string_table, lar_size) != lar_size)
+ printf("error reading string table\n");
+ 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 );
+ endname = name + strlen( name );
+ }
+ else
+ {
+ /* normal name */
+ name = ar_hdr.ar_name;
+ endname = name + sizeof( ar_hdr.ar_name );
+ }
+
+ /* strip trailing space, slashes, and backslashes */
+
+ while( endname-- > name )
+ if( *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/src/tools/jam/fileos2.c b/src/tools/jam/fileos2.c
new file mode 100644
index 0000000000..bcf9fe6b58
--- /dev/null
+++ b/src/tools/jam/fileos2.c
@@ -0,0 +1,131 @@
+/*
+ * 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 "filesys.h"
+# include "pathsys.h"
+
+# ifdef OS_OS2
+
+# include
+# include
+
+/*
+ * 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;
+ char filespec[ MAXJPATH ];
+ char filename[ MAXJPATH ];
+ 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 */
+
+ strcpy( 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
+ strcat( filespec, "/" );
+
+ strcat( filespec, "*" );
+
+ /* Now enter contents of directory */
+
+ if( DEBUG_BINDSCAN )
+ printf( "scan directory %s\n", filespec );
+
+ /* 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, _A_NORMAL|_A_RDONLY|_A_SUBDIR, finfo ) )
+ {
+ do
+ {
+ f.f_base.ptr = finfo->name;
+ f.f_base.len = strlen( finfo->name );
+
+ path_build( &f, filename, 0 );
+
+ (*func)( closure, filename, 0 /* not stat()'ed */, (time_t)0 );
+ }
+ while( !_dos_findnext( finfo ) );
+ }
+
+}
+
+/*
+ * 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 */
+
diff --git a/src/tools/jam/filesys.h b/src/tools/jam/filesys.h
new file mode 100644
index 0000000000..a99a0b5502
--- /dev/null
+++ b/src/tools/jam/filesys.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * filesys.h - OS specific file routines
+ */
+
+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 );
diff --git a/src/tools/jam/fileunix.c b/src/tools/jam/fileunix.c
new file mode 100644
index 0000000000..406d31fecb
--- /dev/null
+++ b/src/tools/jam/fileunix.c
@@ -0,0 +1,331 @@
+/*
+ * 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 "filesys.h"
+# include "pathsys.h"
+
+# ifdef USE_FILEUNIX
+
+# if defined( OS_SEQUENT ) || \
+ defined( OS_DGUX ) || \
+ defined( OS_SCO ) || \
+ defined( OS_ISC )
+# define PORTAR 1
+# endif
+
+# if defined( OS_RHAPSODY ) || \
+ defined( OS_MACOSX ) || \
+ defined( OS_NEXT )
+/* need unistd for rhapsody's proper lseek */
+# include
+# include
+# define STRUCT_DIRENT struct direct
+# else
+# include
+# define STRUCT_DIRENT struct dirent
+# endif
+
+# ifdef OS_COHERENT
+# include
+# define HAVE_AR
+# endif
+
+# if defined( OS_MVS ) || \
+ defined( OS_INTERIX ) || defined(OS_AIX)
+
+#define ARMAG "!\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
+# include
+# 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 )
+{
+ PATHNAME f;
+ DIR *d;
+ STRUCT_DIRENT *dirent;
+ char filename[ MAXJPATH ];
+
+ /* 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 / : 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 */
+
+ if( !( d = opendir( dir ) ) )
+ return;
+
+ if( DEBUG_BINDSCAN )
+ printf( "scan directory %s\n", dir );
+
+ while( dirent = readdir( d ) )
+ {
+# 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 );
+
+ path_build( &f, filename, 0 );
+
+ (*func)( closure, filename, 0 /* not stat()'ed */, (time_t)0 );
+ }
+
+ closedir( d );
+}
+
+/*
+ * 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;
+}
+
+/*
+ * 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 ) )
+ {
+ char lar_name[256];
+ long lar_date;
+ long lar_size;
+ long lar_offset;
+ char *c;
+ char *src, *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 *)malloc(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)
+ free(string_table);
+
+ close( fd );
+
+# endif /* NO_AR */
+
+}
+
+# else /* AIAMAG - RS6000 AIX */
+
+void
+file_archscan(
+ char *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;
+ int fd;
+
+ if( ( fd = open( archive, O_RDONLY, 0 ) ) < 0 )
+ return;
+
+ if( read( fd, (char *)&fl_hdr, FL_HSZ ) != FL_HSZ ||
+ strncmp( AIAMAG, fl_hdr.fl_magic, SAIAMAG ) )
+ {
+ close( fd );
+ 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 );
+ }
+
+ close( fd );
+}
+
+# endif /* AIAMAG - RS6000 AIX */
+
+# endif /* USE_FILEUNIX */
+
diff --git a/src/tools/jam/filevms.c b/src/tools/jam/filevms.c
new file mode 100644
index 0000000000..dc2fe4c114
--- /dev/null
+++ b/src/tools/jam/filevms.c
@@ -0,0 +1,318 @@
+/*
+ * 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 "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
+# include
+# include
+# include
+# include
+# include
+# include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* Supply missing prototypes for lbr$-routines*/
+
+extern "C" {
+
+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 ** );
+
+}
+
+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];
+ char filename2[256];
+ 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 */ );
+ }
+
+ 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;
+ }
+
+ 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, 1 /* time valid */, time );
+ }
+}
+
+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/src/tools/jam/glob.c b/src/tools/jam/glob.c
new file mode 100644
index 0000000000..abb56eb1be
--- /dev/null
+++ b/src/tools/jam/glob.c
@@ -0,0 +1,157 @@
+/*
+ * 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(
+ register char *c,
+ register 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;
+ else 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;
+ }
+
+ /* Don't include \0 in either $[chars] or $[^chars] */
+
+ b[0] &= 0376;
+}
diff --git a/src/tools/jam/hash.c b/src/tools/jam/hash.c
new file mode 100644
index 0000000000..1d11efa22b
--- /dev/null
+++ b/src/tools/jam/hash.c
@@ -0,0 +1,259 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+# include "jam.h"
+# include "hash.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
+ */
+
+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;
+ int keyval; /* for quick comparisons */
+} ;
+
+/* This structure overlays the one handed to hashenter(). */
+/* It's 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 ] */
+ 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 );
+
+/*
+ * hashitem() - find a record in the table, and optionally enter a new one
+ */
+
+int
+hashitem(
+ register struct hash *hp,
+ HASHDATA **data,
+ int enter )
+{
+ ITEM **base;
+ register ITEM *i;
+ char *b = (*data)->key;
+ int keyval;
+
+ if( enter && !hp->items.more )
+ hashrehash( hp );
+
+ if( !enter && !hp->items.nel )
+ return 0;
+
+ keyval = *b;
+
+ while( *b )
+ keyval = keyval * 2147059363 + *b++;
+
+ keyval &= 0x7FFFFFFF;
+
+ base = hp->tab.base + ( keyval % hp->tab.nel );
+
+ for( i = *base; i; i = i->hdr.next )
+ if( keyval == i->hdr.keyval &&
+ !strcmp( i->data.key, (*data)->key ) )
+ {
+ *data = &i->data;
+ return !0;
+ }
+
+ if( enter )
+ {
+ 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;
+ }
+
+ 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 *)malloc( hp->items.more * hp->items.size );
+
+ 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 )
+ free( (char *)hp->tab.base );
+
+ hp->tab.nel = hp->items.nel * hp->bloat;
+ hp->tab.base = (ITEM **)malloc( 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;
+
+ i->hdr.next = *ip;
+ *ip = i;
+ }
+ }
+}
+
+/* --- */
+
+# 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 *)malloc( sizeof( *hp ) );
+
+ hp->bloat = 3;
+ hp->tab.nel = 0;
+ hp->tab.base = (ITEM **)0;
+ hp->items.more = 0;
+ hp->items.datalen = datalen;
+ hp->items.size = sizeof( struct hashhdr ) + ALIGNED( datalen );
+ hp->items.list = -1;
+ hp->items.nel = 0;
+ hp->inel = 11;
+ 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 )
+ hashstat( hp );
+
+ if( hp->tab.base )
+ free( (char *)hp->tab.base );
+ for( i = 0; i <= hp->items.list; i++ )
+ free( hp->items.lists[i].base );
+ free( (char *)hp );
+}
+
+/* ---- */
+
+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, 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+%dK) items+table+hash, %f density\n",
+ hp->name,
+ count,
+ hp->items.nel,
+ hp->tab.nel,
+ hp->items.nel * hp->items.size / 1024,
+ hp->tab.nel * sizeof( ITEM ** ) / 1024,
+ (float)count / (float)sets );
+}
diff --git a/src/tools/jam/hash.h b/src/tools/jam/hash.h
new file mode 100644
index 0000000000..65af38c323
--- /dev/null
+++ b/src/tools/jam/hash.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * hash.h - simple in-memory hashing routines
+ */
+
+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 );
+
+# define hashenter( hp, data ) !hashitem( hp, data, !0 )
+# define hashcheck( hp, data ) hashitem( hp, data, 0 )
diff --git a/src/tools/jam/headers.c b/src/tools/jam/headers.c
new file mode 100644
index 0000000000..1c087f056a
--- /dev/null
+++ b/src/tools/jam/headers.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright 1993, 2000 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+# 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 "newstr.h"
+
+/*
+ * 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) : ;
+ *
+ * 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.
+ */
+
+static LIST *headers1( LIST *l, char *file, int rec, regexp *re[] );
+
+/*
+ * headers() - scan a target for include files and call HDRRULE
+ */
+
+# define MAXINC 10
+
+void
+headers( TARGET *t )
+{
+ LIST *hdrscan;
+ LIST *hdrrule;
+ LIST *headlist = 0;
+ LOL lol;
+ 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++] = regcomp( hdrscan->string );
+ hdrscan = list_next( hdrscan );
+ }
+
+ /* Doctor up call to HDRRULE rule */
+ /* Call headers1() to get LIST of included files. */
+
+ lol_init( &lol );
+ lol_add( &lol, list_new( L0, t->name ) );
+ lol_add( &lol, headers1( headlist, t->boundname, rec, re ) );
+
+ if( lol_get( &lol, 1 ) )
+ list_free( evaluate_rule( hdrrule->string, &lol, L0 ) );
+
+ /* Clean up */
+
+ lol_free( &lol );
+
+ while( rec )
+ free( (char *)re[--rec] );
+}
+
+/*
+ * headers1() - using regexp, scan a file and build include LIST
+ */
+
+static LIST *
+headers1(
+ LIST *l,
+ char *file,
+ int rec,
+ regexp *re[] )
+{
+ FILE *f;
+ char buf[ 1024 ];
+ int i;
+
+ if( !( f = fopen( file, "r" ) ) )
+ return l;
+
+ while( fgets( buf, sizeof( buf ), f ) )
+ {
+ 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] ) );
+ }
+ }
+
+ fclose( f );
+
+ return l;
+}
+
+void
+regerror( char *s )
+{
+ printf( "re error %s\n", s );
+}
diff --git a/src/tools/jam/headers.h b/src/tools/jam/headers.h
new file mode 100644
index 0000000000..8c33735c60
--- /dev/null
+++ b/src/tools/jam/headers.h
@@ -0,0 +1,11 @@
+/*
+ * 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 );
diff --git a/src/tools/jam/jam.c b/src/tools/jam/jam.c
new file mode 100644
index 0000000000..42a393fbe7
--- /dev/null
+++ b/src/tools/jam/jam.c
@@ -0,0 +1,365 @@
+/*
+ * /+\
+ * +\ 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.
+ */
+
+/*
+ * 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
+ * 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"
+
+/* Macintosh is "special" */
+
+# ifdef OS_MAC
+# include
+# endif
+
+/* And UNIX for this */
+
+# ifdef unix
+# include
+# endif
+
+struct globs globs = {
+ 0, /* noexec */
+ 1, /* jobs */
+ 0, /* quitquick */
+ 0, /* newestfirst */
+# ifdef OS_MAC
+ { 0, 0 }, /* debug - suppress tracing output */
+# else
+ { 0, 1 }, /* debug ... */
+# endif
+ 0 /* output commands, not run them */
+} ;
+
+/* 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
+
+# ifndef use_environ
+# define use_environ environ
+# if !defined( __WATCOM__ ) && !defined( OS_OS2 ) && !defined( OS_NT )
+extern char **environ;
+# endif
+# endif
+
+main( int argc, char **argv, char **arg_environ )
+{
+ int n;
+ char *s;
+ struct option optv[N_OPTS];
+ char *all = "all";
+ int anyhow = 0;
+ int status;
+
+# ifdef OS_MAC
+ InitGraf(&qd.thePort);
+# endif
+
+ argc--, argv++;
+
+ if( ( n = getoptions( argc, argv, "d:j:f:gs:t:ano:qv", optv ) ) < 0 )
+ {
+ printf( "\nusage: jam [ options ] targets...\n\n" );
+
+ 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( "-n Don't actually execute the updating actions.\n" );
+ printf( "-ox Write the updating actions to file x.\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\n" );
+
+ exit( EXITBAD );
+ }
+
+ argc -= n, argv += n;
+
+ /* Version info. */
+
+ if( ( s = getoptval( optv, 'v', 0 ) ) )
+ {
+ printf( "Jam %s. %s. ", VERSION, OSMINOR );
+ printf( "Copyright 1993-2002 Christopher Seiwald.\n" );
+
+ return EXITOK;
+ }
+
+ /* Pick up interesting options */
+
+ if( ( s = getoptval( optv, 'n', 0 ) ) )
+ globs.noexec++, globs.debug[2] = 1;
+
+ 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( ( s = getoptval( optv, 'g', 0 ) ) )
+ globs.newestfirst = 1;
+
+ /* 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;
+ }
+
+ /* Set JAMDATE first */
+
+ {
+ char *date;
+ time_t clock;
+ time( &clock );
+ date = newstr( ctime( &clock ) );
+
+ /* Trim newline from date */
+
+ if( strlen( date ) == 25 )
+ date[ 24 ] = 0;
+
+ var_set( "JAMDATE", list_new( L0, newstr( date ) ), VAR_SET );
+ }
+
+ /* And 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 */
+
+ /*
+ * Jam defined variables OS, OSPLAT
+ */
+
+ var_defines( othersyms );
+
+ /* load up environment variables */
+
+ var_defines( use_environ );
+
+ /* 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 );
+ }
+
+ /* Initialize built-in rules */
+
+ load_builtins();
+
+ /* Parse ruleset */
+
+ for( n = 0; s = getoptval( optv, 'f', n ); n++ )
+ parse_file( s );
+
+ if( !n )
+ parse_file( "+" );
+
+ status = yyanyerrors();
+
+ /* Manually touch -t targets */
+
+ for( n = 0; s = getoptval( optv, 't', n ); n++ )
+ touchtarget( 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++;
+ }
+
+ /* Now make target */
+
+ if( !argc )
+ status |= make( 1, &all, anyhow );
+ else
+ status |= make( argc, argv, anyhow );
+
+ /* Widely scattered cleanup */
+
+ var_done();
+ donerules();
+ donestamps();
+ donestr();
+
+ /* close cmdout */
+
+ if( globs.cmdout )
+ fclose( globs.cmdout );
+
+ return status ? EXITBAD : EXITOK;
+}
diff --git a/src/tools/jam/jam.h b/src/tools/jam/jam.h
new file mode 100644
index 0000000000..86be7a5015
--- /dev/null
+++ b/src/tools/jam/jam.h
@@ -0,0 +1,507 @@
+/*
+ * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * 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.
+ */
+
+/*
+ * VMS, OPENVMS
+ */
+
+# ifdef VMS
+
+# define unlink remove
+
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+
+# 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
+
+/* Do any of these work? */
+# if defined( VAX ) || defined( __VAX ) || defined( vax )
+# define OSPLAT "OSPLAT=VAX"
+# endif
+
+# endif
+
+/*
+ * Windows NT
+ */
+
+# ifdef NT
+
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+
+# define OSMAJOR "NT=true"
+# define OSMINOR "OS=NT"
+# define OS_NT
+# define SPLITPATH ';'
+# define MAXLINE 996 /* longest 'together' actions */
+# define USE_EXECUNIX
+# 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
+
+# endif
+
+/*
+ * Windows MingW32
+ */
+
+# ifdef MINGW
+
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+
+# 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
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+
+# 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
+
+# endif
+
+/*
+ * Macintosh MPW
+ */
+
+# ifdef macintosh
+
+# include
+# include
+# include
+# include
+
+# define OSMAJOR "MAC=true"
+# define OSMINOR "OS=MAC"
+# define OS_MAC
+# define SPLITPATH ','
+# define DOWNSHIFT_PATHS
+
+# 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 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
+# ifdef __cygwin__
+# define OSMINOR "OS=CYGWIN"
+# define OS_CYGWIN
+# endif
+# ifdef __FreeBSD__
+# define OSMINOR "OS=FREEBSD"
+# define OS_FREEBSD
+# 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__
+# ifdef __QNXNTO__
+# define OSMINOR "OS=QNXNTO"
+# define OS_QNXNTO
+# else
+# define unix
+# 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__
+# 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
+# ifndef OSMINOR
+# define OSMINOR "OS=UNKNOWN"
+# endif
+
+/* All the UNIX includes */
+
+# include
+# include
+
+# ifndef OS_MPEIX
+# include
+# endif
+
+# include
+# include
+# include
+# include
+# include
+# include
+
+# ifndef OS_QNX
+# include
+# endif
+
+# ifndef OS_ULTRIX
+# include
+# endif
+
+# if !defined(OS_BSDI) && \
+ !defined(OS_FREEBSD) && \
+ !defined(OS_NEXT) && \
+ !defined(OS_MACHTEN) && \
+ !defined(OS_MACOSX) && \
+ !defined(OS_RHAPSODY) && \
+ !defined(OS_MVS)
+# include
+# endif
+
+# endif
+
+/*
+ * OSPLAT definitions - suppressed when it's 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( _M_IX86 )
+# if !defined( OS_FREEBSD ) && \
+ !defined( OS_OS2 ) && \
+ !defined( OS_AS400 )
+# define OSPLAT "OSPLAT=X86"
+# endif
+# endif
+
+# ifdef __sparc__
+# if !defined( OS_SUNOS ) && \
+ !defined( OS_SOLARIS )
+# define OSPLAT "OSPLAT=SPARC"
+# endif
+# endif
+
+# ifdef __mips__
+# if !defined( OS_SGI )
+# define OSPLAT "OSPLAT=MIPS"
+# endif
+# endif
+
+# ifdef __arm__
+# define OSPLAT "OSPLAT=ARM"
+# endif
+
+# if defined( __ia64__ ) || \
+ defined( __IA64__ ) || \
+ defined( _M_IA64 )
+# define OSPLAT "OSPLAT=IA64"
+# endif
+
+# ifdef __s390__
+# define OSPLAT "OSPLAT=390"
+# endif
+
+# ifndef OSPLAT
+# define OSPLAT ""
+# endif
+
+/*
+ * Jam implementation misc.
+ */
+
+# ifndef MAXLINE
+# define MAXLINE 10240 /* longest 'together' actions' */
+# endif
+
+# ifndef EXITOK
+# define EXITOK 0
+# define EXITBAD 1
+# endif
+
+# ifndef SPLITPATH
+# define SPLITPATH ':'
+# endif
+
+/* You probably don't need to muck with these. */
+
+# define MAXSYM 1024 /* longest symbol in the environment */
+# define MAXJPATH 1024 /* longest filename */
+
+# define MAXJOBS 64 /* silently enforce -j limit */
+# define MAXARGC 32 /* words in $(JAMSHELL) */
+
+/* Jam private definitions below. */
+
+# define DEBUG_MAX 10
+
+struct globs {
+ int noexec;
+ int jobs;
+ int quitquick;
+ int newestfirst; /* build newest sources first */
+ char debug[DEBUG_MAX];
+ FILE *cmdout; /* print cmds, not run them */
+} ;
+
+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 */
+
diff --git a/src/tools/jam/jambase.c b/src/tools/jam/jambase.c
new file mode 100644
index 0000000000..ffa2cbac73
--- /dev/null
+++ b/src/tools/jam/jambase.c
@@ -0,0 +1,1219 @@
+/* Generated by mkjambase from Jambase */
+char *jambase[] = {
+/* Jambase */
+"if $(NT)\n",
+"{\n",
+"MV ?= move /y ;\n",
+"CP ?= copy ;\n",
+"RM ?= del /f/q ;\n",
+"SLASH ?= \\\\ ;\n",
+"SUFLIB ?= .lib ;\n",
+"SUFOBJ ?= .obj ;\n",
+"SUFEXE ?= .exe ;\n",
+"if $(BCCROOT)\n",
+"{\n",
+"Echo \"Compiler is Borland C++\" ;\n",
+"AR ?= tlib /C /P64 ;\n",
+"CC ?= bcc32 ;\n",
+"CCFLAGS ?= -v -w- -q -DWIN -tWR -tWM -tWC ;\n",
+"C++ ?= $(CC) ;\n",
+"C++FLAGS ?= $(CCFLAGS) -P ;\n",
+"LINK ?= $(CC) ;\n",
+"LINKFLAGS ?= $(CCFLAGS) ;\n",
+"STDLIBPATH ?= $(BCCROOT)\\\\lib ;\n",
+"STDHDRS ?= $(BCCROOT)\\\\include ;\n",
+"NOARSCAN ?= true ;\n",
+"}\n",
+"else if $(MSVC)\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",
+"$(MSVC)\\\\lib\\\\mlibce.lib\n",
+"$(MSVC)\\\\lib\\\\oldnames.lib\n",
+";\n",
+"LINKLIBS ?= ;\n",
+"NOARSCAN ?= true ;\n",
+"OPTIM ?= ;\n",
+"STDHDRS ?= $(MSVC)\\\\include ;\n",
+"UNDEFFLAG ?= \"/u _\" ;\n",
+"}\n",
+"else if $(MSVCNT)\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 ?= $(MSVCNT)\\\\lib\\\\advapi32.lib\n",
+"$(MSVCNT)\\\\lib\\\\libc.lib\n",
+"$(MSVCNT)\\\\lib\\\\oldnames.lib\n",
+"$(MSVCNT)\\\\lib\\\\kernel32.lib ;\n",
+"OPTIM ?= \"\" ;\n",
+"STDHDRS ?= $(MSVCNT)\\\\include ;\n",
+"UNDEFFLAG ?= \"/u _\" ;\n",
+"}\n",
+"else\n",
+"{\n",
+"Exit On NT, set BCCROOT, MSVCNT, or MSVC to the root of the\n",
+"Borland or Microsoft directories. ;\n",
+"}\n",
+"}\n",
+"else if $(MINGW)\n",
+"{\n",
+"Echo \"MingW32\" ;\n",
+"CC ?= gcc ;\n",
+"C++ ?= g++ ;\n",
+"CCFLAGS += -DMINGW ;\n",
+"RANLIB ?= \"ranlib\" ;\n",
+"SUFEXE ?= .exe ;\n",
+"}\n",
+"else if $(OS2)\n",
+"{\n",
+"WATCOM ?= $(watcom) ;\n",
+"if ! $(WATCOM)\n",
+"{\n",
+"Exit On OS2, set WATCOM to the root of the Watcom directory. ;\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",
+"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 ;\n",
+"SUFEXE ?= .exe ;\n",
+"SUFLIB ?= .lib ;\n",
+"SUFOBJ ?= .obj ;\n",
+"UNDEFFLAG ?= \"/u _\" ;\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",
+"JAMSHELL ?= sh -c ;\n",
+"RANLIB ?= \"\" ;\n",
+"SUFEXE ?= .exe ;\n",
+"YACC ?= bison -y ;\n",
+"case DGUX :\n",
+"RANLIB ?= \"\" ;\n",
+"RELOCATE ?= true ;\n",
+"case HPUX :\n",
+"RANLIB ?= \"\" ;\n",
+"case INTERIX :\n",
+"CC ?= gcc ;\n",
+"JAMSHELL ?= sh -c ;\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",
+"YACCGEN ?= .c ;\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",
+"YACCGEN ?= ;\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",
+"ASHDRS on $(<) = [ FIncludes $(SEARCH_SOURCE) $(SUBDIRHDRS) $(HDRS) ] ;\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",
+"Depends $(<) : $(>) ;\n",
+"if $(RELOCATE)\n",
+"{\n",
+"CcMv $(<) : $(>) ;\n",
+"}\n",
+"CCFLAGS on $(<) += $(CCFLAGS) $(SUBDIRCCFLAGS) $(OPTIM) ;\n",
+"CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ;\n",
+"CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ;\n",
+"}\n",
+"rule C++\n",
+"{\n",
+"Depends $(<) : $(>) ;\n",
+"if $(RELOCATE)\n",
+"{\n",
+"CcMv $(<) : $(>) ;\n",
+"}\n",
+"C++FLAGS on $(<) += $(C++FLAGS) $(SUBDIRC++FLAGS) $(OPTIM) ;\n",
+"CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ;\n",
+"CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ;\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 HdrRule\n",
+"{\n",
+"local s = $(>:G=$(HDRGRIST:E)) ;\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=$(INSTALLGRIST)) = $(EXEMODE) ;\n",
+"}\n",
+"rule InstallFile\n",
+"{\n",
+"InstallInto $(<) : $(>) ;\n",
+"MODE on $(>:G=$(INSTALLGRIST)) = $(FILEMODE) ;\n",
+"}\n",
+"rule InstallLib\n",
+"{\n",
+"InstallInto $(<) : $(>) ;\n",
+"MODE on $(>:G=$(INSTALLGRIST)) = $(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=$(INSTALLGRIST)) = $(FILEMODE) ;\n",
+"}\n",
+"rule InstallShell\n",
+"{\n",
+"InstallInto $(<) : $(>) ;\n",
+"MODE on $(>:G=$(INSTALLGRIST)) = $(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",
+"Clean clean : $(<) ;\n",
+"MakeLocate $(<) : $(LOCATE_TARGET) ;\n",
+"SEARCH on $(>) = $(SEARCH_SOURCE) ;\n",
+"HDRS on $(<) = $(SEARCH_SOURCE) $(SUBDIRHDRS) $(HDRS) ;\n",
+"HDRRULE on $(>) = HdrRule ;\n",
+"HDRSCAN on $(>) = $(HDRPATTERN) ;\n",
+"HDRSEARCH on $(>) = \n",
+"$(SEARCH_SOURCE:E) $(SUBDIRHDRS) $(HDRS) $(STDHDRS) ;\n",
+"HDRGRIST on $(>) = $(HDRGRIST) ;\n",
+"DEFINES on $(<) += $(DEFINES) ;\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=$(YACCGEN)) ;\n",
+"Yacc $(<:S=$(YACCGEN)) : $(>) ;\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 ObjectDefines\n",
+"{\n",
+"local s = [ FGristFiles $(<:S=$(SUFOBJ)) ] ;\n",
+"DEFINES on $(s) += $(>) ;\n",
+"CCDEFS on $(s) = [ on $(s) FDefines $(DEFINES) ] ;\n",
+"}\n",
+"rule ObjectHdrs\n",
+"{\n",
+"local s = [ FGristFiles $(<:S=$(SUFOBJ)) ] ;\n",
+"HDRS on $(s) += $(>) ;\n",
+"CCHDRS on $(s) = [ on $(s) FIncludes $(HDRS) ] ;\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 SoftLink\n",
+"{\n",
+"Depends files : $(<) ;\n",
+"Depends $(<) : $(>) ;\n",
+"SEARCH on $(>) = $(SEARCH_SOURCE) ;\n",
+"Clean clean : $(<) ;\n",
+"}\n",
+"rule SubDir\n",
+"{\n",
+"local _top = $(<[1]) ;\n",
+"if ! $($(_top))\n",
+"{\n",
+"if ! $(_top)\n",
+"{\n",
+"Exit SubDir syntax error ;\n",
+"}\n",
+"$(_top) = [ FSubDir $(<[2-]) ] ;\n",
+"}\n",
+"if ! $($(_top)-included)\n",
+"{\n",
+"$(_top)-included = TRUE ;\n",
+"include $($(_top)RULES:E=$(JAMRULES:R=$($(_top)))) ;\n",
+"}\n",
+"local _s = [ FDirName $(<[2-]) ] ;\n",
+"SUBDIR = $(_s:R=$($(_top))) ;\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 += [ FDirName $(<) ] ;\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",
+"return $(<:J=!) ;\n",
+"}\n",
+"rule FGristFiles \n",
+"{\n",
+"return $(<:G=$(SOURCE_GRIST:E)) ;\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 FSubDir\n",
+"{\n",
+"if ! $(<[1]) \n",
+"{\n",
+"return $(DOT) ;\n",
+"} \n",
+"else\n",
+"{\n",
+"local _i _d ;\n",
+"_d = $(DOTDOT) ;\n",
+"for _i in $(<[2-])\n",
+"{\n",
+"_d = $(_d:R=$(DOTDOT)) ;\n",
+"}\n",
+"return $(_d) ;\n",
+"}\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 FQuote { return \\\\\\\"$(<)\\\\\\\" ; }\n",
+"rule FDefines { return -D$(<) ; }\n",
+"rule FIncludes { return -I$(<) ; }\n",
+"rule FDirName\n",
+"{\n",
+"local _i ;\n",
+"local _s = $(DOT) ;\n",
+"for _i in $(<)\n",
+"{\n",
+"_s = $(_i:R=$(_s)) ;\n",
+"}\n",
+"return $(_s) ;\n",
+"}\n",
+"if $(OS2)\n",
+"{\n",
+"rule FQuote { return \\\"$(<)\\\" ; }\n",
+"rule FIncludes { return /I$(<) ; }\n",
+"}\n",
+"else if $(NT)\n",
+"{\n",
+"rule FDefines { return /D$(<) ; }\n",
+"rule FIncludes { return /I$(<) ; }\n",
+"}\n",
+"else if $(MAC)\n",
+"{\n",
+"rule FQuote { return \\\"$(<)\\\" ; }\n",
+"rule FDefines { return \"-define '$(<)'\" ; }\n",
+"rule FIncludes { return \\\"$(<:J=,)\\\" ; }\n",
+"}\n",
+"else if $(VMS)\n",
+"{\n",
+"rule FQuote { return \\\"\\\"\\\"$(<)\\\"\\\"\\\" ; }\n",
+"rule FDefines { return \"/define=( $(<:J=,) )\" ; }\n",
+"rule FIncludes { return \"/inc=( $(<:J=,) )\" ; }\n",
+"rule FDirName\n",
+"{\n",
+"local _s _i ;\n",
+"if ! $(<)\n",
+"{\n",
+"_s = $(DOT) ;\n",
+"}\n",
+"else \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",
+"return $(_s) ;\n",
+"}\n",
+"}\n",
+"actions updated together piecemeal Archive\n",
+"{\n",
+"$(AR) $(<) $(>)\n",
+"}\n",
+"actions As\n",
+"{\n",
+"$(AS) $(ASFLAGS) $(ASHDRS) -o $(<) $(>)\n",
+"}\n",
+"actions C++\n",
+"{\n",
+"$(C++) -c -o $(<) $(C++FLAGS) $(CCDEFS) $(CCHDRS) $(>)\n",
+"}\n",
+"actions Cc\n",
+"{\n",
+"$(CC) -c -o $(<) $(CCFLAGS) $(CCDEFS) $(CCHDRS) $(>)\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 SoftLink\n",
+"{\n",
+"$(RM) $(<) && $(LN) -s $(>) $(<)\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) $(CCDEFS) $(CCHDRS) $(>)\n",
+"}\n",
+"actions Cc\n",
+"{\n",
+"$(CC) -c $(CCFLAGS) $(CCDEFS) $(CCHDRS) $(>)\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) && $(MSVCNT)\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 /Fo$(<) $(CCFLAGS) $(CCDEFS) $(CCHDRS) /I$(STDHDRS) $(>)\n",
+"}\n",
+"actions C++\n",
+"{\n",
+"$(C++) /c /Fo$(<) $(C++FLAGS) $(CCDEFS) $(CCHDRS) /I$(STDHDRS) /Tp$(>)\n",
+"}\n",
+"actions Link bind NEEDLIBS\n",
+"{\n",
+"$(LINK) $(LINKFLAGS) /out:$(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)\n",
+"}\n",
+"}\n",
+"else if $(NT) && $(MSVC)\n",
+"{\n",
+"actions updated together piecemeal Archive\n",
+"{\n",
+"$(AR) $(<) -+$(>)\n",
+"}\n",
+"actions Cc\n",
+"{\n",
+"$(CC) /c /Fo$(<) $(CCFLAGS) $(CCDEFS) $(CCHDRS) $(>)\n",
+"}\n",
+"actions C++\n",
+"{\n",
+"$(C++) /c /Fo$(<) $(C++FLAGS) $(CCDEFS) $(CCHDRS) /Tp$(>)\n",
+"}\n",
+"actions Link bind NEEDLIBS\n",
+"{\n",
+"$(LINK) $(LINKFLAGS) /out:$(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)\n",
+"}\n",
+"}\n",
+"else if $(NT) && $(BCCROOT)\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 -o$(<) $(C++FLAGS) $(CCDEFS) $(CCHDRS) $(>)\n",
+"}\n",
+"actions Cc\n",
+"{\n",
+"$(CC) -c -o$(<) $(CCFLAGS) $(CCDEFS) $(CCHDRS) $(>)\n",
+"}\n",
+"}\n",
+"else if $(OS2) && $(WATCOM)\n",
+"{\n",
+"actions together piecemeal Archive\n",
+"{\n",
+"$(AR) $(<) +-$(>) \n",
+"}\n",
+"actions Cc\n",
+"{\n",
+"$(CC) /Fo=$(<) $(CCFLAGS) $(CCDEFS) $(CCHDRS) $(>)\n",
+"}\n",
+"actions C++\n",
+"{\n",
+"$(C++) /Fo=$(<) $(C++FLAGS) $(CCDEFS) $(CCHDRS) $(>)\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 $(VMS)\n",
+"{\n",
+"actions updated together piecemeal Archive \n",
+"{\n",
+"lib/replace $(<) $(>[1]) ,$(>[2-])\n",
+"}\n",
+"actions Cc\n",
+"{ \n",
+"$(CC)/obj=$(<) $(CCFLAGS) $(CCDEFS) $(CCHDRS) $(>) \n",
+"}\n",
+"actions C++\n",
+"{ \n",
+"$(C++)/obj=$(<) $(C++FLAGS) $(CCDEFS) $(CCHDRS) $(>) \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) $(>:J=,) ,$(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 $(CCHDRS)\n",
+"$(CC) -o $(<) $(CCFLAGS) $(CCDEFS) $(>) \n",
+"}\n",
+"actions C++\n",
+"{\n",
+"set -e MWCincludes $(CCHDRS)\n",
+"$(CC) -o $(<) $(C++FLAGS) $(CCDEFS) $(>) \n",
+"}\n",
+"actions Link bind NEEDLIBS\n",
+"{\n",
+"$(LINK) -o $(<) $(LINKFLAGS) $(>) $(NEEDLIBS) \"$(LINKLIBS)\"\n",
+"}\n",
+"}\n",
+"if $(WIN98)\n",
+"{\n",
+"actions existing Clean\n",
+"{\n",
+"del $(>)\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 makeCommon { _makeCommon $(<) : $(>) ; }\n",
+"rule makeDirName { $(<) = [ FDirName $(>) ] ; }\n",
+"rule makeGrist { $(<) = [ FGrist $(>) ] ; }\n",
+"rule makeGristedName { $(<) = [ FGristSourceFiles $(>) ] ; }\n",
+"rule makeRelPath { $(<[1]) = [ FRelPath $(<[2-]) : $(>) ] ; }\n",
+"rule makeString { $(<) = $(>:J) ; }\n",
+"rule makeSubDir { $(<) = [ FSubDir $(>) ] ; }\n",
+"rule makeSuffixed { $(<[1]) = [ FAppendSuffix $(>) : $(<[2]) ] ; }\n",
+"include $(JAMFILE) ;\n",
+0 };
diff --git a/src/tools/jam/jambase.h b/src/tools/jam/jambase.h
new file mode 100644
index 0000000000..75393f4cc5
--- /dev/null
+++ b/src/tools/jam/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/src/tools/jam/jamgram.c b/src/tools/jam/jamgram.c
new file mode 100644
index 0000000000..c77e6baf59
--- /dev/null
+++ b/src/tools/jam/jamgram.c
@@ -0,0 +1,876 @@
+#ifndef lint
+static char const
+yyrcsid[] = "$FreeBSD: src/usr.bin/yacc/skeleton.c,v 1.28 2000/01/17 02:04:06 bde Exp $";
+#endif
+#include
+#define YYBYACC 1
+#define YYMAJOR 1
+#define YYMINOR 9
+#define YYLEX yylex()
+#define YYEMPTY -1
+#define yyclearin (yychar=(YYEMPTY))
+#define yyerrok (yyerrflag=0)
+#define YYRECOVERING() (yyerrflag!=0)
+static int yygrowstack();
+#define YYPREFIX "yy"
+#line 89 "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 *, LOL *))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 ) parse_make( compile_foreach,l,r,P0,s,S0,0 )
+# 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 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( a,p ) parse_make( compile_rule,a,p,P0,S0,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 ) parse_make( compile_setcomp,p,P0,P0,s,S0,0 )
+# 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 )
+
+#line 54 "y.tab.c"
+#define YYERRCODE 256
+#define _BANG_t 257
+#define _BANG_EQUALS_t 258
+#define _AMPER_t 259
+#define _AMPERAMPER_t 260
+#define _LPAREN_t 261
+#define _RPAREN_t 262
+#define _PLUS_EQUALS_t 263
+#define _COLON_t 264
+#define _SEMIC_t 265
+#define _LANGLE_t 266
+#define _LANGLE_EQUALS_t 267
+#define _EQUALS_t 268
+#define _RANGLE_t 269
+#define _RANGLE_EQUALS_t 270
+#define _QUESTION_EQUALS_t 271
+#define _LBRACKET_t 272
+#define _RBRACKET_t 273
+#define ACTIONS_t 274
+#define BIND_t 275
+#define CASE_t 276
+#define DEFAULT_t 277
+#define ELSE_t 278
+#define EXISTING_t 279
+#define FOR_t 280
+#define IF_t 281
+#define IGNORE_t 282
+#define IN_t 283
+#define INCLUDE_t 284
+#define LOCAL_t 285
+#define ON_t 286
+#define PIECEMEAL_t 287
+#define QUIETLY_t 288
+#define RETURN_t 289
+#define RULE_t 290
+#define SWITCH_t 291
+#define TOGETHER_t 292
+#define UPDATED_t 293
+#define WHILE_t 294
+#define _LBRACE_t 295
+#define _BAR_t 296
+#define _BARBAR_t 297
+#define _RBRACE_t 298
+#define ARG 299
+#define STRING 300
+const short yylhs[] = { -1,
+ 0, 0, 2, 2, 1, 1, 1, 1, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 12, 13, 3, 7, 7, 7, 7, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 8, 8, 14, 6, 6, 4, 15, 15,
+ 5, 17, 5, 16, 16, 16, 10, 10, 18, 18,
+ 18, 18, 18, 18, 11, 11,
+};
+const short yylen[] = { 2,
+ 0, 1, 0, 1, 1, 2, 4, 6, 3, 3,
+ 3, 4, 6, 3, 7, 5, 5, 7, 5, 3,
+ 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,
+};
+const short yydefred[] = { 0,
+ 52, 57, 0, 0, 49, 49, 0, 49, 0, 49,
+ 0, 0, 51, 0, 2, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 4, 0, 6, 26, 25, 27, 0, 49,
+ 0, 0, 49, 0, 49, 0, 64, 61, 63, 62,
+ 60, 59, 0, 58, 49, 41, 0, 49, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 10,
+ 50, 0, 49, 21, 14, 20, 0, 0, 9, 28,
+ 0, 49, 11, 0, 0, 54, 53, 49, 0, 0,
+ 42, 40, 0, 0, 0, 32, 33, 0, 34, 35,
+ 0, 0, 0, 7, 0, 0, 0, 0, 0, 49,
+ 47, 12, 49, 49, 66, 22, 0, 0, 0, 0,
+ 16, 44, 19, 0, 56, 55, 0, 0, 0, 8,
+ 0, 13, 23, 15, 18, 45, 0, 24,
+};
+const short yydgoto[] = { 14,
+ 33, 34, 16, 41, 23, 42, 43, 107, 24, 19,
+ 89, 127, 137, 108, 26, 46, 18, 54,
+};
+const short yysindex[] = { -44,
+ 0, 0, -278, -232, 0, 0, -191, 0, -273, 0,
+ -232, -44, 0, 0, 0, -44, -154, -245, -66, -252,
+ -232, -232, -250, -103, -228, -191, -249, 31, -206, 31,
+ -230, -90, 0, -219, 0, 0, 0, 0, -200, 0,
+ -194, -180, 0, -191, 0, -187, 0, 0, 0, 0,
+ 0, 0, -172, 0, 0, 0, -58, 0, -232, -232,
+ -232, -232, -232, -232, -232, -232, -44, -232, -232, 0,
+ 0, -44, 0, 0, 0, 0, -169, -44, 0, 0,
+ -48, 0, 0, -152, -233, 0, 0, 0, -179, -175,
+ 0, 0, -35, -1, -1, 0, 0, -35, 0, 0,
+ -176, -6, -6, 0, -141, -174, -161, -169, -150, 0,
+ 0, 0, 0, 0, 0, 0, -44, -148, -44, -138,
+ 0, 0, 0, -91, 0, 0, -125, -115, 31, 0,
+ -44, 0, 0, 0, 0, 0, -113, 0,
+};
+const short yyrindex[] = { 186,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, -102, 0, 0, 0, 3, -74, 0, 0, 0,
+ 0, 0, -108, 0, 0, -124, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ -223, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, -100, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, -102, 0, 0, 0,
+ 0, 4, 0, 0, 0, 0, -101, -102, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, -224, -168, -78, 0, 0, -207, 0, 0,
+ 0, -213, -185, 0, 0, 0, 0, -101, 0, 0,
+ 0, 0, 0, 0, 0, 0, -102, 1, 4, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ -276, 0, 0, 0, 0, 0, 0, 0,
+};
+const short yygindex[] = { 0,
+ 8, -55, -23, 5, 2, -39, 118, 95, 36, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+};
+#define YYTABLESIZE 330
+const short yytable[] = { 3,
+ 17, 17, 5, 3, 74, 86, 76, 15, 28, 25,
+ 27, 101, 29, 17, 31, 72, 104, 17, 73, 45,
+ 20, 3, 109, 35, 21, 30, 1, 71, 22, 17,
+ 55, 17, 58, 31, 31, 31, 70, 31, 1, 1,
+ 44, 46, 111, 31, 81, 85, 32, 84, 38, 46,
+ 30, 30, 30, 13, 30, 113, 56, 57, 75, 90,
+ 30, 128, 92, 130, 77, 13, 13, 80, 17, 82,
+ 31, 31, 31, 17, 126, 136, 39, 105, 79, 17,
+ 1, 38, 38, 38, 83, 87, 114, 30, 30, 30,
+ 36, 36, 115, 36, 93, 94, 95, 96, 97, 98,
+ 99, 100, 88, 102, 103, 135, 106, 13, 36, 39,
+ 39, 39, 112, 37, 124, 116, 38, 125, 17, 117,
+ 17, 118, 39, 119, 120, 131, 36, 36, 36, 129,
+ 17, 40, 17, 48, 48, 48, 121, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 123, 48, 29,
+ 29, 29, 48, 29, 59, 60, 61, 29, 29, 29,
+ 29, 29, 62, 63, 64, 65, 66, 59, 60, 61,
+ 48, 48, 48, 132, 133, 62, 63, 64, 65, 66,
+ 37, 37, 134, 37, 138, 1, 29, 29, 29, 49,
+ 49, 67, 68, 69, 65, 3, 43, 49, 110, 59,
+ 60, 61, 122, 91, 78, 68, 69, 62, 63, 64,
+ 65, 66, 47, 0, 36, 48, 37, 37, 37, 37,
+ 49, 50, 38, 0, 49, 51, 52, 1, 39, 2,
+ 62, 63, 53, 65, 66, 3, 4, 68, 69, 5,
+ 6, 7, 0, 0, 8, 9, 10, 0, 0, 11,
+ 12, 59, 60, 61, 13, 0, 59, 0, 0, 62,
+ 63, 64, 65, 66, 62, 63, 64, 65, 66, 0,
+ 0, 0, 17, 0, 17, 0, 17, 0, 5, 3,
+ 17, 17, 0, 0, 17, 17, 17, 0, 0, 17,
+ 17, 17, 0, 0, 17, 17, 0, 0, 17, 17,
+ 5, 3, 1, 0, 2, 0, 0, 0, 0, 0,
+ 3, 4, 0, 0, 5, 0, 7, 0, 0, 8,
+ 9, 10, 0, 0, 11, 12, 0, 0, 0, 13,
+};
+const short yycheck[] = { 276,
+ 0, 0, 0, 0, 28, 45, 30, 0, 7, 5,
+ 6, 67, 8, 12, 10, 265, 72, 16, 268, 18,
+ 299, 298, 78, 16, 257, 299, 272, 26, 261, 28,
+ 283, 30, 283, 258, 259, 260, 265, 262, 272, 272,
+ 286, 265, 82, 268, 40, 44, 11, 43, 262, 273,
+ 258, 259, 260, 299, 262, 289, 21, 22, 265, 55,
+ 268, 117, 58, 119, 295, 299, 299, 268, 67, 264,
+ 295, 296, 297, 72, 114, 131, 262, 73, 298, 78,
+ 272, 295, 296, 297, 265, 273, 85, 295, 296, 297,
+ 259, 260, 88, 262, 59, 60, 61, 62, 63, 64,
+ 65, 66, 275, 68, 69, 129, 276, 299, 263, 295,
+ 296, 297, 265, 268, 110, 295, 271, 113, 117, 295,
+ 119, 298, 277, 265, 299, 264, 295, 296, 297, 278,
+ 129, 286, 131, 258, 259, 260, 298, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 298, 273, 258,
+ 259, 260, 277, 262, 258, 259, 260, 266, 267, 268,
+ 269, 270, 266, 267, 268, 269, 270, 258, 259, 260,
+ 295, 296, 297, 265, 300, 266, 267, 268, 269, 270,
+ 259, 260, 298, 262, 298, 0, 295, 296, 297, 264,
+ 265, 295, 296, 297, 295, 298, 298, 272, 81, 258,
+ 259, 260, 108, 262, 295, 296, 297, 266, 267, 268,
+ 269, 270, 279, -1, 263, 282, 295, 296, 297, 268,
+ 287, 288, 271, -1, 299, 292, 293, 272, 277, 274,
+ 266, 267, 299, 269, 270, 280, 281, 296, 297, 284,
+ 285, 286, -1, -1, 289, 290, 291, -1, -1, 294,
+ 295, 258, 259, 260, 299, -1, 258, -1, -1, 266,
+ 267, 268, 269, 270, 266, 267, 268, 269, 270, -1,
+ -1, -1, 272, -1, 274, -1, 276, -1, 276, 276,
+ 280, 281, -1, -1, 284, 285, 286, -1, -1, 289,
+ 290, 291, -1, -1, 294, 295, -1, -1, 298, 299,
+ 298, 298, 272, -1, 274, -1, -1, -1, -1, -1,
+ 280, 281, -1, -1, 284, -1, 286, -1, -1, 289,
+ 290, 291, -1, -1, 294, 295, -1, -1, -1, 299,
+};
+#define YYFINAL 14
+#ifndef YYDEBUG
+#define YYDEBUG 0
+#endif
+#define YYMAXTOKEN 300
+#if YYDEBUG
+const char * const yyname[] = {
+"end-of-file",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,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,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,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,0,0,0,0,"_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","DEFAULT_t","ELSE_t","EXISTING_t","FOR_t","IF_t","IGNORE_t",
+"IN_t","INCLUDE_t","LOCAL_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",
+};
+const char * const yyrule[] = {
+"$accept : run",
+"run :",
+"run : rules",
+"block :",
+"block : rules",
+"rules : rule",
+"rules : rule rules",
+"rules : LOCAL_t list _SEMIC_t block",
+"rules : LOCAL_t list _EQUALS_t list _SEMIC_t block",
+"rule : _LBRACE_t block _RBRACE_t",
+"rule : INCLUDE_t list _SEMIC_t",
+"rule : arg lol _SEMIC_t",
+"rule : arg assign list _SEMIC_t",
+"rule : arg ON_t list assign list _SEMIC_t",
+"rule : RETURN_t list _SEMIC_t",
+"rule : FOR_t ARG IN_t list _LBRACE_t block _RBRACE_t",
+"rule : SWITCH_t list _LBRACE_t cases _RBRACE_t",
+"rule : IF_t expr _LBRACE_t block _RBRACE_t",
+"rule : IF_t expr _LBRACE_t block _RBRACE_t ELSE_t rule",
+"rule : WHILE_t expr _LBRACE_t block _RBRACE_t",
+"rule : RULE_t ARG rule",
+"rule : ON_t arg rule",
+"$$1 :",
+"$$2 :",
+"rule : ACTIONS_t eflags ARG bindlist _LBRACE_t $$1 STRING $$2 _RBRACE_t",
+"assign : _EQUALS_t",
+"assign : _PLUS_EQUALS_t",
+"assign : _QUESTION_EQUALS_t",
+"assign : DEFAULT_t _EQUALS_t",
+"expr : arg",
+"expr : expr _EQUALS_t expr",
+"expr : expr _BANG_EQUALS_t expr",
+"expr : expr _LANGLE_t expr",
+"expr : expr _LANGLE_EQUALS_t expr",
+"expr : expr _RANGLE_t expr",
+"expr : expr _RANGLE_EQUALS_t expr",
+"expr : expr _AMPER_t expr",
+"expr : expr _AMPERAMPER_t expr",
+"expr : expr _BAR_t expr",
+"expr : expr _BARBAR_t expr",
+"expr : arg IN_t list",
+"expr : _BANG_t expr",
+"expr : _LPAREN_t expr _RPAREN_t",
+"cases :",
+"cases : case cases",
+"case : CASE_t ARG _COLON_t block",
+"lol : list",
+"lol : list _COLON_t lol",
+"list : listp",
+"listp :",
+"listp : listp arg",
+"arg : ARG",
+"$$3 :",
+"arg : _LBRACKET_t $$3 func _RBRACKET_t",
+"func : arg lol",
+"func : ON_t arg arg lol",
+"func : ON_t arg RETURN_t list",
+"eflags :",
+"eflags : eflags eflag",
+"eflag : UPDATED_t",
+"eflag : TOGETHER_t",
+"eflag : IGNORE_t",
+"eflag : QUIETLY_t",
+"eflag : PIECEMEAL_t",
+"eflag : EXISTING_t",
+"bindlist :",
+"bindlist : BIND_t list",
+};
+#endif
+#ifndef YYSTYPE
+typedef int YYSTYPE;
+#endif
+#if YYDEBUG
+#include
+#endif
+#ifdef YYSTACKSIZE
+#undef YYMAXDEPTH
+#define YYMAXDEPTH YYSTACKSIZE
+#else
+#ifdef YYMAXDEPTH
+#define YYSTACKSIZE YYMAXDEPTH
+#else
+#define YYSTACKSIZE 10000
+#define YYMAXDEPTH 10000
+#endif
+#endif
+#define YYINITSTACKSIZE 200
+int yydebug;
+int yynerrs;
+int yyerrflag;
+int yychar;
+short *yyssp;
+YYSTYPE *yyvsp;
+YYSTYPE yyval;
+YYSTYPE yylval;
+short *yyss;
+short *yysslim;
+YYSTYPE *yyvs;
+int yystacksize;
+/* allocate initial stack or double stack size, up to YYMAXDEPTH */
+static int yygrowstack()
+{
+ int newsize, i;
+ short *newss;
+ YYSTYPE *newvs;
+
+ if ((newsize = yystacksize) == 0)
+ newsize = YYINITSTACKSIZE;
+ else if (newsize >= YYMAXDEPTH)
+ return -1;
+ else if ((newsize *= 2) > YYMAXDEPTH)
+ newsize = YYMAXDEPTH;
+ i = yyssp - yyss;
+ newss = yyss ? (short *)realloc(yyss, newsize * sizeof *newss) :
+ (short *)malloc(newsize * sizeof *newss);
+ if (newss == NULL)
+ return -1;
+ yyss = newss;
+ yyssp = newss + i;
+ newvs = yyvs ? (YYSTYPE *)realloc(yyvs, newsize * sizeof *newvs) :
+ (YYSTYPE *)malloc(newsize * sizeof *newvs);
+ if (newvs == NULL)
+ return -1;
+ yyvs = newvs;
+ yyvsp = newvs + i;
+ yystacksize = newsize;
+ yysslim = yyss + newsize - 1;
+ return 0;
+}
+
+#define YYABORT goto yyabort
+#define YYREJECT goto yyabort
+#define YYACCEPT goto yyaccept
+#define YYERROR goto yyerrlab
+
+#ifndef YYPARSE_PARAM
+#if defined(__cplusplus) || __STDC__
+#define YYPARSE_PARAM_ARG void
+#define YYPARSE_PARAM_DECL
+#else /* ! ANSI-C/C++ */
+#define YYPARSE_PARAM_ARG
+#define YYPARSE_PARAM_DECL
+#endif /* ANSI-C/C++ */
+#else /* YYPARSE_PARAM */
+#ifndef YYPARSE_PARAM_TYPE
+#define YYPARSE_PARAM_TYPE void *
+#endif
+#if defined(__cplusplus) || __STDC__
+#define YYPARSE_PARAM_ARG YYPARSE_PARAM_TYPE YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL
+#else /* ! ANSI-C/C++ */
+#define YYPARSE_PARAM_ARG YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL YYPARSE_PARAM_TYPE YYPARSE_PARAM;
+#endif /* ANSI-C/C++ */
+#endif /* ! YYPARSE_PARAM */
+
+int
+yyparse (YYPARSE_PARAM_ARG)
+ YYPARSE_PARAM_DECL
+{
+ register int yym, yyn, yystate;
+#if YYDEBUG
+ register const char *yys;
+
+ if ((yys = getenv("YYDEBUG")))
+ {
+ yyn = *yys;
+ if (yyn >= '0' && yyn <= '9')
+ yydebug = yyn - '0';
+ }
+#endif
+
+ yynerrs = 0;
+ yyerrflag = 0;
+ yychar = (-1);
+
+ if (yyss == NULL && yygrowstack()) goto yyoverflow;
+ yyssp = yyss;
+ yyvsp = yyvs;
+ *yyssp = yystate = 0;
+
+yyloop:
+ if ((yyn = yydefred[yystate])) goto yyreduce;
+ if (yychar < 0)
+ {
+ if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("%sdebug: state %d, reading %d (%s)\n",
+ YYPREFIX, yystate, yychar, yys);
+ }
+#endif
+ }
+ if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, shifting to state %d\n",
+ YYPREFIX, yystate, yytable[yyn]);
+#endif
+ if (yyssp >= yysslim && yygrowstack())
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate = yytable[yyn];
+ *++yyvsp = yylval;
+ yychar = (-1);
+ if (yyerrflag > 0) --yyerrflag;
+ goto yyloop;
+ }
+ if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+ {
+ yyn = yytable[yyn];
+ goto yyreduce;
+ }
+ if (yyerrflag) goto yyinrecovery;
+#if defined(lint) || defined(__GNUC__)
+ goto yynewerror;
+#endif
+yynewerror:
+ yyerror("syntax error");
+#if defined(lint) || defined(__GNUC__)
+ goto yyerrlab;
+#endif
+yyerrlab:
+ ++yynerrs;
+yyinrecovery:
+ if (yyerrflag < 3)
+ {
+ yyerrflag = 3;
+ for (;;)
+ {
+ if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, error recovery shifting\
+ to state %d\n", YYPREFIX, *yyssp, yytable[yyn]);
+#endif
+ if (yyssp >= yysslim && yygrowstack())
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate = yytable[yyn];
+ *++yyvsp = yylval;
+ goto yyloop;
+ }
+ else
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: error recovery discarding state %d\n",
+ YYPREFIX, *yyssp);
+#endif
+ if (yyssp <= yyss) goto yyabort;
+ --yyssp;
+ --yyvsp;
+ }
+ }
+ }
+ else
+ {
+ if (yychar == 0) goto yyabort;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("%sdebug: state %d, error recovery discards token %d (%s)\n",
+ YYPREFIX, yystate, yychar, yys);
+ }
+#endif
+ yychar = (-1);
+ goto yyloop;
+ }
+yyreduce:
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, reducing by rule %d (%s)\n",
+ YYPREFIX, yystate, yyn, yyrule[yyn]);
+#endif
+ yym = yylen[yyn];
+ yyval = yyvsp[1-yym];
+ switch (yyn)
+ {
+case 2:
+#line 132 "jamgram.y"
+{ parse_save( yyvsp[0].parse ); }
+break;
+case 3:
+#line 143 "jamgram.y"
+{ yyval.parse = pnull(); }
+break;
+case 4:
+#line 145 "jamgram.y"
+{ yyval.parse = yyvsp[0].parse; }
+break;
+case 5:
+#line 149 "jamgram.y"
+{ yyval.parse = yyvsp[0].parse; }
+break;
+case 6:
+#line 151 "jamgram.y"
+{ yyval.parse = prules( yyvsp[-1].parse, yyvsp[0].parse ); }
+break;
+case 7:
+#line 153 "jamgram.y"
+{ yyval.parse = plocal( yyvsp[-2].parse, pnull(), yyvsp[0].parse ); }
+break;
+case 8:
+#line 155 "jamgram.y"
+{ yyval.parse = plocal( yyvsp[-4].parse, yyvsp[-2].parse, yyvsp[0].parse ); }
+break;
+case 9:
+#line 159 "jamgram.y"
+{ yyval.parse = yyvsp[-1].parse; }
+break;
+case 10:
+#line 161 "jamgram.y"
+{ yyval.parse = pincl( yyvsp[-1].parse ); }
+break;
+case 11:
+#line 163 "jamgram.y"
+{ yyval.parse = prule( yyvsp[-2].parse, yyvsp[-1].parse ); }
+break;
+case 12:
+#line 165 "jamgram.y"
+{ yyval.parse = pset( yyvsp[-3].parse, yyvsp[-1].parse, yyvsp[-2].number ); }
+break;
+case 13:
+#line 167 "jamgram.y"
+{ yyval.parse = pset1( yyvsp[-5].parse, yyvsp[-3].parse, yyvsp[-1].parse, yyvsp[-2].number ); }
+break;
+case 14:
+#line 169 "jamgram.y"
+{ yyval.parse = yyvsp[-1].parse; }
+break;
+case 15:
+#line 171 "jamgram.y"
+{ yyval.parse = pfor( yyvsp[-5].string, yyvsp[-3].parse, yyvsp[-1].parse ); }
+break;
+case 16:
+#line 173 "jamgram.y"
+{ yyval.parse = pswitch( yyvsp[-3].parse, yyvsp[-1].parse ); }
+break;
+case 17:
+#line 175 "jamgram.y"
+{ yyval.parse = pif( yyvsp[-3].parse, yyvsp[-1].parse, pnull() ); }
+break;
+case 18:
+#line 177 "jamgram.y"
+{ yyval.parse = pif( yyvsp[-5].parse, yyvsp[-3].parse, yyvsp[0].parse ); }
+break;
+case 19:
+#line 179 "jamgram.y"
+{ yyval.parse = pwhile( yyvsp[-3].parse, yyvsp[-1].parse ); }
+break;
+case 20:
+#line 181 "jamgram.y"
+{ yyval.parse = psetc( yyvsp[-1].string, yyvsp[0].parse ); }
+break;
+case 21:
+#line 183 "jamgram.y"
+{ yyval.parse = pon( yyvsp[-1].parse, yyvsp[0].parse ); }
+break;
+case 22:
+#line 185 "jamgram.y"
+{ yymode( SCAN_STRING ); }
+break;
+case 23:
+#line 187 "jamgram.y"
+{ yymode( SCAN_NORMAL ); }
+break;
+case 24:
+#line 189 "jamgram.y"
+{ yyval.parse = psete( yyvsp[-6].string,yyvsp[-5].parse,yyvsp[-2].string,yyvsp[-7].number ); }
+break;
+case 25:
+#line 197 "jamgram.y"
+{ yyval.number = ASSIGN_SET; }
+break;
+case 26:
+#line 199 "jamgram.y"
+{ yyval.number = ASSIGN_APPEND; }
+break;
+case 27:
+#line 201 "jamgram.y"
+{ yyval.number = ASSIGN_DEFAULT; }
+break;
+case 28:
+#line 203 "jamgram.y"
+{ yyval.number = ASSIGN_DEFAULT; }
+break;
+case 29:
+#line 211 "jamgram.y"
+{ yyval.parse = peval( EXPR_EXISTS, yyvsp[0].parse, pnull() ); }
+break;
+case 30:
+#line 213 "jamgram.y"
+{ yyval.parse = peval( EXPR_EQUALS, yyvsp[-2].parse, yyvsp[0].parse ); }
+break;
+case 31:
+#line 215 "jamgram.y"
+{ yyval.parse = peval( EXPR_NOTEQ, yyvsp[-2].parse, yyvsp[0].parse ); }
+break;
+case 32:
+#line 217 "jamgram.y"
+{ yyval.parse = peval( EXPR_LESS, yyvsp[-2].parse, yyvsp[0].parse ); }
+break;
+case 33:
+#line 219 "jamgram.y"
+{ yyval.parse = peval( EXPR_LESSEQ, yyvsp[-2].parse, yyvsp[0].parse ); }
+break;
+case 34:
+#line 221 "jamgram.y"
+{ yyval.parse = peval( EXPR_MORE, yyvsp[-2].parse, yyvsp[0].parse ); }
+break;
+case 35:
+#line 223 "jamgram.y"
+{ yyval.parse = peval( EXPR_MOREEQ, yyvsp[-2].parse, yyvsp[0].parse ); }
+break;
+case 36:
+#line 225 "jamgram.y"
+{ yyval.parse = peval( EXPR_AND, yyvsp[-2].parse, yyvsp[0].parse ); }
+break;
+case 37:
+#line 227 "jamgram.y"
+{ yyval.parse = peval( EXPR_AND, yyvsp[-2].parse, yyvsp[0].parse ); }
+break;
+case 38:
+#line 229 "jamgram.y"
+{ yyval.parse = peval( EXPR_OR, yyvsp[-2].parse, yyvsp[0].parse ); }
+break;
+case 39:
+#line 231 "jamgram.y"
+{ yyval.parse = peval( EXPR_OR, yyvsp[-2].parse, yyvsp[0].parse ); }
+break;
+case 40:
+#line 233 "jamgram.y"
+{ yyval.parse = peval( EXPR_IN, yyvsp[-2].parse, yyvsp[0].parse ); }
+break;
+case 41:
+#line 235 "jamgram.y"
+{ yyval.parse = peval( EXPR_NOT, yyvsp[0].parse, pnull() ); }
+break;
+case 42:
+#line 237 "jamgram.y"
+{ yyval.parse = yyvsp[-1].parse; }
+break;
+case 43:
+#line 247 "jamgram.y"
+{ yyval.parse = P0; }
+break;
+case 44:
+#line 249 "jamgram.y"
+{ yyval.parse = pnode( yyvsp[-1].parse, yyvsp[0].parse ); }
+break;
+case 45:
+#line 253 "jamgram.y"
+{ yyval.parse = psnode( yyvsp[-2].string, yyvsp[0].parse ); }
+break;
+case 46:
+#line 262 "jamgram.y"
+{ yyval.parse = pnode( P0, yyvsp[0].parse ); }
+break;
+case 47:
+#line 264 "jamgram.y"
+{ yyval.parse = pnode( yyvsp[0].parse, yyvsp[-2].parse ); }
+break;
+case 48:
+#line 274 "jamgram.y"
+{ yyval.parse = yyvsp[0].parse; yymode( SCAN_NORMAL ); }
+break;
+case 49:
+#line 278 "jamgram.y"
+{ yyval.parse = pnull(); yymode( SCAN_PUNCT ); }
+break;
+case 50:
+#line 280 "jamgram.y"
+{ yyval.parse = pappend( yyvsp[-1].parse, yyvsp[0].parse ); }
+break;
+case 51:
+#line 284 "jamgram.y"
+{ yyval.parse = plist( yyvsp[0].string ); }
+break;
+case 52:
+#line 285 "jamgram.y"
+{ yymode( SCAN_NORMAL ); }
+break;
+case 53:
+#line 286 "jamgram.y"
+{ yyval.parse = yyvsp[-1].parse; }
+break;
+case 54:
+#line 295 "jamgram.y"
+{ yyval.parse = prule( yyvsp[-1].parse, yyvsp[0].parse ); }
+break;
+case 55:
+#line 297 "jamgram.y"
+{ yyval.parse = pon( yyvsp[-2].parse, prule( yyvsp[-1].parse, yyvsp[0].parse ) ); }
+break;
+case 56:
+#line 299 "jamgram.y"
+{ yyval.parse = pon( yyvsp[-2].parse, yyvsp[0].parse ); }
+break;
+case 57:
+#line 308 "jamgram.y"
+{ yyval.number = 0; }
+break;
+case 58:
+#line 310 "jamgram.y"
+{ yyval.number = yyvsp[-1].number | yyvsp[0].number; }
+break;
+case 59:
+#line 314 "jamgram.y"
+{ yyval.number = RULE_UPDATED; }
+break;
+case 60:
+#line 316 "jamgram.y"
+{ yyval.number = RULE_TOGETHER; }
+break;
+case 61:
+#line 318 "jamgram.y"
+{ yyval.number = RULE_IGNORE; }
+break;
+case 62:
+#line 320 "jamgram.y"
+{ yyval.number = RULE_QUIETLY; }
+break;
+case 63:
+#line 322 "jamgram.y"
+{ yyval.number = RULE_PIECEMEAL; }
+break;
+case 64:
+#line 324 "jamgram.y"
+{ yyval.number = RULE_EXISTING; }
+break;
+case 65:
+#line 333 "jamgram.y"
+{ yyval.parse = pnull(); }
+break;
+case 66:
+#line 335 "jamgram.y"
+{ yyval.parse = yyvsp[0].parse; }
+break;
+#line 821 "y.tab.c"
+ }
+ yyssp -= yym;
+ yystate = *yyssp;
+ yyvsp -= yym;
+ yym = yylhs[yyn];
+ if (yystate == 0 && yym == 0)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: after reduction, shifting from state 0 to\
+ state %d\n", YYPREFIX, YYFINAL);
+#endif
+ yystate = YYFINAL;
+ *++yyssp = YYFINAL;
+ *++yyvsp = yyval;
+ if (yychar < 0)
+ {
+ if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("%sdebug: state %d, reading %d (%s)\n",
+ YYPREFIX, YYFINAL, yychar, yys);
+ }
+#endif
+ }
+ if (yychar == 0) goto yyaccept;
+ goto yyloop;
+ }
+ if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yystate)
+ yystate = yytable[yyn];
+ else
+ yystate = yydgoto[yym];
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: after reduction, shifting from state %d \
+to state %d\n", YYPREFIX, *yyssp, yystate);
+#endif
+ if (yyssp >= yysslim && yygrowstack())
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate;
+ *++yyvsp = yyval;
+ goto yyloop;
+yyoverflow:
+ yyerror("yacc stack overflow");
+yyabort:
+ return (1);
+yyaccept:
+ return (0);
+}
diff --git a/src/tools/jam/jamgram.h b/src/tools/jam/jamgram.h
new file mode 100644
index 0000000000..b37598a6a3
--- /dev/null
+++ b/src/tools/jam/jamgram.h
@@ -0,0 +1,48 @@
+#ifndef YYERRCODE
+#define YYERRCODE 256
+#endif
+
+#define _BANG_t 257
+#define _BANG_EQUALS_t 258
+#define _AMPER_t 259
+#define _AMPERAMPER_t 260
+#define _LPAREN_t 261
+#define _RPAREN_t 262
+#define _PLUS_EQUALS_t 263
+#define _COLON_t 264
+#define _SEMIC_t 265
+#define _LANGLE_t 266
+#define _LANGLE_EQUALS_t 267
+#define _EQUALS_t 268
+#define _RANGLE_t 269
+#define _RANGLE_EQUALS_t 270
+#define _QUESTION_EQUALS_t 271
+#define _LBRACKET_t 272
+#define _RBRACKET_t 273
+#define ACTIONS_t 274
+#define BIND_t 275
+#define CASE_t 276
+#define DEFAULT_t 277
+#define ELSE_t 278
+#define EXISTING_t 279
+#define FOR_t 280
+#define IF_t 281
+#define IGNORE_t 282
+#define IN_t 283
+#define INCLUDE_t 284
+#define LOCAL_t 285
+#define ON_t 286
+#define PIECEMEAL_t 287
+#define QUIETLY_t 288
+#define RETURN_t 289
+#define RULE_t 290
+#define SWITCH_t 291
+#define TOGETHER_t 292
+#define UPDATED_t 293
+#define WHILE_t 294
+#define _LBRACE_t 295
+#define _BAR_t 296
+#define _BARBAR_t 297
+#define _RBRACE_t 298
+#define ARG 299
+#define STRING 300
diff --git a/src/tools/jam/jamgram.y b/src/tools/jam/jamgram.y
new file mode 100644
index 0000000000..0d899b5aed
--- /dev/null
+++ b/src/tools/jam/jamgram.y
@@ -0,0 +1,338 @@
+%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 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 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-2002 Christopher Seiwald and Perforce Software, Inc.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * 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 *, LOL *))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 ) parse_make( compile_foreach,l,r,P0,s,S0,0 )
+# 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 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( a,p ) parse_make( compile_rule,a,p,P0,S0,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 ) parse_make( compile_setcomp,p,P0,P0,s,S0,0 )
+# 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 : /* empty */
+ { $$.parse = pnull(); }
+ | rules
+ { $$.parse = $1.parse; }
+ ;
+
+rules : rule
+ { $$.parse = $1.parse; }
+ | rule rules
+ { $$.parse = prules( $1.parse, $2.parse ); }
+ | LOCAL_t list _SEMIC_t block
+ { $$.parse = plocal( $2.parse, pnull(), $4.parse ); }
+ | LOCAL_t list _EQUALS_t list _SEMIC_t block
+ { $$.parse = plocal( $2.parse, $4.parse, $6.parse ); }
+ ;
+
+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.parse, $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 ARG IN_t list _LBRACE_t block _RBRACE_t
+ { $$.parse = pfor( $2.string, $4.parse, $6.parse ); }
+ | 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() ); }
+ | IF_t expr _LBRACE_t block _RBRACE_t ELSE_t rule
+ { $$.parse = pif( $2.parse, $4.parse, $7.parse ); }
+ | WHILE_t expr _LBRACE_t block _RBRACE_t
+ { $$.parse = pwhile( $2.parse, $4.parse ); }
+ | RULE_t ARG rule
+ { $$.parse = psetc( $2.string, $3.parse ); }
+ | 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.parse, $2.parse ); }
+ | ON_t arg arg lol
+ { $$.parse = pon( $2.parse, prule( $3.parse, $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 = RULE_UPDATED; }
+ | TOGETHER_t
+ { $$.number = RULE_TOGETHER; }
+ | IGNORE_t
+ { $$.number = RULE_IGNORE; }
+ | QUIETLY_t
+ { $$.number = RULE_QUIETLY; }
+ | PIECEMEAL_t
+ { $$.number = RULE_PIECEMEAL; }
+ | EXISTING_t
+ { $$.number = RULE_EXISTING; }
+ ;
+
+
+/*
+ * bindlist - list of variable to bind for an action
+ */
+
+bindlist : /* empty */
+ { $$.parse = pnull(); }
+ | BIND_t list
+ { $$.parse = $2.parse; }
+ ;
+
+
diff --git a/src/tools/jam/jamgram.yy b/src/tools/jam/jamgram.yy
new file mode 100644
index 0000000000..817e49bd57
--- /dev/null
+++ b/src/tools/jam/jamgram.yy
@@ -0,0 +1,296 @@
+/*
+ * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * 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 *, LOL *))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 ) parse_make( compile_foreach,l,r,P0,s,S0,0 )
+# 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 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( a,p ) parse_make( compile_rule,a,p,P0,S0,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 ) parse_make( compile_setcomp,p,P0,P0,s,S0,0 )
+# 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 : /* empty */
+ { $$.parse = pnull(); }
+ | rules
+ { $$.parse = $1.parse; }
+ ;
+
+rules : rule
+ { $$.parse = $1.parse; }
+ | rule rules
+ { $$.parse = prules( $1.parse, $2.parse ); }
+ | `local` list `;` block
+ { $$.parse = plocal( $2.parse, pnull(), $4.parse ); }
+ | `local` list `=` list `;` block
+ { $$.parse = plocal( $2.parse, $4.parse, $6.parse ); }
+ ;
+
+rule : `{` block `}`
+ { $$.parse = $2.parse; }
+ | `include` list `;`
+ { $$.parse = pincl( $2.parse ); }
+ | arg lol `;`
+ { $$.parse = prule( $1.parse, $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` ARG `in` list `{` block `}`
+ { $$.parse = pfor( $2.string, $4.parse, $6.parse ); }
+ | `switch` list `{` cases `}`
+ { $$.parse = pswitch( $2.parse, $4.parse ); }
+ | `if` expr `{` block `}`
+ { $$.parse = pif( $2.parse, $4.parse, pnull() ); }
+ | `if` expr `{` block `}` `else` rule
+ { $$.parse = pif( $2.parse, $4.parse, $7.parse ); }
+ | `while` expr `{` block `}`
+ { $$.parse = pwhile( $2.parse, $4.parse ); }
+ | `rule` ARG rule
+ { $$.parse = psetc( $2.string, $3.parse ); }
+ | `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.parse, $2.parse ); }
+ | `on` arg arg lol
+ { $$.parse = pon( $2.parse, prule( $3.parse, $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 = RULE_UPDATED; }
+ | `together`
+ { $$.number = RULE_TOGETHER; }
+ | `ignore`
+ { $$.number = RULE_IGNORE; }
+ | `quietly`
+ { $$.number = RULE_QUIETLY; }
+ | `piecemeal`
+ { $$.number = RULE_PIECEMEAL; }
+ | `existing`
+ { $$.number = RULE_EXISTING; }
+ ;
+
+
+/*
+ * bindlist - list of variable to bind for an action
+ */
+
+bindlist : /* empty */
+ { $$.parse = pnull(); }
+ | `bind` list
+ { $$.parse = $2.parse; }
+ ;
+
+
diff --git a/src/tools/jam/jamgramtab.h b/src/tools/jam/jamgramtab.h
new file mode 100644
index 0000000000..dcc6a47fa4
--- /dev/null
+++ b/src/tools/jam/jamgramtab.h
@@ -0,0 +1,42 @@
+ { "!", _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 },
+ { "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 },
+ { "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/src/tools/jam/lists.c b/src/tools/jam/lists.c
new file mode 100644
index 0000000000..e205e0ff39
--- /dev/null
+++ b/src/tools/jam/lists.c
@@ -0,0 +1,243 @@
+/*
+ * 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 *)malloc( sizeof( *l ) );
+ }
+
+ /* 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;
+}
+
+/*
+ * 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_print() - print a list of strings to stdout
+ */
+
+void
+list_print( LIST *l )
+{
+ for( ; l; l = list_next( l ) )
+ printf( "%s ", l->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;
+}
+
+/*
+ * 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] );
+ }
+}
diff --git a/src/tools/jam/lists.h b/src/tools/jam/lists.h
new file mode 100644
index 0000000000..33afc58860
--- /dev/null
+++ b/src/tools/jam/lists.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * 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()
+ */
+
+/*
+ * 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 9
+
+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 );
+
+# 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 );
diff --git a/src/tools/jam/make.c b/src/tools/jam/make.c
new file mode 100644
index 0000000000..7c5d8c2b7b
--- /dev/null
+++ b/src/tools/jam/make.c
@@ -0,0 +1,479 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * 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 of the
+ * 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 aren't seen as dependents 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"
+
+# include "search.h"
+# include "newstr.h"
+# include "make.h"
+# include "headers.h"
+# include "command.h"
+
+# ifndef max
+# define max( a,b ) ((a)>(b)?(a):(b))
+# endif
+
+typedef struct {
+ int temp;
+ int updating;
+ int cantfind;
+ int cantmake;
+ int targets;
+ int made;
+} COUNTS ;
+
+static void make0( TARGET *t, time_t ptime, int depth,
+ COUNTS *counts, int anyhow );
+
+static TARGETS *make0sort( TARGETS *c );
+
+static 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 */
+ "missing", /* T_FATE_MISSING */
+ "old", /* T_FATE_OUTDATED */
+ "update", /* T_FATE_UPDATE */
+ "nofind", /* T_FATE_CANTFIND */
+ "nomake" /* T_FATE_CANTMAKE */
+} ;
+
+static char *target_bind[] =
+{
+ "unbound",
+ "missing",
+ "parents",
+ "exists",
+} ;
+
+# define spaces(x) ( " " + 16 - ( x > 16 ? 16 : x ) )
+
+/*
+ * make() - make a target, given its name
+ */
+
+int
+make(
+ int n_targets,
+ char **targets,
+ int anyhow )
+{
+ int i;
+ COUNTS counts[1];
+ int status = 0; /* 1 if anything fails */
+
+ memset( (char *)counts, 0, sizeof( *counts ) );
+
+ for( i = 0; i < n_targets; i++ )
+ {
+ TARGET *t = bindtarget( targets[i] );
+
+ make0( t, (time_t)0, 0, counts, anyhow );
+ }
+
+ if( DEBUG_MAKE )
+ {
+ if( counts->targets )
+ printf( "...found %d target(s)...\n", counts->targets );
+ if( counts->temp )
+ printf( "...using %d temp target(s)...\n", counts->temp );
+ if( counts->updating )
+ printf( "...updating %d target(s)...\n", counts->updating );
+ if( counts->cantfind )
+ printf( "...can't find %d target(s)...\n", counts->cantfind );
+ if( counts->cantmake )
+ printf( "...can't make %d target(s)...\n", counts->cantmake );
+ }
+
+ status = counts->cantfind || counts->cantmake;
+
+ for( i = 0; i < n_targets; i++ )
+ status |= make1( bindtarget( targets[i] ) );
+
+ return status;
+}
+
+/*
+ * make0() - bind and scan everything to make a TARGET
+ *
+ * Make0() recursively binds a target, searches for #included headers,
+ * calls itself on those headers, and calls itself on any dependents.
+ */
+
+static void
+make0(
+ TARGET *t,
+ time_t ptime, /* parent target's timestamp */
+ int depth, /* for display purposes */
+ COUNTS *counts, /* for reporting */
+ int anyhow ) /* forcibly touch all (real) targets */
+{
+ TARGETS *c;
+ int fate, hfate;
+ time_t last, leaf, hlast, hleaf;
+ char *flag = "";
+
+ if( DEBUG_MAKEPROG )
+ printf( "make\t--\t%s%s\n", spaces( depth ), t->name );
+
+ /*
+ * Step 1: don't remake if already trying or tried
+ */
+
+ switch( t->fate )
+ {
+ case T_FATE_MAKING:
+ printf( "warning: %s depends on itself\n", t->name );
+ return;
+
+ default:
+ return;
+
+ case T_FATE_INIT:
+ break;
+ }
+
+ 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. */
+
+ pushsettings( t->settings );
+
+ /* Step 2b: find and timestamp the target file (if it's a file). */
+
+ if( t->binding == T_BIND_UNBOUND && !( t->flags & T_FLAG_NOTFILE ) )
+ {
+ t->boundname = search( t->name, &t->time );
+ t->binding = t->time ? T_BIND_EXISTS : T_BIND_MISSING;
+ }
+
+ /* If temp file doesn't exist but parent does, use parent */
+
+ if( t->binding == T_BIND_MISSING && t->flags & T_FLAG_TEMP && ptime )
+ t->binding = T_BIND_PARENTS;
+
+ /* Step 2c: If its a file, search for headers. */
+
+ if( t->binding == T_BIND_EXISTS )
+ headers( t );
+
+ /* Step 2d: reset "on target" variables */
+
+ popsettings( t->settings );
+
+ /*
+ * 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[ 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() dependents
+ */
+
+ last = 0;
+ leaf = 0;
+ fate = T_FATE_STABLE;
+
+ for( c = t->deps[ T_DEPS_DEPENDS ]; c; c = c->next )
+ {
+ /* Pass our time or our parent's time down. */
+
+ int time = t->binding == T_BIND_PARENTS ? ptime : t->time;
+
+ make0( c->target, time, depth + 1, counts, anyhow );
+ leaf = max( leaf, c->target->leaf );
+ leaf = max( leaf, c->target->hleaf );
+
+ /* If LEAVES has been applied, we only heed the timestamps of */
+ /* the leaf source nodes. */
+
+ if( t->flags & T_FLAG_LEAVES )
+ {
+ last = leaf;
+ continue;
+ }
+
+ last = max( last, c->target->time );
+ last = max( last, c->target->htime );
+ fate = max( fate, c->target->fate );
+ fate = max( fate, c->target->hfate );
+ }
+
+ /* If a NOUPDATE file exists, make dependents eternally old. */
+
+ if( t->flags & T_FLAG_NOUPDATE )
+ {
+ last = 0;
+ t->time = 0;
+
+ /*
+ * Don't inherit our fate from our dependents. Decide fate
+ * based only upon other flags and our binding (done later).
+ */
+
+ fate = T_FATE_STABLE;
+ }
+
+ /* Step 3b: determine fate: rebuild target or what? */
+
+ /*
+ In English:
+ If can't find or make child, can't 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 deliberately touched, make it.
+ If up-to-date temp file present, use it.
+ If target newer than parent, mark target newer.
+ Don't propagate child's "newer" status.
+ */
+
+ 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 )
+ {
+ fate = T_FATE_OUTDATED;
+ }
+ else if( t->binding == T_BIND_PARENTS && last > ptime )
+ {
+ fate = T_FATE_OUTDATED;
+ }
+ 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 && t->time > ptime )
+ {
+ fate = T_FATE_NEWER;
+ }
+ else if( fate == T_FATE_NEWER )
+ {
+ fate = T_FATE_STABLE;
+ }
+
+ /* Step 3c: handle missing files */
+ /* If it's missing and there are no actions to create it, boom. */
+ /* If we can't make a target we don't care about, 'sokay */
+ /* We could insist that there are updating actions for all missing */
+ /* files, but if they have dependents we just pretend it's NOTFILE. */
+
+ if( fate == T_FATE_MISSING &&
+ !t->actions &&
+ !t->deps[ T_DEPS_DEPENDS ] )
+ {
+ if( t->flags & T_FLAG_NOCARE )
+ {
+ fate = T_FATE_STABLE;
+ }
+ else
+ {
+ printf( "don't know how to make %s\n", t->name );
+
+ fate = T_FATE_CANTFIND;
+ }
+ }
+
+ /* Step 3d: propagate dependents' 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 ;
+ t->fate = fate;
+
+ /* Step 3e: sort dependents by their update time. */
+
+ if( globs.newestfirst )
+ t->deps[ T_DEPS_DEPENDS ] = make0sort( t->deps[ T_DEPS_DEPENDS ] );
+
+ /*
+ * Step 4: Recursively make0() headers.
+ */
+
+ /* Step 4a: recursively make0() headers */
+
+ hlast = 0;
+ hleaf = 0;
+ hfate = T_FATE_STABLE;
+
+ for( c = t->deps[ T_DEPS_INCLUDES ]; c; c = c->next )
+ {
+ make0( c->target, ptime, depth + 1, counts, anyhow );
+ hlast = max( hlast, c->target->time );
+ hlast = max( hlast, c->target->htime );
+ hleaf = max( hleaf, c->target->leaf );
+ hleaf = max( hleaf, c->target->hleaf );
+ hfate = max( hfate, c->target->fate );
+ hfate = max( hfate, c->target->hfate );
+ }
+
+ /* Step 4b: propagate dependents' time & fate. */
+
+ t->htime = hlast;
+ t->hleaf = hleaf ? hleaf : t->htime;
+ t->hfate = hfate;
+
+ /*
+ * Step 5: a little harmless tabulating for tracing purposes
+ */
+
+ if( !( ++counts->targets % 1000 ) && DEBUG_MAKE )
+ printf( "...patience...\n" );
+
+ 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 && ptime && t->time > ptime )
+ flag = "*";
+
+ if( DEBUG_MAKEPROG )
+ printf( "made%s\t%s\t%s%s\n",
+ flag, target_fate[ t->fate ],
+ spaces( depth ), t->name );
+}
+
+/*
+ * make0sort() - reorder TARGETS chain by their time (newest to oldest)
+ */
+
+static TARGETS *
+make0sort( TARGETS *chain )
+{
+ TARGETS *result = 0;
+
+ /* We walk chain, taking each item and inserting it on the */
+ /* sorted result, with newest items at the front. This involves */
+ /* updating each 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. */
+
+ /* Walk 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). */
+ /* Don't 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 */
+ }
+
+ return result;
+}
diff --git a/src/tools/jam/make.h b/src/tools/jam/make.h
new file mode 100644
index 0000000000..87ca862093
--- /dev/null
+++ b/src/tools/jam/make.h
@@ -0,0 +1,12 @@
+/*
+ * 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
+ */
+
+int make( int n_targets, char **targets, int anyhow );
+int make1( TARGET *t );
diff --git a/src/tools/jam/make1.c b/src/tools/jam/make1.c
new file mode 100644
index 0000000000..f2e62459f2
--- /dev/null
+++ b/src/tools/jam/make1.c
@@ -0,0 +1,647 @@
+/*
+ * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * 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 its dependents
+ *
+ * Internal routines, the recursive/asynchronous command executors:
+ *
+ * make1a() - recursively traverse target tree, calling make1b()
+ * make1b() - dependents of target built, now build target with make1c()
+ * make1c() - launch target's next command, call make1b() when done
+ * 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 execcmd().
+ * 02/28/95 (seiwald) - Handle empty "existing" actions.
+ * 03/10/95 (seiwald) - Fancy counts.
+ */
+
+# include "jam.h"
+
+# include "lists.h"
+# include "parse.h"
+# include "variable.h"
+# include "rules.h"
+
+# include "search.h"
+# include "newstr.h"
+# include "make.h"
+# include "command.h"
+# include "execcmd.h"
+
+static void make1a( TARGET *t, TARGET *parent );
+static void make1b( TARGET *t );
+static void make1c( TARGET *t );
+static void make1d( void *closure, int status );
+
+static CMD *make1cmds( ACTIONS *a0 );
+static LIST *make1list( LIST *l, TARGETS *targets, int flags );
+static SETTINGS *make1settings( LIST *vars );
+static void make1bind( TARGET *t, int warn );
+
+/* Ugly static - it's too hard to carry it through the callbacks. */
+
+static struct {
+ int failed;
+ int skipped;
+ int total;
+ int made;
+} counts[1] ;
+
+/*
+ * make1() - execute commands to update a TARGET and all its dependents
+ */
+
+static int intr = 0;
+
+int
+make1( TARGET *t )
+{
+ memset( (char *)counts, 0, sizeof( *counts ) );
+
+ /* Recursively make the target and its dependents */
+
+ make1a( t, (TARGET *)0 );
+
+ /* Wait for any outstanding commands to finish running. */
+
+ while( execwait() )
+ ;
+
+ /* Talk about it */
+
+ if( DEBUG_MAKE && counts->failed )
+ printf( "...failed updating %d target(s)...\n", counts->failed );
+
+ if( DEBUG_MAKE && counts->skipped )
+ printf( "...skipped %d target(s)...\n", counts->skipped );
+
+ if( DEBUG_MAKE && counts->made )
+ printf( "...updated %d target(s)...\n", counts->made );
+
+ return counts->total != counts->made;
+}
+
+/*
+ * make1a() - recursively traverse target tree, calling make1b()
+ */
+
+static void
+make1a(
+ TARGET *t,
+ TARGET *parent )
+{
+ TARGETS *c;
+ int i;
+
+ /* 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( parent )
+ switch( t->progress )
+ {
+ case T_MAKE_INIT:
+ case T_MAKE_ACTIVE:
+ case T_MAKE_RUNNING:
+ t->parents = targetentry( t->parents, parent );
+ parent->asynccnt++;
+ }
+
+ if( t->progress != T_MAKE_INIT )
+ return;
+
+ /* Asynccnt counts the dependents 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 */
+ /* call all dependents. This 1 is accounted for when we call */
+ /* make1b() ourselves, below. */
+
+ t->asynccnt = 1;
+
+ /* Recurse on our dependents, manipulating progress to guard */
+ /* against circular dependency. */
+
+ t->progress = T_MAKE_ONSTACK;
+
+ for( i = T_DEPS_DEPENDS; i <= T_DEPS_INCLUDES; i++ )
+ for( c = t->deps[i]; c && !intr; c = c->next )
+ make1a( c->target, t );
+
+ t->progress = T_MAKE_ACTIVE;
+
+ /* Now that all dependents have bumped asynccnt, we now allow */
+ /* decrement our reference to asynccnt. */
+
+ make1b( t );
+}
+
+/*
+ * make1b() - dependents of target built, now build target with make1c()
+ */
+
+static void
+make1b( TARGET *t )
+{
+ TARGETS *c;
+ int i;
+ char *failed = "dependents";
+
+ /* If any dependents are still outstanding, wait until they */
+ /* call make1b() to signal their completion. */
+
+ if( --t->asynccnt )
+ return;
+
+ /* Now ready to build target 't'... if dependents built ok. */
+
+ /* Collect status from dependents */
+
+ for( i = T_DEPS_DEPENDS; i <= T_DEPS_INCLUDES; i++ )
+ for( c = t->deps[i]; c; c = c->next )
+ if( c->target->status > t->status )
+ {
+ failed = c->target->name;
+ t->status = c->target->status;
+ }
+
+ /* If actions on deps have failed, bail. */
+ /* Otherwise, execute all actions to make target */
+
+ if( t->status == EXEC_CMD_FAIL && t->actions )
+ {
+ ++counts->skipped;
+ printf( "...skipped %s for lack of %s...\n", t->name, failed );
+ }
+
+ if( t->status == EXEC_CMD_OK )
+ switch( t->fate )
+ {
+ case T_FATE_INIT:
+ case T_FATE_MAKING:
+ /* shouldn't happen */
+
+ case T_FATE_STABLE:
+ case T_FATE_NEWER:
+ break;
+
+ case T_FATE_CANTFIND:
+ case T_FATE_CANTMAKE:
+ t->status = EXEC_CMD_FAIL;
+ break;
+
+ case T_FATE_ISTMP:
+ if( DEBUG_MAKE )
+ printf( "...using %s...\n", t->name );
+ break;
+
+ case T_FATE_TOUCHED:
+ case T_FATE_MISSING:
+ case T_FATE_OUTDATED:
+ case T_FATE_UPDATE:
+ /* Set "on target" vars, build actions, unset vars */
+ /* Set "progress" so that make1c() counts this target among */
+ /* the successes/failures. */
+
+ if( t->actions )
+ {
+ ++counts->total;
+
+ if( DEBUG_MAKE && !( counts->total % 100 ) )
+ printf( "...on %dth target...\n", counts->total );
+
+ pushsettings( t->settings );
+ t->cmds = (char *)make1cmds( t->actions );
+ popsettings( t->settings );
+
+ t->progress = T_MAKE_RUNNING;
+ }
+
+ break;
+ }
+
+ /* Call make1c() to begin the execution of the chain of commands */
+ /* needed to build target. If we're not going to build target */
+ /* (because of dependency failures or because no commands need to */
+ /* be run) the chain will be empty and make1c() will directly */
+ /* signal the completion of target. */
+
+ make1c( t );
+}
+
+/*
+ * make1c() - launch target's next command, call make1b() when done
+ */
+
+static void
+make1c( TARGET *t )
+{
+ CMD *cmd = (CMD *)t->cmds;
+
+ /* If there are (more) commands to run to build this target */
+ /* (and we haven't hit an error running earlier comands) we */
+ /* launch the command with execcmd(). */
+
+ /* If there are no more commands to run, we collect the status */
+ /* from all the actions then report our completion to all the */
+ /* parents. */
+
+ if( cmd && t->status == EXEC_CMD_OK )
+ {
+ if( DEBUG_MAKE )
+ if( DEBUG_MAKEQ || ! ( cmd->rule->flags & RULE_QUIETLY ) )
+ {
+ printf( "%s ", cmd->rule->name );
+ list_print( lol_get( &cmd->args, 0 ) );
+ printf( "\n" );
+ }
+
+ if( DEBUG_EXEC )
+ printf( "%s\n", cmd->buf );
+
+ if( globs.cmdout )
+ fprintf( globs.cmdout, "%s", cmd->buf );
+
+ if( globs.noexec )
+ {
+ make1d( t, EXEC_CMD_OK );
+ }
+ else
+ {
+ fflush( stdout );
+ execcmd( cmd->buf, make1d, t, cmd->shell );
+ }
+ }
+ else
+ {
+ TARGETS *c;
+ ACTIONS *actions;
+
+ /* Collect status from actions, and distribute it as well */
+
+ for( actions = t->actions; actions; actions = actions->next )
+ if( actions->action->status > t->status )
+ t->status = actions->action->status;
+
+ for( actions = t->actions; actions; actions = actions->next )
+ if( t->status > actions->action->status )
+ actions->action->status = t->status;
+
+ /* Tally success/failure for those we tried to update. */
+
+ if( t->progress == T_MAKE_RUNNING )
+ switch( t->status )
+ {
+ case EXEC_CMD_OK:
+ ++counts->made;
+ break;
+ case EXEC_CMD_FAIL:
+ ++counts->failed;
+ break;
+ }
+
+ /* Tell parents dependent has been built */
+
+ t->progress = T_MAKE_DONE;
+
+ for( c = t->parents; c; c = c->next )
+ make1b( c->target );
+ }
+}
+
+/*
+ * make1d() - handle command execution completion and call back make1c()
+ */
+
+static void
+make1d(
+ void *closure,
+ int status )
+{
+ TARGET *t = (TARGET *)closure;
+ CMD *cmd = (CMD *)t->cmds;
+
+ /* Execcmd() has completed. All we need to do is fiddle with the */
+ /* status and signal our completion so make1c() can run the next */
+ /* command. On interrupts, we bail heavily. */
+
+ if( status == EXEC_CMD_FAIL && ( cmd->rule->flags & RULE_IGNORE ) )
+ status = EXEC_CMD_OK;
+
+ /* On interrupt, set intr so _everything_ fails */
+
+ if( status == EXEC_CMD_INTR )
+ ++intr;
+
+ if( status == EXEC_CMD_FAIL && DEBUG_MAKE )
+ {
+ /* Print command text on failure */
+
+ if( !DEBUG_EXEC )
+ printf( "%s\n", cmd->buf );
+
+ printf( "...failed %s ", cmd->rule->name );
+ list_print( lol_get( &cmd->args, 0 ) );
+ printf( "...\n" );
+
+ if( globs.quitquick ) ++intr;
+ }
+
+ /* If the command was interrupted or failed and the target */
+ /* is not "precious", remove the targets. */
+ /* Precious == 'actions updated' -- the target maintains state. */
+
+ if( status != EXEC_CMD_OK && !( cmd->rule->flags & RULE_UPDATED ) )
+ {
+ LIST *targets = lol_get( &cmd->args, 0 );
+
+ for( ; targets; targets = list_next( targets ) )
+ if( !unlink( targets->string ) )
+ printf( "...removing %s\n", targets->string );
+ }
+
+ /* Free this command and call make1c() to move onto next command. */
+
+ t->status = status;
+ t->cmds = (char *)cmd_next( cmd );
+
+ cmd_free( cmd );
+
+ make1c( t );
+}
+
+/*
+ * 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_UPDATED actions. The result is a chain of
+ * CMDs which can be expanded by var_string() and executed with
+ * execcmd().
+ */
+
+static CMD *
+make1cmds( ACTIONS *a0 )
+{
+ CMD *cmds = 0;
+ LIST *shell = var_get( "JAMSHELL" ); /* shell is per-target */
+
+ /* Step through actions */
+ /* Actions may be shared with other targets or grouped with */
+ /* RULE_TOGETHER, so actions already seen are skipped. */
+
+ for( ; a0; a0 = a0->next )
+ {
+ RULE *rule = a0->action->rule;
+ SETTINGS *boundvars;
+ LIST *nt, *ns;
+ ACTIONS *a1;
+ CMD *cmd;
+ int start, chunk, length;
+
+ /* Only do rules with commands to execute. */
+ /* If this action has already been executed, use saved status */
+
+ if( !rule->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, rule->flags );
+
+ if( rule->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, rule->flags );
+ a1->action->running = 1;
+ }
+
+ /* If doing only updated (or existing) sources, but none have */
+ /* been updated (or exist), skip this action. */
+
+ if( !ns && ( rule->flags & ( RULE_UPDATED | RULE_EXISTING ) ) )
+ {
+ list_free( nt );
+ continue;
+ }
+
+ /* If we had 'actions xxx bind vars' we bind the vars now */
+
+ boundvars = make1settings( rule->bindlist );
+ pushsettings( boundvars );
+
+ /*
+ * Build command, starting with all source args.
+ *
+ * If cmd_new returns 0, it's because the resulting command
+ * length is > MAXLINE. In this case, we'll 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's worth it to get as close to MAXLINE
+ * as possible, because launching the commands we're 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( ( rule->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 );
+ 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 );
+ }
+
+ 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;
+
+ /* Sources to 'actions existing' are never in the dependency */
+ /* graph (if they were, they'd get built and 'existing' would */
+ /* be superfluous, so throttle warning message about independent */
+ /* targets. */
+
+ if( t->binding == T_BIND_UNBOUND )
+ make1bind( t, !( flags & RULE_EXISTING ) );
+
+ if( ( flags & RULE_EXISTING ) && t->binding != T_BIND_EXISTS )
+ continue;
+
+ if( ( flags & RULE_UPDATED ) && 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, warning if it is not in the */
+ /* dependency graph. */
+
+ if( t->binding == T_BIND_UNBOUND )
+ make1bind( t, 1 );
+
+ /* Build new list */
+
+ nl = list_new( nl, copystr( t->boundname ) );
+ }
+
+ /* Add to settings chain */
+
+ settings = addsettings( settings, 0, vars->string, nl );
+ }
+
+ return settings;
+}
+
+/*
+ * make1bind() - bind targets that weren't bound in dependency analysis
+ *
+ * Spot the kludge! If a target is not in the dependency tree, it didn't
+ * get bound by make0(), so we have to do it here. Ugly.
+ */
+
+static void
+make1bind(
+ TARGET *t,
+ int warn )
+{
+ if( t->flags & T_FLAG_NOTFILE )
+ return;
+
+ /* Sources to 'actions existing' are never in the dependency */
+ /* graph (if they were, they'd get built and 'existing' would */
+ /* be superfluous, so throttle warning message about independent */
+ /* targets. */
+
+ if( warn )
+ printf( "warning: using independent target %s\n", t->name );
+
+ pushsettings( t->settings );
+ t->boundname = search( t->name, &t->time );
+ t->binding = t->time ? T_BIND_EXISTS : T_BIND_MISSING;
+ popsettings( t->settings );
+}
diff --git a/src/tools/jam/mkjambase.c b/src/tools/jam/mkjambase.c
new file mode 100644
index 0000000000..2a67db7ea5
--- /dev/null
+++ b/src/tools/jam/mkjambase.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * 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
+# include
+
+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;
+ 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/src/tools/jam/newstr.c b/src/tools/jam/newstr.c
new file mode 100644
index 0000000000..08336480ee
--- /dev/null
+++ b/src/tools/jam/newstr.c
@@ -0,0 +1,96 @@
+/*
+ * 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"
+
+/*
+ * newstr.c - string manipulation routines
+ *
+ * To minimize string copying, string creation, copying, and freeing
+ * is done through newstr.
+ *
+ * External functions:
+ *
+ * newstr() - return a malloc'ed copy of a string
+ * copystr() - return a copy of a string previously returned by newstr()
+ * freestr() - free a string returned by newstr() or copystr()
+ * donestr() - 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;
+
+/*
+ * newstr() - return a malloc'ed copy of a string
+ */
+
+char *
+newstr( char *string )
+{
+ STRING str, *s = &str;
+
+ if( !strhash )
+ strhash = hashinit( sizeof( STRING ), "strings" );
+
+ *s = string;
+
+ if( hashenter( strhash, (HASHDATA **)&s ) )
+ {
+ int l = strlen( string );
+ char *m = (char *)malloc( l + 1 );
+
+ if (DEBUG_MEM)
+ printf("newstr: allocating %d bytes\n", l + 1 );
+
+ strtotal += l + 1;
+ memcpy( m, string, l + 1 );
+ *s = m;
+ }
+
+ return *s;
+}
+
+/*
+ * copystr() - return a copy of a string previously returned by newstr()
+ */
+
+char *
+copystr( char *s )
+{
+ return s;
+}
+
+/*
+ * freestr() - free a string returned by newstr() or copystr()
+ */
+
+void
+freestr( char *s )
+{
+}
+
+/*
+ * donestr() - free string tables
+ */
+
+void
+donestr()
+{
+ hashdone( strhash );
+
+ if( DEBUG_MEM )
+ printf( "%dK in strings\n", strtotal / 1024 );
+}
diff --git a/src/tools/jam/newstr.h b/src/tools/jam/newstr.h
new file mode 100644
index 0000000000..cf2dd34b7e
--- /dev/null
+++ b/src/tools/jam/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 *newstr( char *string );
+char *copystr( char *s );
+void freestr( char *s );
+void donestr();
diff --git a/src/tools/jam/option.c b/src/tools/jam/option.c
new file mode 100644
index 0000000000..e4491523cb
--- /dev/null
+++ b/src/tools/jam/option.c
@@ -0,0 +1,103 @@
+/*
+ * 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 .
+ * 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,
+ 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] != '-' || !isalpha( argv[i][1] ) )
+ break;
+
+ 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(
+ 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/src/tools/jam/option.h b/src/tools/jam/option.h
new file mode 100644
index 0000000000..657a763a08
--- /dev/null
+++ b/src/tools/jam/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 option
+{
+ char flag; /* filled in by getoption() */
+ char *val; /* set to random address if true */
+} option;
+
+# define N_OPTS 256
+
+int getoptions( int argc, char **argv, char *opts, option *optv );
+char * getoptval( option *optv, char opt, int subopt );
diff --git a/src/tools/jam/parse.c b/src/tools/jam/parse.c
new file mode 100644
index 0000000000..1e8681227e
--- /dev/null
+++ b/src/tools/jam/parse.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright 1993, 2000 Christopher Seiwald.
+ *
+ * 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 "newstr.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 )
+{
+ /* 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(;;)
+ {
+ LOL l;
+ PARSE *p;
+
+ /* $(<) and $(>) empty in outer scope. */
+
+ lol_init( &l );
+
+ /* 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. */
+
+ (*(p->func))( p, &l );
+
+ parse_free( p );
+ }
+}
+
+void
+parse_save( PARSE *p )
+{
+ yypsave = p;
+}
+
+PARSE *
+parse_make(
+ LIST *(*func)( PARSE *p, LOL *args ),
+ PARSE *left,
+ PARSE *right,
+ PARSE *third,
+ char *string,
+ char *string1,
+ int num )
+{
+ PARSE *p = (PARSE *)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;
+
+ 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 );
+
+ free( (char *)p );
+}
diff --git a/src/tools/jam/parse.h b/src/tools/jam/parse.h
new file mode 100644
index 0000000000..e1c0575a38
--- /dev/null
+++ b/src/tools/jam/parse.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 1993, 2000 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * parse.h - make and destroy parse trees as driven by the parser
+ */
+
+/*
+ * parse tree node
+ */
+
+typedef struct _PARSE PARSE;
+
+struct _PARSE {
+ LIST *(*func)( PARSE *p, LOL *args );
+ PARSE *left;
+ PARSE *right;
+ PARSE *third;
+ char *string;
+ char *string1;
+ int num;
+ int refs;
+} ;
+
+void parse_file( char *f );
+void parse_save( PARSE *p );
+
+PARSE * parse_make(
+ LIST *(*func)( PARSE *p, LOL *args ),
+ PARSE *left,
+ PARSE *right,
+ PARSE *third,
+ char *string,
+ char *string1,
+ int num );
+
+void parse_refer( PARSE *p );
+void parse_free( PARSE *p );
diff --git a/src/tools/jam/patchlevel.h b/src/tools/jam/patchlevel.h
new file mode 100644
index 0000000000..ae63fd4eba
--- /dev/null
+++ b/src/tools/jam/patchlevel.h
@@ -0,0 +1,5 @@
+/* Keep JAMVERSYM in sync with VERSION. */
+/* It can be accessed as $(JAMVERSION) in the Jamfile. */
+
+#define VERSION "2.4"
+#define JAMVERSYM "JAMVERSION=2.4"
diff --git a/src/tools/jam/pathmac.c b/src/tools/jam/pathmac.c
new file mode 100644
index 0000000000..541729bb43
--- /dev/null
+++ b/src/tools/jam/pathmac.c
@@ -0,0 +1,282 @@
+/*
+ * 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 "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 */
+
+ 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,
+ char *file,
+ int binding )
+{
+ char *ofile = file;
+ int dflag, rflag, act;
+
+ 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 );
+ }
+
+ /* 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] != '<' ) *file++ = '<';
+ memcpy( file, f->f_grist.ptr, f->f_grist.len );
+ file += f->f_grist.len;
+ if( file[-1] != '>' ) *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 */
+ *file++ = DELIM;
+ /* fall through */
+
+ case G_DIR:
+ /* take dir */
+ memcpy( file, f->f_dir.ptr, f->f_dir.len );
+ file += f->f_dir.len;
+ break;
+
+ case G_ROOT:
+ /* take root */
+ memcpy( file, f->f_root.ptr, f->f_root.len );
+ file += f->f_root.len;
+ break;
+
+ case G_CAT:
+ /* prepend root to dir */
+ memcpy( file, f->f_root.ptr, f->f_root.len );
+ file += f->f_root.len;
+ if( file[-1] == DELIM ) --file;
+ memcpy( file, f->f_dir.ptr, f->f_dir.len );
+ file += f->f_dir.len;
+ break;
+
+ case G_DDDD:
+ /* make it ::: (../..) */
+ strcpy( file, ":::" );
+ file += 3;
+ break;
+ }
+
+ /* Put : between dir and file (if none already) */
+
+ if( act != G_MT &&
+ file[-1] != DELIM &&
+ ( f->f_base.len || f->f_suffix.len ) )
+ {
+ *file++ = DELIM;
+ }
+
+ if( f->f_base.len )
+ {
+ memcpy( file, f->f_base.ptr, f->f_base.len );
+ file += f->f_base.len;
+ }
+
+ if( f->f_suffix.len )
+ {
+ memcpy( file, f->f_suffix.ptr, f->f_suffix.len );
+ file += f->f_suffix.len;
+ }
+
+ if( f->f_member.len )
+ {
+ *file++ = '(';
+ memcpy( file, f->f_member.ptr, f->f_member.len );
+ file += f->f_member.len;
+ *file++ = ')';
+ }
+ *file = 0;
+
+ if( DEBUG_SEARCH )
+ printf(" -> '%s'\n", ofile);
+}
+
+/*
+ * 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/src/tools/jam/pathsys.h b/src/tools/jam/pathsys.h
new file mode 100644
index 0000000000..c16442bbc4
--- /dev/null
+++ b/src/tools/jam/pathsys.h
@@ -0,0 +1,49 @@
+/*
+ * 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 dir/base/suffix(member)
+ *
+ * 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.
+ */
+
+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, char *file, int binding );
+void path_parse( char *file, PATHNAME *f );
+void path_parent( PATHNAME *f );
+
diff --git a/src/tools/jam/pathunix.c b/src/tools/jam/pathunix.c
new file mode 100644
index 0000000000..ae0bd01a44
--- /dev/null
+++ b/src/tools/jam/pathunix.c
@@ -0,0 +1,234 @@
+/*
+ * 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 "pathsys.h"
+
+# 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, *q;
+ char *end;
+
+ memset( (char *)f, 0, sizeof( *f ) );
+
+ /* Look for */
+
+ 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_build() - build a filename given dir/base/suffix/member
+ */
+
+void
+path_build(
+ PATHNAME *f,
+ char *file,
+ int binding )
+{
+ /* 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] != '<' ) *file++ = '<';
+ memcpy( file, f->f_grist.ptr, f->f_grist.len );
+ file += f->f_grist.len;
+ if( file[-1] != '>' ) *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 */
+
+ {
+ memcpy( file, f->f_root.ptr, f->f_root.len );
+ file += f->f_root.len;
+ *file++ = PATH_DELIM;
+ }
+
+ if( f->f_dir.len )
+ {
+ memcpy( file, f->f_dir.ptr, f->f_dir.len );
+ file += 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 && f->f_dir.ptr[0] == PATH_DELIM ) )
+ *file++ = PATH_DELIM;
+ }
+
+ if( f->f_base.len )
+ {
+ memcpy( file, f->f_base.ptr, f->f_base.len );
+ file += f->f_base.len;
+ }
+
+ if( f->f_suffix.len )
+ {
+ memcpy( file, f->f_suffix.ptr, f->f_suffix.len );
+ file += f->f_suffix.len;
+ }
+
+ if( f->f_member.len )
+ {
+ *file++ = '(';
+ memcpy( file, f->f_member.ptr, f->f_member.len );
+ file += f->f_member.len;
+ *file++ = ')';
+ }
+ *file = 0;
+}
+
+/*
+ * 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 /* unix, NT, OS/2, AmigaOS */
diff --git a/src/tools/jam/pathvms.c b/src/tools/jam/pathvms.c
new file mode 100644
index 0000000000..c0140cca79
--- /dev/null
+++ b/src/tools/jam/pathvms.c
@@ -0,0 +1,430 @@
+/*
+ * 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 "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, *q;
+ char *end;
+
+ memset( (char *)f, 0, sizeof( *f ) );
+
+ /* Look for */
+
+ 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,
+ char *file,
+ int binding )
+{
+ char *ofile = file;
+ struct dirinf root, dir;
+ int g;
+
+ /* 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] != '<' ) *file++ = '<';
+ memcpy( file, f->f_grist.ptr, f->f_grist.len );
+ file += f->f_grist.len;
+ if( file[-1] != '>' ) *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 */
+ memcpy( file, f->f_dir.ptr, f->f_dir.len );
+ file += f->f_dir.len;
+ break;
+
+ case G_ROOT:
+ /* take root */
+ memcpy( file, f->f_root.ptr, f->f_root.len );
+ file += f->f_root.len;
+ break;
+
+ case G_VAD:
+ /* root's dev + abs directory */
+ memcpy( file, root.dev.ptr, root.dev.len );
+ file += root.dev.len;
+ memcpy( file, dir.dir.ptr, dir.dir.len );
+ file += dir.dir.len;
+ break;
+
+ case G_DRD:
+ case G_DDD:
+ /* root's dev:[dir] + rel directory */
+ memcpy( file, f->f_root.ptr, f->f_root.len );
+ file += f->f_root.len;
+
+ /* sanity checks: root ends with ] */
+
+ if( file[-1] == ']' )
+ --file;
+
+ /* Add . if separating two -'s */
+
+ if( g == G_DDD )
+ *file++ = '.';
+
+ /* skip [ of dir */
+ memcpy( file, dir.dir.ptr + 1, dir.dir.len - 1 );
+ file += dir.dir.len - 1;
+ break;
+
+ case G_VRD:
+ /* root's dev + rel directory made abs */
+ memcpy( file, root.dev.ptr, root.dev.len );
+ file += root.dev.len;
+ *file++ = '[';
+ /* skip [. of rel dir */
+ memcpy( file, dir.dir.ptr + 2, dir.dir.len - 2 );
+ file += dir.dir.len - 2;
+ break;
+ }
+
+# ifdef DEBUG
+ if( DEBUG_SEARCH && ( root.flags || dir.flags ) )
+ {
+ *file = 0;
+ printf( "%d x %d = %d (%s)\n", root.flags, dir.flags,
+ grid[ root.flags ][ dir.flags ], ofile );
+ }
+# endif
+
+ /*
+ * Now do the special :P modifier when no file was present.
+ * (none) (none)
+ * [dir1.dir2] [dir1]
+ * [dir] [000000]
+ * [.dir] []
+ * [] []
+ */
+
+ if( file[-1] == ']' && f->parent )
+ {
+ while( file-- > ofile )
+ {
+ if( *file == '.' )
+ {
+ *file++ = ']';
+ break;
+ }
+ else if( *file == '-' )
+ {
+ /* handle .- or - */
+ if( file > ofile && file[-1] == '.' )
+ --file;
+ *file++ = ']';
+ break;
+ }
+ else if( *file == '[' )
+ {
+ if( file[1] == ']' )
+ {
+ file += 2;
+ }
+ else
+ {
+ strcpy( file, "[000000]" );
+ file += 8;
+ }
+ break;
+ }
+ }
+ }
+
+ /* Now copy the file pieces. */
+
+ if( f->f_base.len )
+ {
+ memcpy( file, f->f_base.ptr, f->f_base.len );
+ file += 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 )
+ {
+ memcpy( file, f->f_suffix.ptr, f->f_suffix.len );
+ file += f->f_suffix.len;
+ }
+ else if( binding && f->f_base.len )
+ {
+ *file++ = '.';
+ }
+
+ if( f->f_member.len )
+ {
+ *file++ = '(';
+ memcpy( file, f->f_member.ptr, f->f_member.len );
+ file += f->f_member.len;
+ *file++ = ')';
+ }
+ *file = 0;
+
+# 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,
+ ofile );
+# 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/src/tools/jam/regexp.c b/src/tools/jam/regexp.c
new file mode 100644
index 0000000000..92ee8efd8a
--- /dev/null
+++ b/src/tools/jam/regexp.c
@@ -0,0 +1,1317 @@
+/*
+ * 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 "regexp.h"
+#include
+#include
+#ifndef ultrix
+#include
+#endif
+#include
+
+/*
+ * 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; ®dummy = 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 = ®dummy;
+ 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 *)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;
+ 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 == ®dummy) {
+ 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 != ®dummy)
+ *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 == ®dummy) {
+ 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 == ®dummy)
+ 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 == ®dummy || 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) == 0)
+ 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 don't -- 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 don't
+ * 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 )
+{
+ register 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 == ®dummy)
+ 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/src/tools/jam/regexp.h b/src/tools/jam/regexp.h
new file mode 100644
index 0000000000..40b7538a07
--- /dev/null
+++ b/src/tools/jam/regexp.h
@@ -0,0 +1,26 @@
+/*
+ * Definitions etc. for regexp(3) routines.
+ *
+ * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof],
+ * not the System V one.
+ */
+#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
diff --git a/src/tools/jam/rules.c b/src/tools/jam/rules.c
new file mode 100644
index 0000000000..dc3f8dbc56
--- /dev/null
+++ b/src/tools/jam/rules.c
@@ -0,0 +1,267 @@
+/*
+ * 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"
+
+/*
+ * 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
+ * touchtarget() - 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
+ * usesettings() - set all target specific variables
+ * pushsettings() - set all target specific variables
+ * popsettings() - reset target specific variables to their pre-push values
+ * freesettings() - delete a settings list
+ * donerules() - 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 struct hash *rulehash = 0;
+static struct hash *targethash = 0;
+
+
+/*
+ * bindrule() - return pointer to RULE, creating it if necessary
+ */
+
+RULE *
+bindrule( char *rulename )
+{
+ RULE rule, *r = &rule;
+
+ if( !rulehash )
+ rulehash = hashinit( sizeof( RULE ), "rules" );
+
+ r->name = rulename;
+
+ if( hashenter( rulehash, (HASHDATA **)&r ) )
+ {
+ r->name = newstr( rulename ); /* never freed */
+ r->procedure = (PARSE *)0;
+ r->actions = (char *)0;
+ r->bindlist = L0;
+ r->flags = 0;
+ }
+
+ return r;
+}
+
+/*
+ * bindtarget() - return pointer to TARGET, creating it if necessary
+ */
+
+TARGET *
+bindtarget( char *targetname )
+{
+ TARGET target, *t = ⌖
+
+ if( !targethash )
+ targethash = hashinit( sizeof( TARGET ), "targets" );
+
+ t->name = targetname;
+
+ if( hashenter( targethash, (HASHDATA **)&t ) )
+ {
+ memset( (char *)t, '\0', sizeof( *t ) );
+ t->name = newstr( targetname ); /* never freed */
+ t->boundname = t->name; /* default for T_FLAG_NOTFILE */
+ }
+
+ return t;
+}
+
+/*
+ * touchtarget() - mark a target to simulate being new
+ */
+
+void
+touchtarget( 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 *targets )
+{
+ for( ; targets; targets = list_next( targets ) )
+ chain = targetentry( chain, bindtarget( targets->string ) );
+
+ return chain;
+}
+
+/*
+ * targetentry() - add a TARGET to a chain of TARGETS
+ *
+ * Inputs:
+ * chain exisitng TARGETS to append to
+ * target new target to append
+ */
+
+TARGETS *
+targetentry(
+ TARGETS *chain,
+ TARGET *target )
+{
+ TARGETS *c;
+
+ c = (TARGETS *)malloc( sizeof( TARGETS ) );
+ c->target = target;
+
+ if( !chain ) chain = c;
+ else chain->tail->next = c;
+ chain->tail = c;
+ c->next = 0;
+
+ return chain;
+}
+
+/*
+ * actionlist() - append to an ACTION chain
+ */
+
+ACTIONS *
+actionlist(
+ ACTIONS *chain,
+ ACTION *action )
+{
+ ACTIONS *actions = (ACTIONS *)malloc( sizeof( ACTIONS ) );
+
+ actions->action = action;
+
+ if( !chain ) chain = actions;
+ else chain->tail->next = actions;
+ chain->tail = actions;
+ actions->next = 0;
+
+ return chain;
+}
+
+/*
+ * addsettings() - add a deferred "set" command to a target
+ *
+ * Adds a variable setting (varname=list) onto a chain of settings
+ * for a particular target. Replaces the previous previous value,
+ * if any, unless 'append' says to append the new list onto the old.
+ * Returns the head of the chain of settings.
+ */
+
+SETTINGS *
+addsettings(
+ SETTINGS *head,
+ int append,
+ char *symbol,
+ LIST *value )
+{
+ SETTINGS *v;
+
+ /* Look for previous setting */
+
+ 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 *)malloc( sizeof( *v ) );
+ v->symbol = newstr( symbol );
+ v->value = value;
+ v->next = head;
+ head = v;
+ }
+ else if( append )
+ {
+ v->value = list_append( v->value, value );
+ }
+ else
+ {
+ list_free( v->value );
+ v->value = 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 */
+}
+
+/*
+ * freesettings() - delete a settings list
+ */
+
+void
+freesettings( SETTINGS *v )
+{
+ while( v )
+ {
+ SETTINGS *n = v->next;
+
+ freestr( v->symbol );
+ list_free( v->value );
+ free( (char *)v );
+
+ v = n;
+ }
+}
+
+/*
+ * donerules() - free RULE and TARGET tables
+ */
+
+void
+donerules()
+{
+ hashdone( rulehash );
+ hashdone( targethash );
+}
diff --git a/src/tools/jam/rules.h b/src/tools/jam/rules.h
new file mode 100644
index 0000000000..40503d97a2
--- /dev/null
+++ b/src/tools/jam/rules.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * 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 - a file or "thing" 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 */
+
+struct _rule {
+ char *name;
+ PARSE *procedure; /* parse tree from RULE */
+ char *actions; /* command string from ACTIONS */
+ LIST *bindlist; /* variable to bind for actions */
+ int flags; /* modifiers on ACTIONS */
+
+# define RULE_UPDATED 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 /* don't mention it unless verbose */
+# define RULE_PIECEMEAL 0x10 /* split exec so each $(>) is small */
+# define RULE_EXISTING 0x20 /* $(>) is pre-exisitng sources only */
+
+} ;
+
+/* 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() */
+} ;
+
+/* TARGETS - a chain of TARGETs */
+
+struct _targets {
+ TARGETS *next;
+ TARGETS *tail; /* valid only for head */
+ TARGET *target;
+} ;
+
+/* TARGET - a file or "thing" 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 */
+
+ char flags; /* status info */
+
+# define T_FLAG_TEMP 0x01 /* TEMPORARY applied */
+# define T_FLAG_NOCARE 0x02 /* NOCARE applied */
+# define T_FLAG_NOTFILE 0x04 /* NOTFILE applied */
+# define T_FLAG_TOUCHED 0x08 /* ALWAYS applied or -t target */
+# define T_FLAG_LEAVES 0x10 /* LEAVES applied */
+# define T_FLAG_NOUPDATE 0x20 /* NOUPDATE applied */
+
+ char binding; /* how target relates to real file */
+
+# define T_BIND_UNBOUND 0 /* a disembodied name */
+# define T_BIND_MISSING 1 /* couldn't find real file */
+# define T_BIND_PARENTS 2 /* using parent's timestamp */
+# define T_BIND_EXISTS 3 /* real file, timestamp valid */
+
+ TARGETS *deps[2]; /* dependencies */
+
+# define T_DEPS_DEPENDS 0 /* due to DEPENDS */
+# define T_DEPS_INCLUDES 1 /* due to INCLUDES */
+
+ time_t time; /* update time */
+ time_t leaf; /* update time of leaf sources */
+ time_t htime; /* header's time */
+ time_t hleaf; /* update time of leaf headers */
+
+ char fate; /* make0()'s diagnosis */
+ char hfate; /* collected fate for headers */
+
+# define T_FATE_INIT 0 /* nothing done to target */
+# define T_FATE_MAKING 1 /* make0(target) on stack */
+
+# define T_FATE_STABLE 2 /* target didn't 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_MISSING 6 /* is missing, needs updating */
+# define T_FATE_OUTDATED 7 /* is out of date, needs updating */
+# define T_FATE_UPDATE 8 /* deps updated, needs updating */
+
+# define T_FATE_BROKEN 9 /* >= BROKEN ruins parents */
+# define T_FATE_CANTFIND 9 /* no rules to make missing target */
+# define T_FATE_CANTMAKE 10 /* can't find dependents */
+
+ 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 */
+
+ char status; /* execcmd() result */
+
+ int asynccnt; /* child deps outstanding */
+ TARGETS *parents; /* used by make1() for completion */
+ char *cmds; /* type-punned command list */
+} ;
+
+RULE *bindrule( char *rulename );
+TARGET *bindtarget( char *targetname );
+void touchtarget( char *t );
+TARGETS *targetlist( TARGETS *chain, LIST *targets );
+TARGETS *targetentry( TARGETS *chain, TARGET *target );
+ACTIONS *actionlist( ACTIONS *chain, ACTION *action );
+SETTINGS *addsettings( SETTINGS *head, int append, char *symbol, LIST *value );
+void pushsettings( SETTINGS *v );
+void popsettings( SETTINGS *v );
+void freesettings( SETTINGS *v );
+void donerules();
diff --git a/src/tools/jam/scan.c b/src/tools/jam/scan.c
new file mode 100644
index 0000000000..38c1d3ec13
--- /dev/null
+++ b/src/tools/jam/scan.c
@@ -0,0 +1,393 @@
+/*
+ * 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 *s );
+
+# 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 )
+{
+ if( incp )
+ printf( "%s: line %d: ", incp->fname, incp->line );
+
+ printf( "%s at %s\n", s, symdump( &yylval ) );
+
+ ++anyerrors;
+}
+
+int
+yyanyerrors()
+{
+ return anyerrors != 0;
+}
+
+void
+yyfparse( char *s )
+{
+ struct include *i = (struct include *)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're reading from an internal string list, go to the */
+ /* next string. */
+
+ if( i->strings )
+ {
+ if( !*i->strings )
+ goto next;
+
+ i->line++;
+ i->string = *(i->strings++);
+ return *i->string++;
+ }
+
+ /* 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's 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++;
+ }
+
+ next:
+ /* 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 );
+ 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();
+ }
+
+ /* We ate the ending brace -- regurgitate it. */
+
+ if( c != EOF )
+ yyprev();
+
+ /* Check 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 );
+
+ }
+ 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? Swallow up comment line. */
+
+ if( c != '#' )
+ break;
+ while( ( c = yychar() ) != EOF && c != '\n' )
+ ;
+ }
+
+ /* c now points to the first character of a token. */
+
+ if( c == EOF )
+ goto eof;
+
+ /* 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 */
+ *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 */
+ /* don't scan if it's obviously not a keyword or if its */
+ /* 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:
+ 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;
+}
diff --git a/src/tools/jam/scan.h b/src/tools/jam/scan.h
new file mode 100644
index 0000000000..37b026f1f8
--- /dev/null
+++ b/src/tools/jam/scan.h
@@ -0,0 +1,52 @@
+/*
+ * 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;
+} YYSTYPE;
+
+extern YYSTYPE yylval;
+
+void yymode( int n );
+void yyerror( char *s );
+int yyanyerrors();
+void yyfparse( char *s );
+int yyline();
+int yylex();
+int yyparse();
+
+# define SCAN_NORMAL 0 /* normal parsing */
+# define SCAN_STRING 1 /* look only for matching } */
+# define SCAN_PUNCT 2 /* only punctuation keywords */
diff --git a/src/tools/jam/search.c b/src/tools/jam/search.c
new file mode 100644
index 0000000000..c260c2a01f
--- /dev/null
+++ b/src/tools/jam/search.c
@@ -0,0 +1,85 @@
+/*
+ * 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 "search.h"
+# include "timestamp.h"
+# include "pathsys.h"
+# include "variable.h"
+# include "newstr.h"
+
+/*
+ * search.c - find a target along $(SEARCH) or $(LOCATE)
+ */
+
+char *
+search(
+ char *target,
+ time_t *time )
+{
+ PATHNAME f[1];
+ LIST *varlist;
+ char buf[ MAXJPATH ];
+
+ /* 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 );
+
+ timestamp( buf, time );
+
+ return newstr( buf );
+ }
+ else if( varlist = var_get( "SEARCH" ) )
+ {
+ while( varlist )
+ {
+ f->f_root.ptr = varlist->string;
+ f->f_root.len = strlen( varlist->string );
+
+ path_build( f, buf, 1 );
+
+ if( DEBUG_SEARCH )
+ printf( "search %s: %s\n", target, buf );
+
+ timestamp( buf, time );
+
+ if( *time )
+ return newstr( buf );
+
+ varlist = list_next( varlist );
+ }
+ }
+
+ /* 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;
+
+ path_build( f, buf, 1 );
+
+ if( DEBUG_SEARCH )
+ printf( "search %s: %s\n", target, buf );
+
+ timestamp( buf, time );
+
+ return newstr( buf );
+}
diff --git a/src/tools/jam/search.h b/src/tools/jam/search.h
new file mode 100644
index 0000000000..bb8035723c
--- /dev/null
+++ b/src/tools/jam/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 );
diff --git a/src/tools/jam/timestamp.c b/src/tools/jam/timestamp.c
new file mode 100644
index 0000000000..a5bbcc8c40
--- /dev/null
+++ b/src/tools/jam/timestamp.c
@@ -0,0 +1,203 @@
+/*
+ * 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 "hash.h"
+# include "filesys.h"
+# include "pathsys.h"
+# include "timestamp.h"
+# include "newstr.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 )
+{
+ PATHNAME f1, f2;
+ BINDING binding, *b = &binding;
+ char buf[ MAXJPATH ];
+
+# ifdef DOWNSHIFT_PATHS
+ char path[ MAXJPATH ];
+ char *p = path;
+
+ do *p++ = tolower( *target );
+ while( *target++ );
+
+ target = path;
+# endif
+
+ 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, *b = &binding;
+
+ f2 = f1;
+ f2.f_grist.len = 0;
+ path_parent( &f2 );
+ path_build( &f2, buf, 0 );
+
+ b->name = buf;
+ b->time = b->flags = 0;
+ b->progress = BIND_INIT;
+
+ if( hashenter( bindhash, (HASHDATA **)&b ) )
+ b->name = newstr( buf ); /* never freed */
+
+ if( !( b->flags & BIND_SCANNED ) )
+ {
+ file_dirscan( buf, time_enter, bindhash );
+ b->flags |= BIND_SCANNED;
+ }
+ }
+
+ /* Scan archive if not already done so */
+
+ if( f1.f_member.len )
+ {
+ BINDING binding, *b = &binding;
+
+ f2 = f1;
+ f2.f_grist.len = 0;
+ f2.f_member.len = 0;
+ path_build( &f2, buf, 0 );
+
+ b->name = buf;
+ b->time = b->flags = 0;
+ b->progress = BIND_INIT;
+
+ if( hashenter( bindhash, (HASHDATA **)&b ) )
+ b->name = newstr( buf ); /* never freed */
+
+ if( !( b->flags & BIND_SCANNED ) )
+ {
+ file_archscan( buf, time_enter, bindhash );
+ b->flags |= BIND_SCANNED;
+ }
+ }
+
+ afterscanning:
+
+ if( b->progress == BIND_SPOTTED )
+ {
+ if( file_time( b->name, &b->time ) < 0 )
+ b->progress = BIND_MISSING;
+ else
+ b->progress = BIND_FOUND;
+ }
+
+ *time = b->progress == BIND_FOUND ? b->time : 0;
+}
+
+static void
+time_enter(
+ void *closure,
+ char *target,
+ int found,
+ time_t time )
+{
+ 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] );
+}
+
+/*
+ * donestamps() - free timestamp tables
+ */
+
+void
+donestamps()
+{
+ hashdone( bindhash );
+}
diff --git a/src/tools/jam/timestamp.h b/src/tools/jam/timestamp.h
new file mode 100644
index 0000000000..f290f82cb2
--- /dev/null
+++ b/src/tools/jam/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 donestamps();
diff --git a/src/tools/jam/variable.c b/src/tools/jam/variable.c
new file mode 100644
index 0000000000..d62e4c19cf
--- /dev/null
+++ b/src/tools/jam/variable.c
@@ -0,0 +1,341 @@
+/*
+ * 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 "variable.h"
+# include "expand.h"
+# include "hash.h"
+# include "newstr.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_defines() - load a bunch of variable=value settings
+ *
+ * If variable name ends in PATH, split value at :'s.
+ * Otherwise, split at blanks.
+ */
+
+void
+var_defines( char **e )
+{
+ for( ; *e; e++ )
+ {
+ char *val;
+
+ /* Just say "no": windows defines this in the env, */
+ /* but we don't want it to override our notion of OS. */
+
+ if( !strcmp( *e, "OS=Windows_NT" ) )
+ continue;
+
+# 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, *p;
+# ifdef OS_MAC
+ char split = ',';
+# else
+ char split = ' ';
+# endif
+ char buf[ MAXSYM ];
+
+ /* 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; p = strchr( pp, split ); pp = p + 1 )
+ {
+ strncpy( buf, pp, p - pp );
+ buf[ p - pp ] = '\0';
+ l = list_new( l, newstr( buf ) );
+ }
+
+ l = list_new( l, newstr( pp ) );
+
+ /* Get name */
+
+ strncpy( buf, *e, val - *e );
+ buf[ val - *e ] = '\0';
+
+ var_set( buf, l, VAR_SET );
+ }
+ }
+}
+
+/*
+ * 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++;
+ }
+
+ /* If a variable encountered, expand it and and embed the */
+ /* space-separated members of the list in the output. */
+
+ if( dollar )
+ {
+ LIST *l;
+
+ l = var_expand( L0, lastword, out, lol, 0 );
+
+ out = lastword;
+
+ for( ; l; l = list_next( l ) )
+ {
+ int so = strlen( l->string );
+
+ if( out + so >= oute )
+ return -1;
+
+ strcpy( out, l->string );
+ out += so;
+ *out++ = ' ';
+ }
+
+ list_free( l );
+ }
+ }
+
+ if( out >= oute )
+ return -1;
+
+ *out++ = '\0';
+
+ return out - out0;
+}
+
+/*
+ * var_get() - get value of a user defined symbol
+ *
+ * Returns NULL if symbol unset.
+ */
+
+LIST *
+var_get( char *symbol )
+{
+ VARIABLE var, *v = &var;
+
+ v->symbol = symbol;
+
+ if( varhash && hashcheck( varhash, (HASHDATA **)&v ) )
+ {
+ if( DEBUG_VARGET )
+ var_dump( v->symbol, v->value, "get" );
+ return v->value;
+ }
+
+ return 0;
+}
+
+/*
+ * 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, *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
+ */
+
+void
+var_done()
+{
+ hashdone( varhash );
+}
diff --git a/src/tools/jam/variable.h b/src/tools/jam/variable.h
new file mode 100644
index 0000000000..77b89f1eaa
--- /dev/null
+++ b/src/tools/jam/variable.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 1993, 2000 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * variable.h - handle jam multi-element variables
+ */
+
+void var_defines( char **e );
+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();
+
+/*
+ * 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/src/tools/jam/yyacc b/src/tools/jam/yyacc
new file mode 100755
index 0000000000..f566868dda
--- /dev/null
+++ b/src/tools/jam/yyacc
@@ -0,0 +1,89 @@
+#!/bin/sh
+
+# 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
+
+outy=${1?}
+outh=${2?}
+in=${3?}
+out=`basename $in .yy`
+
+T=/tmp/yy$$
+trap 'rm -f $T.*' 0
+
+sed '
+ : 1
+ /`/{
+ h
+ s/[^`]*`\([^`]*\)`.*/\1/
+ p
+ g
+ s/[^`]*`[^`]*`//
+ b 1
+ }
+ d
+' $in | sort -u | sed '
+ h
+ y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
+ s/:/_COLON/
+ s/!/_BANG/
+ s/&&/_AMPERAMPER/
+ s/&/_AMPER/
+ s/+/_PLUS/
+ s/||/_BARBAR/
+ s/|/_BAR/
+ s/;/_SEMIC/
+ s/-/_MINUS/
+ s//_RANGLE/
+ s/\./_PERIOD/
+ s/?/_QUESTION/
+ s/=/_EQUALS/
+ s/,/_COMMA/
+ s/\[/_LBRACKET/
+ s/]/_RBRACKET/
+ s/{/_LBRACE/
+ s/}/_RBRACE/
+ s/(/_LPAREN/
+ s/)/_RPAREN/
+ s/.*/&_t/
+ G
+ s/\n/ /
+' > $T.1
+
+sed '
+ s:^\(.*\) \(.*\)$:s/`\2`/\1/g:
+ s:\.:\\.:g
+ s:\[:\\[:g
+' $T.1 > $T.s
+
+rm -f $outy $outh
+
+(
+ sed 's:^\(.*\) \(.*\)$:%token \1:' $T.1
+ sed -f $T.s $in
+) > $outy
+
+(
+ sed 's:^\(.*\) \(.*\)$: { "\2", \1 },:' $T.1
+) > $outh