3514 lines
		
	
	
		
			114 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			3514 lines
		
	
	
		
			114 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
#define ROOT_DIR "..\\"
 | 
						|
//
 | 
						|
// BUILD 4.1A
 | 
						|
//
 | 
						|
// Copyright (c) 1994, 1995 Greenleaf Software, Inc.  All Rights Reserved
 | 
						|
//
 | 
						|
// June 8, 1995  4.0C
 | 
						|
//
 | 
						|
//  Modified the get first/get next stuff to support native mode OS/2
 | 
						|
//  compilation of BUILD.CPP
 | 
						|
//
 | 
						|
// January 31, 1995 4.0B
 | 
						|
//
 | 
						|
// One last change.  This version of BUILD supports batch mode compilation.
 | 
						|
// If your compiler can handle a CMD file with a list of a zillion files,
 | 
						|
// we can supply it.  This often speeds up the build process, because
 | 
						|
// the compiler is only loaded once, instead of once per file.
 | 
						|
//
 | 
						|
// Batch mode operation has a taint of kludgeness about it.  There
 | 
						|
// are three hardcoded compilation passes: C, CPP, and ASM.  During the
 | 
						|
// stage where we build config files for the compilation, BUILD pays
 | 
						|
// special attention to any config files that have the string "*.c",
 | 
						|
// "*.cpp", or "*.asm" in their contents.  If it sees those strings,
 | 
						|
// it does two things.  First, it expands the line with "*.xxx" on it,
 | 
						|
// adding the line to the config file once per file.  Second, it switches
 | 
						|
// the appropriate compiler command from ONE_AT_A_TIME to BATCH.
 | 
						|
//
 | 
						|
// When it comes time to compile, any command that was switched to batch
 | 
						|
// mode will be executed once and only once, with no file names added
 | 
						|
// to the processing line.  A typical command might be: BCC @CLIB.CMD.
 | 
						|
//
 | 
						|
// The batch flag is in the item class, which means even non-command keys
 | 
						|
// such as DeleteFiles and Description are set up to run one at a time
 | 
						|
// or batch mode.  This indicates a lack of rigor in the class design,
 | 
						|
// mea culpa.
 | 
						|
//
 | 
						|
// January 26, 1995 4.0
 | 
						|
//
 | 
						|
// Yet another major personality change for BUILD.EXE.  I targeted
 | 
						|
// this release with simplifying the program.  In release 3.0, I
 | 
						|
// modified BUILD to create EXEs and LIBraries, but it added quite
 | 
						|
// a bit of complexity to the program.  This release still has both
 | 
						|
// of those capabilities, but the implementation has changed quite
 | 
						|
// a bit.
 | 
						|
//
 | 
						|
// BUILD now operates like two completely different programs that
 | 
						|
// share a common infrastructore.  If you are building libraries,
 | 
						|
// as indicated by the presence of "-lib" on the command line,
 | 
						|
// BUILD first compiles all of your source code, then builds
 | 
						|
// your library and/or DLL.  Compilation is done by running
 | 
						|
// the command lines specified by the CPP, C, and ASM key values.
 | 
						|
// Libraries are built by the LIB key values.  This means we no longer
 | 
						|
// have separate key values for the linker and the librarion, the are
 | 
						|
// all combined into LIB.  This release of BUILD allows you to specify
 | 
						|
// multiple commands for a specific tag, so when building a DLL you
 | 
						|
// might have LIB broken into three or four steps.
 | 
						|
//
 | 
						|
// If you are building an executable, presumably a demo program, build
 | 
						|
// takes a completely different approach.  It doesn't do any precompilation
 | 
						|
// on the file list.  Instead, it executes all the EXE tag values for
 | 
						|
// each file.  DOS programs may only use 1 EXE line, but building a Windows
 | 
						|
// application might take three or four lines.
 | 
						|
//
 | 
						|
// The biggest change to this program is in the area of configuration
 | 
						|
// files.  BUILD used to automatically build config files, using names
 | 
						|
// like CPP.CMD to hold the options from the CppOptions key value.  Now,
 | 
						|
// BUILD requires you to specify exactly which CMD files you want to BUILD.
 | 
						|
// When you are building libraries, it will create all CMD files that are
 | 
						|
// defined using a key value that looks like this: *LIB.CMD.  When building
 | 
						|
// EXE files, it will create all CMD files that are defined using a key
 | 
						|
// value that looks like this: *EXE.CMD.
 | 
						|
//
 | 
						|
// Config files for libraries are built once, before any compilation takes
 | 
						|
// place.  This means that you can't include the name of a specific file
 | 
						|
// that you are compiling in the config file, it has to be listed on the
 | 
						|
// command line.  Config files can have multiple lines, just by creating
 | 
						|
// multiple lines with the same key in the INI file.
 | 
						|
//
 | 
						|
// The most exciting thing about config files built for libraries is the
 | 
						|
// ability to put all your file names into one config file dynamically.
 | 
						|
// As BUILD is scanning the input lines for the config file, it looks for
 | 
						|
// %file*% strings inside the file.  If it finds it, it creates a line
 | 
						|
// for every single file in the input list.  This is how we create LIB.CMD,
 | 
						|
// which is used to feed file names to the librarian.
 | 
						|
//
 | 
						|
// When building CONFIG files for example programs, this file name
 | 
						|
// expansion isn't done.  Instead, a config file is build for each
 | 
						|
// demo program being built, and file name substitutions are performed
 | 
						|
// just as they would be for any other arguments.
 | 
						|
//
 | 
						|
// The final item in the new BUILD is the argument substitution syntax.
 | 
						|
// There are a bunch of arguments in INI files that get substituted
 | 
						|
// when executing command lines or when building config files.  In
 | 
						|
// BUILD 3.0 these were a bunch of single letter codes escaped with
 | 
						|
// a '%' symbol.  In build 4.0, I have modified these to be actual
 | 
						|
// strings enclosed in a pair of '%' symbols.  The goal is to make the
 | 
						|
// INI file more readable.  Don't know if I succeeded or not.  For the
 | 
						|
// record, the current crop of arguments that will be substituted in
 | 
						|
// command lines are:
 | 
						|
//
 | 
						|
//  %file%          :  The complete name of the source file.  For example,
 | 
						|
//                     when building libraries, this string will be turned
 | 
						|
//                     into a name something like: ..\C_W16\TW_W16.C
 | 
						|
//
 | 
						|
//  %file.path%     :  The drive and path of the file, with a guaranteed
 | 
						|
//                     '\' character.  In the above example, this would
 | 
						|
//                     yield: "..\C_W16\".  If the file is in the current
 | 
						|
//                     directory, you get ".\".
 | 
						|
//
 | 
						|
//  %file.name%     :  The name component of the source file, minus an
 | 
						|
//                     extension.  If my source file is "EXAMP00.C", this
 | 
						|
//                     argument will be converted to "EXAMP00".  We use
 | 
						|
//                     this argument a lot for commands passed to the
 | 
						|
//                     librarian or linker.
 | 
						|
//
 | 
						|
//  %model%         :  This string yields the model name, as defined in the
 | 
						|
//                     "[]" string in the INI file.  For example, a small
 | 
						|
//                     model build will return the "S" string.  The commlib
 | 
						|
//                     6.0 DLL will probably return "600".  This may or
 | 
						|
//                     may not have anything to do with the actual memory
 | 
						|
//                     model in use by the compiler.  By convention, normal
 | 
						|
//                     models will often use the same first initial that
 | 
						|
//                     is used to pass the memory model to the compiler.
 | 
						|
//
 | 
						|
//  %model.lower%   :  Borland usually takes the model specification for
 | 
						|
//                     the compiler with a lower case letter, generally
 | 
						|
//                     chosen from s, m, c, l, h.  This expression will
 | 
						|
//                     yield those letters by taken the first letter from
 | 
						|
//                     the model name, and converting it to lower case.
 | 
						|
//
 | 
						|
//  %model.upper%   :  Same deal, but converts to upper case instead.
 | 
						|
//                     Microsoft usually wants model letters in upper
 | 
						|
//                     case.  No accounting for taste.
 | 
						|
//
 | 
						|
//  %model.asm%     :  This creates a string suitable for passing to
 | 
						|
//                     MASM or TASM with the /DGF_MODEL=xxx option.
 | 
						|
//                     Note that it figures this out based on the
 | 
						|
//                     first character of the model name.  Values
 | 
						|
//                     other than s, m, c, l, and h give a bad result,
 | 
						|
//                     because we can't figure out what the memory
 | 
						|
//                     model should be.
 | 
						|
//
 | 
						|
//  %nl%            :  Inserts a new line into the output stream,
 | 
						|
//                     literally a '\n';
 | 
						|
//
 | 
						|
//  %%              :  Inserts the literal '%' character.
 | 
						|
//
 | 
						|
//  %comspec%       :  Inserts the name of the current command processor.
 | 
						|
//                     We figure this out by looking at the COMSPEC
 | 
						|
//                     environment variable.  If this variable isn't
 | 
						|
//                     defined, "COMMAND.COM" is used.  On my system,
 | 
						|
//                     the substitution yields "C:\4DOS40\4DOS.COM".
 | 
						|
//
 | 
						|
//  %continue%      :  This means the character immediately following
 | 
						|
//                     the %continue% is a continuation character.  If
 | 
						|
//                     there are more files coming after this one, we
 | 
						|
//                     output the continuation character.  Otherwise,
 | 
						|
//                     it gets eaten.
 | 
						|
//
 | 
						|
// To add new tags to the INI file substitutions, just edit the process()
 | 
						|
// function.  It's easy.
 | 
						|
//
 | 
						|
// June 29, 1994  2.1
 | 
						|
//
 | 
						|
//  Lots of changes to BUILD.CPP.  First, I added the template
 | 
						|
//  capability.  You can now define a library template, which is
 | 
						|
//  shared among multiple libraries.  For example, to build DOS
 | 
						|
//  libraries, I have a single DOS template which is shared among
 | 
						|
//  S, SD, C, ...etc.  The include the template by using the
 | 
						|
//  the new identifier Template=xxx.  Template definitions start
 | 
						|
//  usually have a "." right before the name, which prevents them
 | 
						|
//  from being displayed when sections are being dumped.
 | 
						|
//
 | 
						|
//  The second major change in this release of BUILD is the addition
 | 
						|
//  of format specifiers to the keywords.  It used to be that you
 | 
						|
//  could put %s in a command line, and BUILD would substitute the
 | 
						|
//  filename.  It still does that, but now you can put %m for the model
 | 
						|
//  letter in lower case, %M for upper case, %p for the file path and
 | 
						|
//  drive, %n for the name, and %l for the library name.
 | 
						|
//
 | 
						|
//  The third major change is the addition of the BUILDDEMO capability.
 | 
						|
//  Each library now has a Demo identifier, which allows BUILD
 | 
						|
//  to create demo programs using that library.
 | 
						|
//
 | 
						|
//  A minor feature change is that most commands can now appear multiple
 | 
						|
//  times in a section.  Each multiple appearance of a command is
 | 
						|
//  tacked on to the previous ones.  When it comes time to execute the
 | 
						|
//  commands, they are done one at a time.  This is real useful for
 | 
						|
//  building demos that require a compile/rc/link sequence.  See the INI
 | 
						|
//  file for details on how this works.
 | 
						|
//
 | 
						|
//  The Linker line now accepts multiple commands, which need
 | 
						|
//  to be separated by the caret character: ^.  Because of this,
 | 
						|
//  all BUILD.INI files will need to be modified to perform
 | 
						|
//  an explicit IMPLIB.
 | 
						|
//
 | 
						|
// May 18, 1994   2.0
 | 
						|
//
 | 
						|
// This is a completely new version of the Greenleaf Build program.
 | 
						|
// It has the same syntax as the previous version, and works mostly
 | 
						|
// the same way.  The major changes are:
 | 
						|
//
 | 
						|
//   o  This version of BUILD will create DLLs and Import Libraries
 | 
						|
//
 | 
						|
//   o  Instead of hardcoding the information in BUILD.C, this version
 | 
						|
//      of BUILD reads all of its information out of BUILD.INI.
 | 
						|
//
 | 
						|
// One of the major constraints on BUILD 1.0 was that it was required
 | 
						|
// to have as small a memory footprint as possible.  That is why so
 | 
						|
// much of the program was hardcoded instead of flexible.  Now, all of
 | 
						|
// our compilers use DOS Extenders, so we don't care any more how
 | 
						|
// much memory we take up.  This version of BUILD is the result.
 | 
						|
//
 | 
						|
// Much of the functionality in this version of BUILD is found into
 | 
						|
// the bstring [Build String] class.  Some day we may be able to use a
 | 
						|
// standard ANSI string class, but that does not exist yet.
 | 
						|
 | 
						|
//
 | 
						|
// The rules:  All of the information needed by the linker is stored
 | 
						|
//             in BUILD.INI.  The data in BUILD.INI is used to generate
 | 
						|
//             CPPLIB.CMD and LINKLIB.CMD (and others).  LIB.CMD is generated
 | 
						|
//             using a list of file names.
 | 
						|
//
 | 
						|
// "BUILD ?" will create the CMD files, and not delete them
 | 
						|
// afterwards.  You have to delete them yourself.
 | 
						|
//
 | 
						|
// "BUILD -keep" will leave the objects around even if the library
 | 
						|
// builds properly.
 | 
						|
//
 | 
						|
// "BUILD -lib" builds libraries
 | 
						|
//
 | 
						|
// "BUILD -exe" builds demos and examples
 | 
						|
//
 | 
						|
// "BUILD -cmd " performs a dry run, showing you the commands without
 | 
						|
//               actually executing any of them.
 | 
						|
//
 | 
						|
#include <iostream.h>
 | 
						|
#include <iomanip.h>
 | 
						|
#include <fstream.h>
 | 
						|
#include <assert.h>
 | 
						|
#include <string.h>
 | 
						|
#include <stdarg.h>
 | 
						|
#include <dos.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <process.h>
 | 
						|
#include <errno.h>
 | 
						|
#include <conio.h>
 | 
						|
#if defined( __WATCOMC__ )
 | 
						|
#include <io.h> // Had to get unlink from here...
 | 
						|
#endif
 | 
						|
#include <ctype.h>
 | 
						|
#include "build.h"
 | 
						|
//#include "c:/tools/mc30/memcheck.h"
 | 
						|
//
 | 
						|
// int main( int argc, char *argv[] )
 | 
						|
//
 | 
						|
// ARGUMENTS
 | 
						|
//
 | 
						|
//  argc    : The number of arguments being passed on the command line.
 | 
						|
//
 | 
						|
//  argv    : The strings containing the arguments.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  0 if all files were built, and everything else ran without error.
 | 
						|
//  1 if anything bad happened along the way.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  The main routine just dispatches the functions that do all the work
 | 
						|
//  for the build.  It checks the return codes from all the functions
 | 
						|
//  to see if an error has occurredd.  An error at any point will 
 | 
						|
//  stop the process.
 | 
						|
//
 | 
						|
//  Note that this build program really operates in two completely
 | 
						|
//  different modes.  If it is building LIBs, it takes one path.  If
 | 
						|
//  it is building EXEs, it takes a completely different approach.
 | 
						|
//
 | 
						|
//  The process goes like this:
 | 
						|
//
 | 
						|
//        Read in build parameters
 | 
						|
//        Display the arguments on the screen.
 | 
						|
//
 | 
						|
//        If building EXE files:
 | 
						|
//          Expand wild cards to make the list of files to process
 | 
						|
//          Build EXE files using all the EXE lines in the INI file
 | 
						|
//
 | 
						|
//        If building LIB files:
 | 
						|
//          Expand wild cards to make the list of files to process
 | 
						|
//          Build all the config files to be used by the compiler/linker/etc.
 | 
						|
//          Compile all eligible C, CPP, and ASM files in batch mode.
 | 
						|
//          Compile all eligible C, CPP, and ASM files in one-at-a-time mode.
 | 
						|
//          Build the library using all the LIB lines in the INI file.
 | 
						|
//
 | 
						|
//
 | 
						|
//  REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 18, 1994  2.0A      :  First release
 | 
						|
//
 | 
						|
//   January 5, 1955 3.0A    : New release with more capabilities
 | 
						|
//
 | 
						|
//   January 26, 1995  4.0A  : Major changes to simplify the code, and add
 | 
						|
//                             to its capabilities.
 | 
						|
//
 | 
						|
 | 
						|
int main( int argc, char *argv[] )
 | 
						|
{
 | 
						|
//
 | 
						|
// These to auto variables are important.  files will contain a list of all
 | 
						|
// the files to be processed during the build.  The algorithm that chooses
 | 
						|
// which files go in the list is different depending on whether you are
 | 
						|
// building EXEs or LIBs.
 | 
						|
//
 | 
						|
// opts holds all the option lines from the INI file, as well as some of the
 | 
						|
// info regarding command line parameters.  The list of predefined options
 | 
						|
// that are passed to the constructor are the *only* options that can be
 | 
						|
// read into an INI file, with two exceptions.  The exceptions are the
 | 
						|
// *LIB.CMD and *EXE.CMD lines.  You can add as many of those as you care
 | 
						|
// too, as long as the names conform to one of these two schemes.
 | 
						|
//
 | 
						|
    file_list files;
 | 
						|
    options opts( "Description",
 | 
						|
                  "Directories",
 | 
						|
                  "Template",
 | 
						|
                  "Cpp",
 | 
						|
                  "C",
 | 
						|
                  "Asm",
 | 
						|
                  "Lib",
 | 
						|
                  "Exe",
 | 
						|
                  "DeleteFiles",
 | 
						|
                  "Opts",
 | 
						|
                  "" );
 | 
						|
 | 
						|
// Symantec defaults to buffered output on cout and cerr.  Yuk.
 | 
						|
 | 
						|
    cout.rdbuf()->setbuf( (char*) 0, 0 );
 | 
						|
    cerr.rdbuf()->setbuf( (char*) 0, 0 );
 | 
						|
//
 | 
						|
// These two functions are used to parse the command line, then read in the
 | 
						|
// INI file.  Everything of importance that comes out of this process is
 | 
						|
// stored in the opts object.
 | 
						|
//
 | 
						|
    if ( parse_args( argc, argv, opts ) == 0 )
 | 
						|
        return 1;
 | 
						|
    if ( read_ini_file( opts ) == 0 )
 | 
						|
        return 1;
 | 
						|
    dump_options( opts );
 | 
						|
//
 | 
						|
// If I am being asked to build demo programs, I first expand
 | 
						|
// the list of files on the command line.  This is done using
 | 
						|
// simple wild card expansion, I don't do any funny directory
 | 
						|
// path manipulation.  This means that if you want to compile
 | 
						|
// files from the examples directory, you have to type:
 | 
						|
//
 | 
						|
//  BUILD -exe L ..\ex_dos\ex*.c
 | 
						|
//
 | 
						|
    if ( opts.target == options::EXE ) {
 | 
						|
        if ( build_exe_file_list( argc, argv, files ) == 0 )
 | 
						|
            return 1;
 | 
						|
        if ( build_exe_files( opts, files ) == 0 )
 | 
						|
            return 1;
 | 
						|
    }
 | 
						|
//
 | 
						|
// Things are completely different when building libraries.  Building
 | 
						|
// the file list is done by looking in the targeted directories for
 | 
						|
// each file type.  For example, if you want to build -lib s *.C, we
 | 
						|
// might look in ..\C_DOS, ..\C_GSCI, ..\C_**, and ..\C_ALL for all
 | 
						|
// of the *.C files.
 | 
						|
//
 | 
						|
// Building libraries also differs in that the config files are only created
 | 
						|
// once, so we do it here.  In addition, we have two steps to build the
 | 
						|
// library.  The first step involves compiling or assembling every one of
 | 
						|
// the files in the list.  The second step involves turning those OBJs
 | 
						|
// into a library, using all of the command lines specified in the
 | 
						|
// various LIB= lines in the INI file.
 | 
						|
//
 | 
						|
    if ( opts.target == options::LIB ) {
 | 
						|
        if ( build_lib_file_list( argc, argv, files, opts ) == 0 )
 | 
						|
            return 1;
 | 
						|
        build_lib_config_files( opts, files );
 | 
						|
        if ( lib_batch_compiles( opts ) == 0 )
 | 
						|
            return 1;
 | 
						|
        if ( compile_lib_files( opts, files ) == 0 )
 | 
						|
            return 1;
 | 
						|
        if ( build_lib( opts ) == 0 )
 | 
						|
            return 1;
 | 
						|
    }
 | 
						|
    if ( !opts.keep_files )
 | 
						|
        cleanup( files, opts );
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// int parse_args( int &argc, char **&argv, options &opts )
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  argc      :  A reference to the argument count passed on the
 | 
						|
//               command line.  One nice bonus of using C++ is that
 | 
						|
//               using a reference argument makes it easy for the
 | 
						|
//               parse routine to modify argc as it reads arguments,
 | 
						|
//               then pass the modified value back to the calling
 | 
						|
//               routine.
 | 
						|
//
 | 
						|
//  argv      :  A reference to the pointer to the array of command
 | 
						|
//               line arguments.  Like argc, this value gets incremented
 | 
						|
//               as we work our way through the command line.
 | 
						|
//
 | 
						|
//  opts      :  A reference to the options structure used by this program.
 | 
						|
//               This routine has the job of initializing four members:
 | 
						|
//               dry_run, keep_files, target, and model.
 | 
						|
//
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  1 if it is okay to keep going, 0 if a crummy command line got passed.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This routine is called to initialize four of the options used in the
 | 
						|
//  build process.  dry_run is set if the user specifies -cmd.  keep_files
 | 
						|
//  is set if -keep is found, and target is set to EXE if -exe is specified.
 | 
						|
//  The library model is the next argument, it gets stored in a member of
 | 
						|
//  opts, after being converted to upper case.
 | 
						|
//
 | 
						|
//  Note also that I strip any "." character from the name of a model.
 | 
						|
//  The "." character in the first position just prevents the model
 | 
						|
//  name from being displayed when the dump_sections() routine is called.
 | 
						|
//
 | 
						|
//  After parsing the one or two arguments, argc and argv will be updated
 | 
						|
//  so the file name parsing can start right up.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A   : First release
 | 
						|
//
 | 
						|
//   January 5, 1995 3.0A : Added support for the -exe option.
 | 
						|
//
 | 
						|
//   January 26, 1995 4.0A : Added support for the -lib option.  I think to
 | 
						|
//                           be downward compatible I need to make this
 | 
						|
//                           guy use -lib as the default, instead of
 | 
						|
//                           requiring it.
 | 
						|
//
 | 
						|
 | 
						|
int parse_args( int &argc, char **&argv, options &opts )
 | 
						|
{
 | 
						|
    opts.dry_run = 0;
 | 
						|
    opts.keep_files = 0;
 | 
						|
    opts.ignore = 0;
 | 
						|
    opts.target = options::LIB;
 | 
						|
//
 | 
						|
// Instead of blindly looking in BUILD.INI for the INI file, I look
 | 
						|
// for an INI file that has the same name as this executable.  This
 | 
						|
// makes it easy to have BUILDB31.EXE look for BUILDB31.INI, which
 | 
						|
// helps keep our patch program happy.
 | 
						|
//
 | 
						|
    opts.ini_file = *argv;
 | 
						|
    int i = opts.ini_file.last( '.' );
 | 
						|
    if ( i != -1 )
 | 
						|
        opts.ini_file.remove_after( i - 1 );
 | 
						|
    opts.ini_file = opts.ini_file + ".INI";
 | 
						|
    argc--;  // go past argv[ 0 ], which is the program name
 | 
						|
    argv++;
 | 
						|
//
 | 
						|
// Now I strip any of the command line arguments, setting option flags
 | 
						|
// accordingly.
 | 
						|
//
 | 
						|
    while ( argc >= 1 ) {
 | 
						|
        if ( stricmp( *argv, "-cmd" ) == 0 ) {
 | 
						|
            opts.dry_run = 1;
 | 
						|
            argc--;
 | 
						|
            argv++;
 | 
						|
        } else if ( stricmp( *argv, "-keep" ) == 0 ) {
 | 
						|
            opts.keep_files = 1;
 | 
						|
            argc--;
 | 
						|
            argv++;
 | 
						|
        } else if ( stricmp( *argv, "-ignore" ) == 0 ) {
 | 
						|
            opts.ignore = 1;
 | 
						|
            argc--;
 | 
						|
            argv++;
 | 
						|
        } else if ( stricmp( *argv, "-exe" ) == 0 ) {
 | 
						|
            opts.target = options::EXE;
 | 
						|
            argc--;
 | 
						|
            argv++;
 | 
						|
        } else if ( stricmp( *argv, "-lib" ) == 0 ) {
 | 
						|
            opts.target = options::LIB;
 | 
						|
            argc--;
 | 
						|
            argv++;
 | 
						|
        } else if ( *argv[0]=='-' || *argv[0]=='/' ) {
 | 
						|
            bstring *stored_value = opts.find_value( "Opts" );
 | 
						|
            if ( stored_value ) {
 | 
						|
                if ( strlen( *stored_value ) > 0 )
 | 
						|
                    *stored_value= *stored_value + " ";
 | 
						|
                *stored_value = *stored_value +*argv;
 | 
						|
            }
 | 
						|
            argc--;
 | 
						|
            argv++;
 | 
						|
        } else
 | 
						|
            break;
 | 
						|
    }
 | 
						|
//
 | 
						|
// Print the command line format if you didn't get anything good on the
 | 
						|
// command line.  At a minimum, I expect to get a memory model, which
 | 
						|
// would mean that argc would have to be 1 or more.
 | 
						|
//
 | 
						|
    if ( argc < 1 || opts.target == options::UNKNOWN ) {
 | 
						|
        cerr << "\nBuild Version 4.1B\n";
 | 
						|
        cerr << "Copyright (c) 1994, 1995 Greenleaf Software, Inc.\n";
 | 
						|
        cerr << "Usage: Build -exe | -lib [-cmd] [-keep] model [file(s)]\n\n";
 | 
						|
        cerr << "Options:\n";
 | 
						|
        cerr << "-cmd    Create CMD files and quit\n";
 | 
						|
        cerr << "-keep   Keep OBJ and CMD files after build\n";
 | 
						|
        cerr << "-lib    Build a library or DLL\n";
 | 
						|
        cerr << "-exe    Make an example or a demo, not a library\n";
 | 
						|
        cerr << "-ignore Ignore EXE and OBJ files that already exist\n";
 | 
						|
        cerr << "\n";
 | 
						|
        cerr << "Any other command switches get added to the opts variable.\n";
 | 
						|
        cerr << "\n";
 | 
						|
        cerr << "Note:  If you are building a library, you shouldn't\n";
 | 
						|
        cerr << "       include path names for your source files.  The\n";
 | 
						|
        cerr << "       BUILD program will look in the appropriate\n";
 | 
						|
        cerr << "       directories for the memory model you are building.\n";
 | 
						|
        cerr << "       Building demo files requires a path to the file.\n";
 | 
						|
        cerr << "\nIni file = " << opts.ini_file << "\n";
 | 
						|
        cerr << "\nHit any key to continue...";
 | 
						|
        getch();
 | 
						|
        cerr << "\nModels:\n";
 | 
						|
        dump_sections( opts );
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
//
 | 
						|
// The next argument should be the memory model.
 | 
						|
//
 | 
						|
    opts.model = *argv;
 | 
						|
    opts.model.upper();
 | 
						|
    argc--;
 | 
						|
    argv++;
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// void dump_sections( options &opts )
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  opts       :  A reference to the options object.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  Nothing.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This function is called when parse_args() determines that that user
 | 
						|
//  didn't put anything on the command line.  All it does is blow through
 | 
						|
//  the INI file, printing the name of each section it encounters
 | 
						|
//  along the way.  Hidden sections are denoted by a leading ".".  They
 | 
						|
//  don't get displayed, but they are treated as a normal section
 | 
						|
//  at all times.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994     2.0A : First release
 | 
						|
//
 | 
						|
//   January 5, 1995  3.0A : Added hidden sections
 | 
						|
//
 | 
						|
 | 
						|
void dump_sections( options &opts )
 | 
						|
{
 | 
						|
    bstring current_description;
 | 
						|
    bstring section;
 | 
						|
 | 
						|
#ifdef __SC__
 | 
						|
    fstream config_file( opts.ini_file, ios::in );
 | 
						|
#else
 | 
						|
    fstream config_file( opts.ini_file, ios::in | ios::binary );
 | 
						|
#endif
 | 
						|
    if ( config_file.fail() ) {
 | 
						|
        cerr << "\nError - Could not open "
 | 
						|
             << opts.ini_file
 | 
						|
             << "!\n";
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    int first_in_section = 1;
 | 
						|
    for ( ; ; ) {
 | 
						|
        switch ( get_next_section( section, config_file ) ) {
 | 
						|
            case NONE :
 | 
						|
                cerr << endl; //For the last section
 | 
						|
                return;
 | 
						|
            case HIDDEN :
 | 
						|
                break;
 | 
						|
            case NORMAL :
 | 
						|
                read_options( opts, config_file, ""  );
 | 
						|
                bstring *d = opts.find_value( "Description" );
 | 
						|
                bstring new_description = *d;
 | 
						|
                int index = new_description.first( ',' );
 | 
						|
                if ( index >= 0 )
 | 
						|
                    new_description.remove_after( index - 1 );
 | 
						|
                if ( !( new_description == current_description ) ) {
 | 
						|
                    cerr << endl;
 | 
						|
                    cerr.width( 29 );
 | 
						|
// I don't know why, but with Symantec I have to do more than
 | 
						|
// just set left, I have to clear right!!
 | 
						|
                    cerr.setf( ios::left );
 | 
						|
                    cerr.unsetf( ios::right );
 | 
						|
                    cerr << new_description;
 | 
						|
                    cerr << ": ";
 | 
						|
                    current_description = new_description;
 | 
						|
                    first_in_section = 1;
 | 
						|
                }
 | 
						|
                if ( !first_in_section )
 | 
						|
                    cerr << ", ";
 | 
						|
                cerr << section;
 | 
						|
                first_in_section = 0;
 | 
						|
                opts.erase_values();
 | 
						|
                break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// int read_ini_file( options &opts )
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  opts      :  A reference to the options parameter that is used
 | 
						|
//               throughout the program.  The options stucture has
 | 
						|
//               a linked list of elements, each of which a tag
 | 
						|
//               name and a value.  This routine reads those elemeents
 | 
						|
//               out of BUILD.INI.
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  1 if it is okay to keep going, 0 if a crummy command line got passed.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This routine opens up BUILD???.INI, then scans until it finds a section
 | 
						|
//  with the same name as the memory model specified on the command line.
 | 
						|
//  If it doesn't find that section, it is considered an error, and a 0
 | 
						|
//  is immediately returned to main().
 | 
						|
//
 | 
						|
//  If the correction section is found, read_options() is then called to
 | 
						|
//  read all of the options into the options array.  read_options() takes
 | 
						|
//  care of expanding any templates that are encounterd in the options
 | 
						|
//  file.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A   : First release
 | 
						|
//
 | 
						|
//   January 5, 1995 3.0A : Updated to support templates
 | 
						|
//
 | 
						|
//   January 26, 1995  4.0A : Removed template support from this section,
 | 
						|
//                            since it is now handled in the read_options()
 | 
						|
//                            routine.
 | 
						|
//
 | 
						|
 | 
						|
int read_ini_file( options &opts )
 | 
						|
{
 | 
						|
    bstring section;
 | 
						|
 | 
						|
#ifdef __SC__  // Symantec doesn't define binary, don't know how they do it, don't care
 | 
						|
    fstream config_file( opts.ini_file, ios::in );
 | 
						|
#else
 | 
						|
    fstream config_file( opts.ini_file, ios::in | ios::binary );
 | 
						|
#endif
 | 
						|
    if ( config_file.fail() ) {
 | 
						|
        cerr << "\nError - Could not open "
 | 
						|
             << opts.ini_file
 | 
						|
             << "!\n";
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
    if ( !read_options( opts, config_file, opts.model ) )
 | 
						|
        return 0;
 | 
						|
    else
 | 
						|
        return 1;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// SECTION_TYPE get_next_section( bstring §ion, fstream &file )
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  section    :  The string where we return the name of the next section
 | 
						|
//                after discovering it.
 | 
						|
//
 | 
						|
//  file       :  A reference to the open fstream object.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  Returns a SECTION_TYPE enum: NONE, HIDDEN, or NORMAL, depending
 | 
						|
//  on what type of section we found.  Note that a section becomes
 | 
						|
//  a hidden section if the first character of its name is ".".  If
 | 
						|
//  that is the case, we strip the leading period here, so it looks
 | 
						|
//  to the rest of the world as if it has a normal name.  In other words,
 | 
						|
//  when I read in section [.DOS], I return HIDDEN, but switch the
 | 
						|
//  name to DOS.  (The brackets get stripped too!)
 | 
						|
//
 | 
						|
//  Note that the important thing about this routine is that it seeks
 | 
						|
//  to the appropriate position in the file, leaving the input pointer
 | 
						|
//  pointing directly to the first key value to be read in.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This utility function is used by read_ini_file() and dump_sections().
 | 
						|
//  All it does is advance through the file until it reaches the next
 | 
						|
//  section.  A section in BUILD???.INI is defined as a line that contains
 | 
						|
//  nothing except "[string]".
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A   : First release
 | 
						|
//
 | 
						|
//   January 5, 1995 3.0A : Added support for hidden sections
 | 
						|
//
 | 
						|
 | 
						|
SECTION_TYPE get_next_section( bstring §ion, fstream &file )
 | 
						|
{
 | 
						|
    char buffer[ 129 ];
 | 
						|
    for ( ; ; ) {
 | 
						|
        file.getline( buffer, 129 );
 | 
						|
        if ( file.fail() || file.eof() ) {
 | 
						|
            section = "";
 | 
						|
            file.clear( 0 );
 | 
						|
            return NONE;
 | 
						|
        }
 | 
						|
        section = buffer;
 | 
						|
//
 | 
						|
// I had to open this file in binary mode because of some problems
 | 
						|
// with Microsoft C++.  Since it is in binary mode, I get an extra '\r'
 | 
						|
// which needs to be nuked.
 | 
						|
//
 | 
						|
        int index = section.first( '\r' );
 | 
						|
        if ( index >= 0 && index == ( section.length() - 1 ) )
 | 
						|
            section.remove_after( index - 1 );
 | 
						|
        if ( section.first( '[' ) != 0 )
 | 
						|
            continue;
 | 
						|
        if ( section.last( ']' ) != ( section.length() - 1 ) )
 | 
						|
            continue;
 | 
						|
        section.remove_before( 1 );
 | 
						|
        section.remove_after( section.length() - 2 );
 | 
						|
//
 | 
						|
// Detect and mung a hidden section
 | 
						|
//
 | 
						|
        if ( section[ 0 ] == '.' ) {
 | 
						|
            section.remove_before( 1 );
 | 
						|
            return HIDDEN;
 | 
						|
        }
 | 
						|
        return NORMAL;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// int read_options( options &opts, fstream &file, const bstring& section )
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  opts      :  A reference to the options parameter that is used
 | 
						|
//               throughout the program.  The options stucture has
 | 
						|
//               a linked list of elements, each of which a tag
 | 
						|
//               name and a value.  This routine reads those elements
 | 
						|
//               out of BUILD.INI.
 | 
						|
//
 | 
						|
//  file      :  A reference to an open fstream connected to BUILD.INI.
 | 
						|
//               This routine is called by read_ini_file(), and it is
 | 
						|
//               that function's job to make sure that the input pointer
 | 
						|
//               is sitting just at the right spot.  In this case the
 | 
						|
//               spot should be right at the start of the first line
 | 
						|
//               of text immediately following the desired section.
 | 
						|
//
 | 
						|
//  section   :  A reference to the section whose options I am supposed
 | 
						|
//               to read.  If this is a blank string, it means I should
 | 
						|
//               just read in the options starting at the current location
 | 
						|
//               in the file.  This is how it would work, for example, when
 | 
						|
//               I have just called get_next_section(), and I know that I
 | 
						|
//               am at the right place.  If a valid section name is here,
 | 
						|
//               I instead seek back to the start of the file and start
 | 
						|
//               hunting for the appropriate section.  This is what happens
 | 
						|
//               when parse_options() is trying to expand a template.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  1 if it is okay to keep going, 0 if a crummy command line got passed.
 | 
						|
//  Note that when this function returns after succesfully reading in a list
 | 
						|
//  of options, it will leave the input file pointer exactly where it was
 | 
						|
//  when it got called.
 | 
						|
//
 | 
						|
//  This business of leaving things right were they were is very important,
 | 
						|
//  because this function may call itself recursively.  When it reads in
 | 
						|
//  a line of text and passes it to parse_option(), it may be a Template=
 | 
						|
//  key line.  If this is the case parse_option() will recursively call
 | 
						|
//  read_options() to read in the options for *that* section.  When it
 | 
						|
//  finally returns, parse_option() will come back here leaving me in the
 | 
						|
//  dark as to all that really went on.  Since I'm not aware that anything
 | 
						|
//  funny happened, I better not have to deal with an input file pointer
 | 
						|
//  that has moved around.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This function is called by read_ini_file() during the startup of
 | 
						|
//  BUILD.  Its job is read in all of the options defined for the current
 | 
						|
//  section.  It doesn't attempt to find the correct section, that
 | 
						|
//  has to be done by read_ini_file() before this guy gets called.
 | 
						|
//
 | 
						|
//  This function is actually a little lazy also.  It reads in lines
 | 
						|
//  from the INI file, checks to make sure they aren't comments or the
 | 
						|
//  start of a new section, then calls parse_option() to do the
 | 
						|
//  real work.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A   : First release
 | 
						|
//
 | 
						|
//   January 5, 1995 3.0A : Unchanged in this release.  Normally this would
 | 
						|
//                          pass without comment, but so much other stuff
 | 
						|
//                          changed that I thought I should mention it.
 | 
						|
//
 | 
						|
//   January 26, 1995  4.0A : Modified to save and restore the file pointer
 | 
						|
//                            settings when called.  This lets me be
 | 
						|
//                            called recursively when expanding templates.
 | 
						|
 | 
						|
int read_options( options &opts,
 | 
						|
                  fstream &file,
 | 
						|
                  const bstring &target_section )
 | 
						|
{
 | 
						|
    bstring line;
 | 
						|
    bstring section;
 | 
						|
    char buffer[ 257 ];
 | 
						|
 | 
						|
//
 | 
						|
// Save the current location in the file, so I can go back to it on exit.
 | 
						|
//
 | 
						|
    long saved_spot;
 | 
						|
    saved_spot = file.tellg();
 | 
						|
//
 | 
						|
// If I am looking for a specific section, I go back to the start of
 | 
						|
// the file.  If I wasn't asked for a specific section, I just assume
 | 
						|
// I can start reading options right where I am at this moment, in which
 | 
						|
// case I don't have to do any hunting.
 | 
						|
//
 | 
						|
    if ( target_section != "" ) {
 | 
						|
        file.rdbuf()->seekoff( 0, ios::beg, ios::in );
 | 
						|
        for ( ; ; ) {
 | 
						|
//
 | 
						|
// I repeatedly call get_next_section() until it returns a match to the
 | 
						|
// section I am looking for.  If I get to the end of the file and
 | 
						|
// haven't found it, flag this as an error.
 | 
						|
//
 | 
						|
            if ( get_next_section( section, file ) == NONE ) {
 | 
						|
                cerr << "\nError - Couldn't find the section for model "
 | 
						|
                     << target_section
 | 
						|
                     << " in "
 | 
						|
                     << opts.ini_file
 | 
						|
                     << "!\n";
 | 
						|
                return 0;
 | 
						|
            } else if ( section == target_section )
 | 
						|
                break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
//
 | 
						|
// Now that I am at the correct section, I can just sit in a loop,
 | 
						|
// reading in lines, and processing them.
 | 
						|
//
 | 
						|
    for ( ; ; ) {
 | 
						|
        file.getline( buffer, 257 );
 | 
						|
        if ( file.fail() || file.eof() ) {
 | 
						|
            file.clear();
 | 
						|
            file.rdbuf()->seekoff( saved_spot, ios::beg, ios::in );
 | 
						|
            return 1;
 | 
						|
        }
 | 
						|
        line = buffer;
 | 
						|
//
 | 
						|
// I had to open this file in binary mode because of some problems
 | 
						|
// with Microsoft C++.  Since it is in binary mode, I get an extra '\r'
 | 
						|
// at the end of the line which needs to be nuked.
 | 
						|
//
 | 
						|
        int index = line.first( '\r' );
 | 
						|
        if ( index >= 0 && index == ( line.length() - 1 ) )
 | 
						|
            line.remove_after( index - 1 );
 | 
						|
//
 | 
						|
// If this is the start of a new section, I have read in all the
 | 
						|
// options I am going to get, so I can quit.
 | 
						|
//
 | 
						|
        if ( line[ 0 ] == '[' ) {
 | 
						|
            file.rdbuf()->seekoff( saved_spot, ios::beg, ios::in );
 | 
						|
            return 1;
 | 
						|
        }
 | 
						|
// A comment or a blank line?
 | 
						|
        if ( line[ 0 ] == ';' || line[ 0 ] == '\0' )
 | 
						|
            continue;
 | 
						|
// This line looks good, I call somebody else to do the parsing.
 | 
						|
        if ( parse_option( opts, line, file ) == 0 )
 | 
						|
            return 0;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// int parse_option( options &opts, bstring &line )
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  opts      :  A reference to the options parameter that is used
 | 
						|
//               throughout the program.  The options stucture has
 | 
						|
//               a linked list of elements, each of which a tag
 | 
						|
//               name and a value.  This routine sets up one of those
 | 
						|
//               elements based on what it finds in the command line
 | 
						|
//               passed to it by read_options().
 | 
						|
//
 | 
						|
//  line      :  An actual line of input that has been read in from the
 | 
						|
//               INI file.  It better be of the form "tag=value", or this
 | 
						|
//               routine is going to complain and return an error.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  0 if the input line looked bad, 1 if it looked okay.  Note that if
 | 
						|
//  this guy doesn't like an input line, it will return a value of
 | 
						|
//  0, and the whole process will shut down.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This routine is a specialist at one thing only.  It takes a line that
 | 
						|
//  somebody else took the trouble of reading in from the INI file, and
 | 
						|
//  divides it into a tag and value.  It then inserts that tag and value
 | 
						|
//  into the opts structure, so that anyone who wants to can take a gander
 | 
						|
//  at it later in the program.
 | 
						|
//
 | 
						|
//  This function makes use of many member functions from the bstring
 | 
						|
//  and options classes to do its work.  If they look a little strange,
 | 
						|
//  you can view the class descriptions in BUILD.H.
 | 
						|
//
 | 
						|
//  In release 2.0B, I added support for multiple lines in a single command.
 | 
						|
//  This was done to give me more control over the Link/Implib process
 | 
						|
//  in CommLib.  You put multiple commands together on one line, separated
 | 
						|
//  by a single caret ('^').  In this release, some of my Demo lines
 | 
						|
//  were getting really long, and unreadable.  So in 3.0, I added
 | 
						|
//  a new way to enter multiple lines.  You can now include multiple
 | 
						|
//  lines by repeating definitions of the the same value in a section.
 | 
						|
//  As each new value is encountered,
 | 
						|
//  it gets appended to the existing value, with a caret to separate them.
 | 
						|
//  Now multiple lines appear in the order to are going to execute them.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A    : First release
 | 
						|
//
 | 
						|
//   January 5, 1995  3.0A : New release supports multiple lines per command.
 | 
						|
//
 | 
						|
//   January 26, 1995  4.0A : Added the ability to recursively call
 | 
						|
//                            read_options() when I encounter a template
 | 
						|
//                            definition.  Note that it doesn't do the
 | 
						|
//                            recursive call if opts.model == "", which
 | 
						|
//                            means we are just displaying the options
 | 
						|
//                            on the screen, since the user didn't specify
 | 
						|
//                            a memory model.
 | 
						|
//
 | 
						|
 | 
						|
int parse_option( options &opts, bstring &line, fstream& file )
 | 
						|
{
 | 
						|
//
 | 
						|
// This code divides the line into two parts, a key and a value.
 | 
						|
// The '=' is the dividing point.  If the line doesn't divide
 | 
						|
// up properly, I return an error message.
 | 
						|
//
 | 
						|
    int divide = line.first( '=' );
 | 
						|
    if ( divide <= 0 ) {
 | 
						|
        cerr << "\nError - Badly formed option line in "
 | 
						|
             << opts.ini_file
 | 
						|
             << ": "
 | 
						|
             << line
 | 
						|
             << "\n";
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
    bstring key = line;
 | 
						|
    key.remove_after( divide - 1 );
 | 
						|
    bstring value = line;
 | 
						|
    value.remove_before( divide + 1 );
 | 
						|
//
 | 
						|
// This is a new piece of code.  If it turns out that the key is "Template",
 | 
						|
// it means I now need to read in all the values for that template.  The
 | 
						|
// name of the template is stored in the value, so I just pass it to
 | 
						|
// read_options().  This is a recursive call, so everyone needs to be
 | 
						|
// careful!!!
 | 
						|
//
 | 
						|
    if ( key == "Template" && opts.model != "" )
 | 
						|
        return read_options( opts, file, value );
 | 
						|
//
 | 
						|
// Now that I have a new value, I need to store it in the options argument.
 | 
						|
// I have to check to see if there is a stored_value already for my key.
 | 
						|
// I'm not allowed to add new keys to the options unless they are CMD file
 | 
						|
// definitions.  So when I check on the stored value, I better get something
 | 
						|
// back, even if it is an empty string.  If I get a null pointer, it means
 | 
						|
// that the key isn't in the opts container.  If this is the case, the
 | 
						|
// key better be "*LIB.CMD or "*EXE.CMD", or I flag an error.
 | 
						|
//
 | 
						|
    bstring *stored_value = opts.find_value( key );
 | 
						|
    if ( stored_value == 0 ) {
 | 
						|
        if ( key.first( "LIB.CMD" ) < 0 && key.first( "EXE.CMD" ) < 0 ) {
 | 
						|
            cerr << "\nError - Unknown option found in "
 | 
						|
                 << opts.ini_file
 | 
						|
                 << ": "
 | 
						|
                 << line
 | 
						|
                 << "\n";
 | 
						|
            return 0;
 | 
						|
        } else {
 | 
						|
            opts.add_option( key, "" );
 | 
						|
            stored_value = opts.find_value( key );
 | 
						|
        }
 | 
						|
    }
 | 
						|
//
 | 
						|
// If I already have this description on file, I am going to append it
 | 
						|
// to the existing line.  I use the "^" symbol to separate lines
 | 
						|
//
 | 
						|
    if ( *stored_value != "" )
 | 
						|
        *stored_value = *stored_value + "^" + value;
 | 
						|
    else
 | 
						|
        *stored_value = value;
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// int build_lib_file_list( int argc,
 | 
						|
//                      char **argv,
 | 
						|
//                      file_list &files,
 | 
						|
//                      options& opts )
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  argc      :  A count of the arguments left after the command
 | 
						|
//               line has been partially parsed.
 | 
						|
//
 | 
						|
//  argv      :  A pointer to the array of strings that are the
 | 
						|
//               arguments passed on the command line.
 | 
						|
//
 | 
						|
//  files     :  This is a reference to the structure that contains
 | 
						|
//               the list of files that are going to be compiled.
 | 
						|
//
 | 
						|
//  opts      :  A reference to the options structure for this build.
 | 
						|
//               By the time this function gets called, the entire
 | 
						|
//               option list will have been filed out.  I need to know
 | 
						|
//               about it here because it contains the directory list.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  0 if no files were added to the file list.  1 if some were.  main()
 | 
						|
//  considers a value of 0 to be a good reason to abort.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This routine is called by main() after it has finished parsing the
 | 
						|
//  INI file.  Its job is to go through the list of files on the command
 | 
						|
//  line and add them all to the file list object.  It does this by
 | 
						|
//  taking each of the file name arguments (which may or may not contain
 | 
						|
//  wildcards), and checking each of the defined directories for the
 | 
						|
//  presence of files that match the spec.  The actual expansion of the
 | 
						|
//  wildcard and its addition to the file list is accomplished in
 | 
						|
//  add_wild_cards().
 | 
						|
//
 | 
						|
//  Note that if the user doesn't specify *any* file names on the command
 | 
						|
//  line, we go ahead and parse "*.CPP", "*.C", and "*.ASM".  This is so
 | 
						|
//  the build program can be run with even less thinking than before.
 | 
						|
//
 | 
						|
//  REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
int build_lib_file_list( int argc, char **argv, file_list &files, options& opts )
 | 
						|
{
 | 
						|
    char *default_files[ 10 ];
 | 
						|
    bstring default_directories( "ALL" );
 | 
						|
    bstring *dirs = opts.find_value( "Directories" );
 | 
						|
    if ( dirs == 0 )
 | 
						|
        dirs = &default_directories;
 | 
						|
    if ( argc == 0 ) {
 | 
						|
        bstring *s;
 | 
						|
        s = opts.find_value( "Cpp" );
 | 
						|
        if ( s != 0 && strlen( *s ) > 0 )
 | 
						|
            default_files[ argc++ ] = "*.CPP";
 | 
						|
        s = opts.find_value( "C" );
 | 
						|
        if ( s != 0 && strlen( *s ) > 0 )
 | 
						|
            default_files[ argc++ ] = "*.C";
 | 
						|
        s = opts.find_value( "Asm" );
 | 
						|
        if ( s != 0 && strlen( *s ) > 0 )
 | 
						|
            default_files[ argc++ ] = "*.ASM";
 | 
						|
        argv = default_files;
 | 
						|
    }
 | 
						|
//
 | 
						|
// This loop is where I expand wildcards for each of the specified files,
 | 
						|
// in each of the specified directories.
 | 
						|
//
 | 
						|
    for ( int i = 0 ; argc ; i++ ) {
 | 
						|
        bstring ext = bstring( *argv ).ext();
 | 
						|
        ext.remove_before( 1 );
 | 
						|
        bstring search_dirs = *dirs;
 | 
						|
        bstring dir;
 | 
						|
        for ( dir = search_dirs.get_first_token("^ ");
 | 
						|
              dir != "";
 | 
						|
              dir = search_dirs.get_next_token("^ ") ) {
 | 
						|
//
 | 
						|
// Microsoft 8.0 chokes on this constructor, had to replace it with the
 | 
						|
// alternate one below it.
 | 
						|
//
 | 
						|
//          bstring wild_guy( bstring( ROOT_DIR ) + ext + "_" + dir + "\\" + *argv );
 | 
						|
            bstring wild_guy = bstring( ROOT_DIR ) + ext + "_" + dir + "\\" + *argv;
 | 
						|
            add_wild_cards( files, wild_guy );
 | 
						|
        }
 | 
						|
        argv++;
 | 
						|
        argc--;
 | 
						|
    }
 | 
						|
    if ( files.get_first_file() == 0 ) {
 | 
						|
        cerr << "\nError - No files found to build!\n";
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// int build_exe_file_list( int argc,
 | 
						|
//                           char **argv,
 | 
						|
//                           file_list &files )
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  argc      :  A count of the arguments left after the command
 | 
						|
//               line has been partially parsed.
 | 
						|
//
 | 
						|
//  argv      :  A pointer to the array of strings that are the
 | 
						|
//               arguments passed on the command line.
 | 
						|
//
 | 
						|
//  files     :  This is a reference to the structure that contains
 | 
						|
//               the list of files that are going to be compiled.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  0 if no files were added to the file list.  1 if some were.  main()
 | 
						|
//  considers a value of 0 to be a good reason to abort.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  In release 3.0, we added the ability to build demo programs.  When
 | 
						|
//  building libraries, we have a function just like this that is used
 | 
						|
//  to build a list of files.  Unfortunately, we have to have a specialized
 | 
						|
//  version of this function for building demos, because we specify files
 | 
						|
//  in a slightly different way.  When building libraries, we always
 | 
						|
//  specify files without a path name, like "STORCMP.CPP", and let BUILD
 | 
						|
//  figure out which directories to search.  With demos it is different,
 | 
						|
//  because we give a full path name.  So I have to perform a simpler
 | 
						|
//  version of the wild card expansion in this routine.
 | 
						|
//
 | 
						|
//  REVISION HISTORY
 | 
						|
//
 | 
						|
//   January 5, 1995  3.0A : First release
 | 
						|
//
 | 
						|
 | 
						|
int build_exe_file_list( int argc, char **argv, file_list &files )
 | 
						|
{
 | 
						|
    if ( argc == 0 ) {
 | 
						|
        cerr << "\nError - No files found to build!\n";
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
//
 | 
						|
// This loop is where I expand wildcards for each of the specified files.
 | 
						|
//
 | 
						|
    for ( int i = 0 ; argc ; i++ ) {
 | 
						|
        bstring wild_guy = *argv;
 | 
						|
        add_wild_cards( files, wild_guy );
 | 
						|
        argv++;
 | 
						|
        argc--;
 | 
						|
    }
 | 
						|
//
 | 
						|
// After it is all done, I check to see if I really got any files.
 | 
						|
//
 | 
						|
    if ( files.get_first_file() == 0 ) {
 | 
						|
        cerr << "\nError - No files found to build!\n";
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// void add_wild_cards( file_list &files, const bstring &wild_spec )
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  files     :  A reference to a file list structure.  The file list
 | 
						|
//               is simply a linked list of file names.  My job here
 | 
						|
//               is to expand the wild card I get passed and add the
 | 
						|
//               names to this guy.
 | 
						|
//
 | 
						|
//  wild_spec :  This is the wild card specification, although it is not
 | 
						|
//               necessarily a wild card, it might just be a name.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  None.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  The user passes a list of file names on the command line, after
 | 
						|
//  the model.  The names can be things like *.CPP and BOB.C.  The
 | 
						|
//  routine that builds this file list takes each of those names,
 | 
						|
//  and prepends each of the directories to it.  Then it calls this
 | 
						|
//  guy to expand the resulting wild card specification and add it to
 | 
						|
//  the file list structure.  Adding to the structure is done via
 | 
						|
//  a nice simple member function.
 | 
						|
//
 | 
						|
//  So if the guy types BUILD -lib S *.CPP, this function might get called
 | 
						|
//  three times, with the names "..\CPP_ALL\*.CPP", "..\CPP_DOS\*.CPP"
 | 
						|
//  and "..\CPP_FOO\*.CPP".  I could add anywhere from zero to a zillion
 | 
						|
//  file names to the list as a result;
 | 
						|
//
 | 
						|
//  Note that this function is called by both the LIB thread of execution
 | 
						|
//  and the EXE path.  In the case of the EXE path, no path names are
 | 
						|
//  prepended to the file names, so we get a straightforward expansion.
 | 
						|
//
 | 
						|
//
 | 
						|
//  REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 18, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
#ifdef __OS2__
 | 
						|
#define INCL_DOSFILEMGR
 | 
						|
#include <os2.h>
 | 
						|
 | 
						|
void add_wild_cards( file_list &files, const bstring &wild_spec )
 | 
						|
{
 | 
						|
    APIRET rc;
 | 
						|
    HDIR handle;
 | 
						|
    FILEFINDBUF3 buffer = { 0 };
 | 
						|
    ULONG count;
 | 
						|
 | 
						|
    handle = 1;
 | 
						|
    count = 1;
 | 
						|
    rc = DosFindFirst( wild_spec,
 | 
						|
                       &handle,
 | 
						|
                       0,
 | 
						|
                       (PVOID) &buffer,
 | 
						|
                       sizeof( buffer ),
 | 
						|
                       &count,
 | 
						|
                       FIL_STANDARD );
 | 
						|
    bstring drive_and_path = wild_spec.drive() + wild_spec.path();
 | 
						|
    while ( rc == 0 ) {
 | 
						|
        bstring new_name = drive_and_path + buffer.achName;
 | 
						|
        files.add_file( new_name );   //Handy member function of class file_list
 | 
						|
        count = 1;
 | 
						|
        rc = DosFindNext( handle,
 | 
						|
                          (PVOID) &buffer,
 | 
						|
                          sizeof( buffer ),
 | 
						|
                          &count );
 | 
						|
    }
 | 
						|
}
 | 
						|
#else
 | 
						|
void add_wild_cards( file_list &files, const bstring &wild_spec )
 | 
						|
{
 | 
						|
    struct find_t ffblk;
 | 
						|
    unsigned int result;
 | 
						|
//
 | 
						|
// Because of the way DOS expands names I need to break out the
 | 
						|
// drive and path separately.  So, if I am expanding C:\BOB\*.CPP,
 | 
						|
// I want to keep a copy here of C:\BOB\.  Then, as I expand the
 | 
						|
// *.CPP part, I pack the resuling 8.3 name to the drive and path
 | 
						|
// and voila, I have a complete filespec.
 | 
						|
//
 | 
						|
    bstring drive_and_path = wild_spec.drive() + wild_spec.path();
 | 
						|
 | 
						|
// The funny cast of wild_spec is to accomodate Symantec C.  They
 | 
						|
// foolishly use char * instead of const char * for this arg.
 | 
						|
 | 
						|
    result = _dos_findfirst( (char *) (const char *) wild_spec, 0, &ffblk );
 | 
						|
    while ( result == 0 ) {
 | 
						|
        bstring new_name = drive_and_path + ffblk.name;
 | 
						|
        files.add_file( new_name );   //Handy member function of class file_list
 | 
						|
        result = _dos_findnext( &ffblk );
 | 
						|
    }
 | 
						|
}
 | 
						|
#endif
 | 
						|
//
 | 
						|
// void process( bstring &result,
 | 
						|
//               const char *command_string,
 | 
						|
//               const bstring &file_name,
 | 
						|
//               options &opts )
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  result         :  A reference to an output string.  Since the dorky
 | 
						|
//                    little string class I built for this program doesn't
 | 
						|
//                    have good functions for adding characters one at at
 | 
						|
//                    time, I build this result in a temporary buffer, then
 | 
						|
//                    copy it over before returning.  Warning!  the output
 | 
						|
//                    buffer is big, so I made it static!
 | 
						|
//
 | 
						|
//  command_string :  A pointer to the command string.  This is the string
 | 
						|
//                    that was actually read out of the INI file.  If there
 | 
						|
//                    were multiple commands in one line, this guy is just
 | 
						|
//                    one of them that has been separated out.  This command
 | 
						|
//                    line should be chock full of special formatting
 | 
						|
//                    arguments such as %file%, %model%, etc.
 | 
						|
//
 | 
						|
//  file_name      :  The name of the source file being affected by this
 | 
						|
//                    command.  This argument is not always meaningful.
 | 
						|
//
 | 
						|
//  opts            :  This structure contains all of the options that have
 | 
						|
//                     been read out of the INI file.  I need to look at these
 | 
						|
//                     because I need to get the option value for this particular
 | 
						|
//                     CMD file out of this structure.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  Nothing.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//   This routine takes a command line and processes it.  Processing means
 | 
						|
//   that it takes all the special formatting arguments and then
 | 
						|
//   substitutes the appropriate values in the output string.  The command
 | 
						|
//   is built up and stored in an output string.  The following formatting
 | 
						|
//   codes are supported: (see top of file for more detail)
 | 
						|
//
 | 
						|
//  %file%          :  The complete name of the source file.
 | 
						|
//
 | 
						|
//  %file.path%     :  The drive and path of the file, with a guaranteed
 | 
						|
//                     '\' character.
 | 
						|
//
 | 
						|
//  %file.name%     :  The name component of the source file, minus an
 | 
						|
//                     extension.
 | 
						|
//
 | 
						|
//  %model%         :  The model/library name, as found inside the "[]".
 | 
						|
//
 | 
						|
//  %model.lower%   :  The first letter of the model name, in lower case.
 | 
						|
//
 | 
						|
//  %model.upper%   :  The first letter of the model name, in upper case.
 | 
						|
//
 | 
						|
//  %model.asm%     :  A greenleaf model name for ASM files.
 | 
						|
//
 | 
						|
//  %nl%            :  Inserts a new line into the output stream,
 | 
						|
//                     literally a '\n';
 | 
						|
//
 | 
						|
//  %%              :  Inserts the literal '%' character.
 | 
						|
//
 | 
						|
//  %comspec%       :  Inserts the name of the current command processor.
 | 
						|
//
 | 
						|
//  %continue%      :  This means the character immediately following
 | 
						|
//                     the %continue% is a continuation character.  If
 | 
						|
//                     there are more files coming after this one, we
 | 
						|
//                     output the continuation character.  Otherwise,
 | 
						|
//                     it gets eaten.
 | 
						|
//
 | 
						|
//  REVISION HISTORY
 | 
						|
//
 | 
						|
//   January 5, 1995
 | 
						|
//
 | 
						|
 | 
						|
void process( bstring &result,
 | 
						|
              const char *command_string,
 | 
						|
              const bstring &file_name,
 | 
						|
              options &opts,
 | 
						|
              int more_to_come )
 | 
						|
{
 | 
						|
    const char *p;
 | 
						|
    static char _temp[ 1024 ];
 | 
						|
    char *buffer = _temp;
 | 
						|
    bstring path = file_name.drive() + file_name.path();
 | 
						|
    bstring name = file_name.name();
 | 
						|
 | 
						|
    while ( *command_string ) {
 | 
						|
        if ( *command_string == '%' ) {
 | 
						|
            command_string++;
 | 
						|
            bstring tag = command_string;
 | 
						|
            p = strchr( command_string, '%' );
 | 
						|
            if ( !p ) {
 | 
						|
                cerr << "Missing closing '%' in command line: "
 | 
						|
                     << command_string
 | 
						|
                     << endl;
 | 
						|
                exit( -1 );
 | 
						|
            }
 | 
						|
            tag.remove_after( p - command_string - 1 );
 | 
						|
            command_string = p + 1;
 | 
						|
            if ( tag == "file" || tag == "*.c" || tag == "*.cpp" || tag == "*.asm" )
 | 
						|
                for ( p = file_name ; *p != 0 ; )
 | 
						|
                    *buffer++ = *p++;
 | 
						|
            else if ( tag == "file.path" )
 | 
						|
                for ( p = path ; *p != 0 ; )
 | 
						|
                    *buffer++ = *p++;
 | 
						|
            else if ( tag == "file.name" )
 | 
						|
                for ( p = name ; *p != 0 ; )
 | 
						|
                    *buffer++ = *p++;
 | 
						|
            else if ( tag == "model" )
 | 
						|
                for ( p = opts.model ; *p != 0 ; )
 | 
						|
                    *buffer++ = *p++;
 | 
						|
            else if ( tag == "model.upper" )
 | 
						|
                *buffer++ = toupper( opts.model[ 0 ] );
 | 
						|
            else if ( tag == "model.lower" )
 | 
						|
                *buffer++ = tolower( opts.model[ 0 ] );
 | 
						|
            else if ( tag == "nl" )
 | 
						|
                *buffer++ = '\n';
 | 
						|
            else if ( tag == "" )
 | 
						|
                *buffer++ = '%';
 | 
						|
            else if ( tag == "comspec" ) {
 | 
						|
                p = getenv( "COMSPEC" );
 | 
						|
                if ( !p )
 | 
						|
                    p = "COMMAND.COM";
 | 
						|
                for ( ; *p != 0 ; )
 | 
						|
                    *buffer++ = *p++;
 | 
						|
            } else if ( tag == "model.asm" ) {
 | 
						|
                switch( tolower( opts.model[ 0 ] ) ) {
 | 
						|
                    case 's' : p = "SMALL"; break;
 | 
						|
                    case 'c' : p = "COMPACT"; break;
 | 
						|
                    case 'm' : p = "MEDIUM"; break;
 | 
						|
                    case 'l' : p = "LARGE"; break;
 | 
						|
                    case 'h' : p = "HUGE"; break;
 | 
						|
                    default  : p = "UNKNOWN"; break;
 | 
						|
                }
 | 
						|
                for ( ; *p != 0 ; )
 | 
						|
                    *buffer++ = *p++;
 | 
						|
            } else if ( tag == "continue" ) {
 | 
						|
                if ( !more_to_come )
 | 
						|
                    command_string++;
 | 
						|
            } else if ( tag == "Opts" ) {
 | 
						|
                bstring *def_value = opts.find_value( "Opts" );
 | 
						|
                for ( p = *def_value ; *p != 0 ; )
 | 
						|
                    *buffer++ = *p++;
 | 
						|
            } else {
 | 
						|
                cerr << "Unrecognized tag in INI file: "
 | 
						|
                     << tag
 | 
						|
                     << endl;
 | 
						|
                exit( 1 );
 | 
						|
            }
 | 
						|
        } else
 | 
						|
            *buffer++ = *command_string++;
 | 
						|
 | 
						|
    }
 | 
						|
    *buffer = '\0';
 | 
						|
    result = _temp;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// void build_lib_config_files( options &opts, file_list &files )
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  opts      :  This structure contains all of the options that have
 | 
						|
//               been read out of the INI file.  I need to look at these
 | 
						|
//               options so I can figure out which CMD files need to be
 | 
						|
//               built.
 | 
						|
//
 | 
						|
//  files     :  A list of all the files that are going to get compiled.
 | 
						|
//               I need this list so that I can build LIB.CMD, as well as
 | 
						|
//               any compiler response files that get wild cards expanded.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  None.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  Sometimes compiler or link commands get too big for the command line.
 | 
						|
//  When this happens, we have to put options into a response file, and
 | 
						|
//  execute something like this:  CC blah blah @CCLIB.CMD file.c.
 | 
						|
//
 | 
						|
//  The stuff that goes into the CMD file is stored in BUILD.INI.  It
 | 
						|
//  is always stored in a tag with a name like xxxLIB.CMD.  This routine
 | 
						|
//  finds every option tag whose name ends with "LIB.CMD".  It then dumps
 | 
						|
//  the value of that tag into a file called "xxxLIB.CMD.
 | 
						|
//
 | 
						|
//  There is one real tricky bit in here.  If one of the lines in the CMD
 | 
						|
//  file contains a %filexxxx% keyword, I repeatedly output that line for
 | 
						|
//  each and every file in the file list.  This is how the LIB.CMD file
 | 
						|
//  gets built, for example.  If the CMD file instead contains "*.ASM",
 | 
						|
//  "*.C", or "*.CPP", we expand just those guys.
 | 
						|
//
 | 
						|
//  REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 18, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
//   January 31, 1195 4.0B : Modified to expand wild cards like "*.C".
 | 
						|
//                           Since these files are only built once at
 | 
						|
//                           the start of a compile cycle, this wasn't a
 | 
						|
//                           a useful feature up until the time I addes
 | 
						|
//                           support for batch compilation.
 | 
						|
//
 | 
						|
 | 
						|
 | 
						|
void build_lib_config_files( options &opts, file_list &files )
 | 
						|
{
 | 
						|
    for ( item *option = opts.get_first_item();
 | 
						|
          option != 0;
 | 
						|
          option = option->get_next_item() ) {
 | 
						|
        if ( option->name.first( "LIB.CMD" ) >= 0 ) {
 | 
						|
            fstream config_file( option->name, ios::out );
 | 
						|
            bstring command_string;
 | 
						|
            command_string = option->value.get_first_token( "^" );
 | 
						|
            while ( command_string != "" ) {
 | 
						|
                bstring parsed_string;
 | 
						|
                if ( command_string.first( "%*." ) >= 0 )
 | 
						|
                    build_wild_card_line( opts, files, config_file, command_string );
 | 
						|
                else if ( command_string.first(  "%file" ) >= 0 ) {
 | 
						|
                    for ( bstring *file = files.get_first_file() ;
 | 
						|
                          file != 0; ) {
 | 
						|
                        bstring *temp = file;
 | 
						|
                        file = files.get_next_file();
 | 
						|
                        process( parsed_string, command_string, *temp, opts, file != 0 );
 | 
						|
                        config_file << parsed_string << endl;
 | 
						|
                    }
 | 
						|
                } else {
 | 
						|
                    process( parsed_string, command_string, "XXXXXXXX.XXX", opts, 0 );
 | 
						|
                    config_file << parsed_string << endl;
 | 
						|
                }
 | 
						|
                command_string = option->value.get_next_token( "^" );
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// void build_wild_card_line( options &opts,
 | 
						|
//                            file_list &files,
 | 
						|
//                            fstream &config_file,
 | 
						|
//                            bstring &command_string )
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  opts      :  This structure contains all of the options that have
 | 
						|
//               been read out of the INI file.  I need to look at these
 | 
						|
//               options so I can figure out how to build this cmd file.
 | 
						|
//
 | 
						|
//  files     :  A list of all the files that are going to get compiled.
 | 
						|
//               I need to search through this list to find the files that
 | 
						|
//               match the wild card specification, and output a parsed line
 | 
						|
//               to the config file for each one.
 | 
						|
//
 | 
						|
//  config_file    : The open config file.  I will be writing out to this file.
 | 
						|
//
 | 
						|
//  command_string : The string that contains the wild card.  I have to process
 | 
						|
//                   this string and write it out once per matching file.
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  None.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  Sometimes I want to perform an assembly or compilation in batch mode.
 | 
						|
//  When this is the case, instead of calling BCC once for every file,
 | 
						|
//  I just call it once with a line like this:  BCC blah blah @CLIB.CMD.
 | 
						|
//  This means that CLIB.CMD has to have a list of files in it.  To put
 | 
						|
//  a list of files in a CMD file, you could just put "%file.name%" or
 | 
						|
//  one of the other "%file%" tages.  But that would put *all* the files
 | 
						|
//  in their, and I just want the "*.c", or "*.asm", or whatever.
 | 
						|
//
 | 
						|
//  So instead, I put exactly that wildcard specification in the definition
 | 
						|
//  for my CMD file: "*.CPP", or "*.C", or "*.ASM".  When a line like this
 | 
						|
//  is encountered, build_lib_config_files() calls this routine to expand
 | 
						|
//  the line and write it out to the output file.
 | 
						|
//
 | 
						|
//  This routine then has to go through the list, and select every file that
 | 
						|
//  matches the extension of the wild card.  Each of those files gets
 | 
						|
//  processed into the output line, and added to the config file.
 | 
						|
//
 | 
						|
//  If even one file gets added to the config file, the compiler for that
 | 
						|
//  type of file gets set to run in BATCH mode.
 | 
						|
//
 | 
						|
//  Note that this guy also checks to see if any objects exist for each
 | 
						|
//  particular file, preventing us from performing compilations that
 | 
						|
//  don't need to be done.
 | 
						|
//
 | 
						|
//  REVISION HISTORY
 | 
						|
//
 | 
						|
//   January 31, 1995  4.0B : First release.
 | 
						|
//
 | 
						|
 | 
						|
void build_wild_card_line( options &opts,
 | 
						|
                           file_list &files,
 | 
						|
                           fstream &config_file,
 | 
						|
                           bstring &command_string )
 | 
						|
{
 | 
						|
//
 | 
						|
// First I need to isolate the wild card extension.  I remove the leading
 | 
						|
// "*.", then the trailing index.  When that is all done, I should have
 | 
						|
// a file extension, as in "C", "CPP", or "ASM".  I put that into bstring
 | 
						|
// extension for clarity, and use it throughout this routine.
 | 
						|
//
 | 
						|
    bstring wild_card = command_string;
 | 
						|
    int index = command_string.first( "%*." );
 | 
						|
    wild_card.remove_before( index + 2 ); // delete % and *
 | 
						|
    index = wild_card.first( "%" );
 | 
						|
    if ( index < 0 ) {
 | 
						|
        cerr << "Failed to find matching % in config file definition!" << endl;
 | 
						|
        exit( 1 );
 | 
						|
    } else
 | 
						|
        wild_card.remove_after( index - 1 );
 | 
						|
    bstring extension = wild_card;
 | 
						|
    extension.remove_before( 1 );
 | 
						|
//
 | 
						|
// I need to find the command for this extension.  If I match up any files
 | 
						|
// with the wildcard, I am going to switch the command to BATCH mode.  If I
 | 
						|
// can't find a command to match this wildcard, something is wrong!
 | 
						|
//
 | 
						|
    item *option = opts.find_item( extension );
 | 
						|
    if ( option == 0 ) {
 | 
						|
        cerr << "No commands defined for " << wild_card << endl;
 | 
						|
        exit( 1 );
 | 
						|
    }
 | 
						|
//
 | 
						|
// Now I just sit in a loop and get files that have the same extension as
 | 
						|
// the wildcard.  Each one I find is going to get stuffed out into the CMD
 | 
						|
// file via the process() routine, unless an object already exists.  If
 | 
						|
// even one file gets added to the config file, I will set the compiler to
 | 
						|
// run in batch mode.
 | 
						|
//
 | 
						|
    for ( bstring *file = files.get_first_file(); file != 0; ) {
 | 
						|
        bstring parsed_string;
 | 
						|
        bstring *temp = file;
 | 
						|
        file = files.get_next_file();
 | 
						|
        if ( temp->ext() == wild_card ) {
 | 
						|
            if ( !opts.ignore && target_file_exists( *temp, ".OBJ" ) ) {
 | 
						|
                cerr << "Skipping " << *temp << endl;
 | 
						|
            } else {
 | 
						|
                process( parsed_string, command_string, *temp, opts, file != 0 );
 | 
						|
                config_file << parsed_string << endl;
 | 
						|
                option->run_mode = item::BATCH;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
//
 | 
						|
// void build_exe_config_files( options &opts, bstring &file )
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  opts      :  This structure contains all of the options that have
 | 
						|
//               been read out of the INI file.  I need to look at these
 | 
						|
//               options so I can figure out which CMD files need to be
 | 
						|
//               built.
 | 
						|
//
 | 
						|
//  file      :  The name of the file that is going to be turned into an
 | 
						|
//               exe.  Note that this is different from building config
 | 
						|
//               files for libraries.  This is because I build a new config
 | 
						|
//               file for each and every EXE I build.  With libraries, I
 | 
						|
//               only build a single set of config files one time.     .
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  None.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  Sometimes compiler or link commands get too big for the command line.
 | 
						|
//  When this happens, we have to put options into a response file, and
 | 
						|
//  execute something like this:  CC blah blah @CCEXE.CMD file.c.
 | 
						|
//
 | 
						|
//  The stuff that goes into the CMD file is stored in BUILD.INI.  It
 | 
						|
//  is always stored in a tag with a name like "xxxxEXE.CMD".  This routine
 | 
						|
//  finds every option tag whose name ends with "EXE.CMD".  It then dumps
 | 
						|
//  the value of that tag into a file called xxxEXE.CMD.
 | 
						|
//
 | 
						|
//  REVISION HISTORY
 | 
						|
//
 | 
						|
//   January 26, 1995 4.0A : Brand new!
 | 
						|
 | 
						|
void build_exe_config_files( options &opts, bstring &file )
 | 
						|
{
 | 
						|
    for ( item *option = opts.get_first_item();
 | 
						|
          option != 0;
 | 
						|
          option = option->get_next_item() ) {
 | 
						|
        if ( option->name.first( "EXE.CMD" ) >= 0 ) {
 | 
						|
            fstream config_file( option->name, ios::out );
 | 
						|
            bstring command_string;
 | 
						|
            command_string = option->value.get_first_token( "^" );
 | 
						|
            while ( command_string != "" ) {
 | 
						|
                bstring parsed_string;
 | 
						|
                process( parsed_string, command_string, file, opts, 0 );
 | 
						|
                config_file << parsed_string << endl;
 | 
						|
                command_string = option->value.get_next_token( "^" );
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// void dump_options( options &opts )
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  opts       :  This structure contains all of the options that have
 | 
						|
//                been read out of the INI file.  They are what gets
 | 
						|
//                printed out in this routine.
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  None.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This routine is called by main() to display all of the current options
 | 
						|
//  to the user before starting to compile.  All of the options are stored
 | 
						|
//  in the opts array, and I just print them one at a time.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A    : First release
 | 
						|
//
 | 
						|
//   January 5, 1995  3.0A : Modified the code so that I don't spill
 | 
						|
//                           over the end of the line if an option line
 | 
						|
//                           is really long.  Instead, I just print some
 | 
						|
//                           dots at the end of the line.
 | 
						|
//
 | 
						|
 | 
						|
void dump_options( options &opts )
 | 
						|
{
 | 
						|
    cout.setf( ios::left );
 | 
						|
//
 | 
						|
// Symantec C++ does really funny things if you don't clear ios::right
 | 
						|
//
 | 
						|
    cout.unsetf( ios::right );
 | 
						|
    cout.width( 15 );
 | 
						|
    cout << "Model" << " : " << opts.model << "\n";
 | 
						|
    for ( item *citem = opts.get_first_item() ;
 | 
						|
          citem != 0;
 | 
						|
          citem = citem->get_next_item() ){
 | 
						|
        bstring token = citem->value.get_first_token( "^" );
 | 
						|
        int i = 0;
 | 
						|
        while ( token != "" ) {
 | 
						|
            cout.width( 15 );
 | 
						|
            if ( i++ == 0 )
 | 
						|
                cout << citem->name;
 | 
						|
            else
 | 
						|
                cout << "";
 | 
						|
            cout << " : ";
 | 
						|
            if ( strlen( token ) > 59 ) {
 | 
						|
                token.remove_after( 57 );
 | 
						|
                token = token + "...";
 | 
						|
            }
 | 
						|
            cout << token << "\n";
 | 
						|
            token = citem->value.get_next_token( "^" );
 | 
						|
        }
 | 
						|
    }
 | 
						|
    cout << "\n";
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// int compile_lib_files( options &opts, file_list &files )
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  opts       :  This structure contains all of the options that have
 | 
						|
//                been read out of the INI file.  I need them here to
 | 
						|
//                figure out how to compile each file.
 | 
						|
//
 | 
						|
//  files      :  The massive list of files.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  0 if any error occurs when compiling, 1 if every single file compiled
 | 
						|
//  without error.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This routine is called by main() to compile all the files in the file
 | 
						|
//  list.  The term "compiler" is used kind of loosely, because we don't
 | 
						|
//  really care whether it is a compiler, assembler or what.
 | 
						|
//
 | 
						|
//  The way it works is that we step through the file list, processing files
 | 
						|
//  one at a time.  Each file has a compile command, which is in the tag
 | 
						|
//  specified by the file extension.  So, for example, if I want to compile
 | 
						|
//  ..\CPP_ALL\KERMIT.CPP, I look up the tag CPP, and execute the command
 | 
						|
//  stored there.  There are a few helper functions that help me accomplish
 | 
						|
//  this.  One of the most important ones is execute(), because it has to
 | 
						|
//  break the command down into tokens and stuff them into an argv[] array
 | 
						|
//  before callling spawnv().
 | 
						|
//
 | 
						|
//  With the addition of BATCH compiles in 4.0, I have one additional
 | 
						|
//  requirement before building a file.  As I process each file, I have
 | 
						|
//  to check to see if the compiler is set to BATCH mode, or ONE_AT_A_TIME
 | 
						|
//  mode.  If it is set to BATCH mode, we aren't going to do anything here,
 | 
						|
//  so we just skip.  It is kind of inefficient, because we have to check
 | 
						|
//  this for every file, not just once per compiler.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A    : First release
 | 
						|
//
 | 
						|
//   January 5, 1995  3.0A : Added the call to process() to provide complete
 | 
						|
//                           substitution for formatting commands.  I used
 | 
						|
//                           to use sprintf() with the file name, so that
 | 
						|
//                           limited the substitutions.  Now a compiler line
 | 
						|
//                           can include anything!
 | 
						|
//
 | 
						|
 | 
						|
int compile_lib_files( options &opts, file_list &files )
 | 
						|
{
 | 
						|
//
 | 
						|
// I iterate through the loop, once per file name.  Note that the first
 | 
						|
// thing I do is check to see if the object file exists.  If it does, we
 | 
						|
// skip the compile step.  This is kind of dangerous, because we might have
 | 
						|
// an object left over from a different memory model, but it saves a lot of
 | 
						|
// time when building is interrupted by an error.
 | 
						|
//
 | 
						|
    bstring *file = files.get_first_file();
 | 
						|
    while ( file ) {
 | 
						|
        bstring extension = file->ext();
 | 
						|
        extension.remove_before( 1 );
 | 
						|
        item *compile_item = opts.find_item( extension );
 | 
						|
        if ( compile_item == 0 ) {
 | 
						|
            cerr << "Error compiling " << *file << ".  No compile command defined.\n";
 | 
						|
            return 0;
 | 
						|
        }
 | 
						|
        if ( compile_item->run_mode == item::ONE_AT_A_TIME ) {
 | 
						|
            if ( !opts.ignore && target_file_exists( *file, ".OBJ" ) ) {
 | 
						|
                cerr << "Skipping " << *file << "\n";
 | 
						|
            } else {
 | 
						|
//
 | 
						|
// Once I have the file, I loop up the compile string, and the exec file.
 | 
						|
// I use sprintf() to format a command line, then call the execute routine
 | 
						|
// to make it all happen.
 | 
						|
//
 | 
						|
                bstring buffer;
 | 
						|
                process( buffer, compile_item->value, *file, opts, 0 );
 | 
						|
                if ( execute( bstring( buffer ), opts.dry_run ) != 0 )
 | 
						|
                    return 0;
 | 
						|
            }
 | 
						|
        } // object_exists
 | 
						|
        file = files.get_next_file();
 | 
						|
    }
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
//  int lib_batch_compiles( options &opts )
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  opts       :  This structure contains all of the options that have
 | 
						|
//                been read out of the INI file.  I need them here to
 | 
						|
//                figure out how to run the compilers.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  0 if any error occurs when compiling, 1 if the compiler ran okay.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  In release 4.0, I added the ability to perform batch compilations.
 | 
						|
//  This routine performs the compilations.  If any one of the three
 | 
						|
//  compiler types has its option set to BATCH, we run the batch compiler
 | 
						|
//  instead of the one at a time compiler.  This routine does it.
 | 
						|
//
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   January 31, 1995  4.0B : First release.
 | 
						|
//
 | 
						|
 | 
						|
int lib_batch_compiles( options &opts )
 | 
						|
{
 | 
						|
    char *exts[]={ "C", "CPP", "ASM", "" };
 | 
						|
 | 
						|
    for ( int i = 0 ; exts[ i ][ 0 ] != '\0'; i++ ) {
 | 
						|
        item *compile_item = opts.find_item( exts[ i ] );
 | 
						|
        if ( compile_item->run_mode != item::BATCH )
 | 
						|
            continue;
 | 
						|
        cerr << "Building " << exts[ i ] << " files in batch mode" << endl;
 | 
						|
        bstring buffer;
 | 
						|
        process( buffer, compile_item->value, "XXXXXXXX.XXX", opts, 0 );
 | 
						|
        if ( execute( bstring( buffer ), opts.dry_run ) != 0 )
 | 
						|
            return 0;;
 | 
						|
    }
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// int build_exe_files( options &opts, file_list &files )
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  opts       :  This structure contains all of the options that have
 | 
						|
//                been read out of the INI file.  I need them here to
 | 
						|
//                figure out how to compile each file.
 | 
						|
//
 | 
						|
//  files      :  The massive list of files.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  0 if any error occurs when compiling, 1 if every single file compiled
 | 
						|
//  without error.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This is the routine I call when the program is in demo building mode.
 | 
						|
//  It is similar to the previous version used to compiler programs, but
 | 
						|
//  has enough differences to justify a separate routine.  The first big
 | 
						|
//  difference is that this guy will handle compound statements.  I can
 | 
						|
//  use four or five different lines to build a demo.  Second, in the
 | 
						|
//  previous function, I picked a command based on the extension of the
 | 
						|
//  file.  In this routine, I only use the Demo command.  I think
 | 
						|
//  that covers it.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   January 5, 1995  3.0A : First release.
 | 
						|
//
 | 
						|
//   January 5, 1995  3.0A : Added the call to process() to provide complete
 | 
						|
//                           substitution for formatting commands.  I used
 | 
						|
//                           to use sprintf() with the file name, so that
 | 
						|
//                           limited the substitutions.  Now a compiler line
 | 
						|
//                           can include anything!
 | 
						|
//
 | 
						|
//  January 26, 1995  4.0A : Added the call to build_exe_config_files()
 | 
						|
//                           before the start of each file's execution.
 | 
						|
//                           Previously demo files were restricted to
 | 
						|
//                           a single hardcode DEMO.CMD file name, now
 | 
						|
//                           they can have any number of files, as long
 | 
						|
//                           as they all fit the pattern of *EXE.CMD
 | 
						|
//
 | 
						|
 | 
						|
int build_exe_files( options &opts, file_list &files )
 | 
						|
{
 | 
						|
    bstring *compile_string = opts.find_value( "EXE" );
 | 
						|
    if ( compile_string == 0 || *compile_string == "" ) {
 | 
						|
        cerr << "Error in build of demo.  No EXE command(s) defined.\n";
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    bstring *file = files.get_first_file();
 | 
						|
    while ( file ) {
 | 
						|
        build_exe_config_files( opts, *file );
 | 
						|
        bstring command_string = compile_string->get_first_token( "^" );
 | 
						|
        if ( !opts.ignore && target_file_exists( *file, ".EXE" ) ) {
 | 
						|
            cout << "Skipping " << *file << endl;
 | 
						|
        } else
 | 
						|
            while ( command_string != "" ) {
 | 
						|
                bstring buffer;
 | 
						|
                process( buffer, command_string, *file, opts, 0 );
 | 
						|
                if ( execute( bstring( buffer ), opts.dry_run ) != 0 )
 | 
						|
                    return 0;
 | 
						|
                command_string = compile_string->get_next_token( "^" );
 | 
						|
            }
 | 
						|
        file = files.get_next_file();
 | 
						|
    }
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// int target_file_exists( const bstring &file, const bstring &ext )
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  file        :  The name of a source file.  This can contain a full
 | 
						|
//                 pathname and an extension.  Normally, this parameter
 | 
						|
//                 will be something akin to "..\\CPP_WIN\FOO.ASM".
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  1 if an object file exists, 0 if not.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This routine is called by compile_lib_files().  It doesn't want to
 | 
						|
//  compile a file if a good object for that file already exists.
 | 
						|
//  This routine just strips the path and extension, then checks to see if
 | 
						|
//  an OBJ file exists in the current directory.  If so, it returns
 | 
						|
//  a 1.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
int target_file_exists( const bstring &file, const bstring &ext )
 | 
						|
{
 | 
						|
    FILE *p = fopen ( file.name() + ext, "r" );
 | 
						|
    if ( !p )
 | 
						|
        return 0;
 | 
						|
    fclose( p );
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// int execute( bstring &command, int dry_run )
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  command    :  This is the command string, just as you would type it at
 | 
						|
//                the DOS prompt, e.g. "cl /I..\H /AL /c foo.cpp".
 | 
						|
//
 | 
						|
//  dry_run    :  If this flag is set, it means the user has passed the
 | 
						|
//                -cmd on the command line, meaning I don't really want
 | 
						|
//                to execute the command, instead I just want to display
 | 
						|
//                the commands that would have been executed.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  The return code from the spawn() command.  If things went okay it
 | 
						|
//  will be a 0.  If the command didn't run it will be 0xffff. If it
 | 
						|
//  is greater than 0, we assume the application ran and returned some
 | 
						|
//  sort of error code.  If the user hits the escape key I return 1.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This is the function that actually executes a command.  We need a
 | 
						|
//  completely separate function because of the way the spawn() functoin
 | 
						|
//  works.  Instead of passing spawnv() a nicely built command line, we
 | 
						|
//  have to break it apart into tokens, and pass an array of pointers
 | 
						|
//  to those tokens.  What a pain!  Not only that, the tokens have to
 | 
						|
//  be nice little char * objects, which means I have to allocate them
 | 
						|
//  and then free them up after the command.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A   : First release
 | 
						|
//
 | 
						|
//   June 29, 1994 2.1A   : Added the ability to cancel by hitting the ESC key.
 | 
						|
//
 | 
						|
//   January 5, 1995 3.0A : Changed the token separator characters.  I was
 | 
						|
//                          having a little problem with TLINK from Borland.
 | 
						|
//                          it seems to want *all* of its arguments piled
 | 
						|
//                          up in a single character.  When I separated them
 | 
						|
//                          base on the comma character, I was getting lots
 | 
						|
//                          of problems.  So, comma is no longer a separator.
 | 
						|
//
 | 
						|
//   January 26, 1995 4.0A : I used to search the path for the file to
 | 
						|
//                           execute, which was a lot of extra work.  I did
 | 
						|
//                           it because I figured that if I searched the
 | 
						|
//                           path once, then cached the result, I could save
 | 
						|
//                           time the next time I invoked the same command.
 | 
						|
//                           Now I'm not so sure I believe that.
 | 
						|
//
 | 
						|
 | 
						|
int execute( bstring &command, int dry_run )
 | 
						|
{
 | 
						|
    const int COUNT = 20;
 | 
						|
    char *args[ COUNT ];
 | 
						|
 | 
						|
    int i = 0;
 | 
						|
    while ( kbhit() ) {
 | 
						|
        int c = getch();
 | 
						|
        if ( c == 0x1b ) {
 | 
						|
            cerr << "User abort!\n";
 | 
						|
            return 1;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    cerr << command << "\n";
 | 
						|
    for ( bstring token = command.get_first_token( " " );
 | 
						|
          token.length() != 0;
 | 
						|
          token = command.get_next_token( " " ) ) {
 | 
						|
        if ( i >= ( COUNT - 3 ) ) {
 | 
						|
            cerr << "\nError - Too many arguments in command!\n";
 | 
						|
            return 1;
 | 
						|
        }
 | 
						|
        args[ i++ ] = strdup( token );
 | 
						|
    }
 | 
						|
    args[ i ] = strdup( "" );
 | 
						|
    args[ i + 1 ] = 0;
 | 
						|
    int ret_code = 0;
 | 
						|
    if ( !dry_run ) {
 | 
						|
#if defined( __TURBOC__ ) || defined( __WATCOMC__ )
 | 
						|
        ret_code =  spawnvp( P_WAIT, args[ 0 ], args );
 | 
						|
#else
 | 
						|
        ret_code =  spawnvp( P_WAIT, args[ 0 ], (const char * const *) args );
 | 
						|
#endif
 | 
						|
        if ( ret_code != 0 ) {
 | 
						|
            cerr << "Error code "
 | 
						|
                 << ret_code
 | 
						|
                 << " executing: <"
 | 
						|
                 << command
 | 
						|
                 << ">\n";
 | 
						|
        }
 | 
						|
    }
 | 
						|
    for ( i = 0 ; i < COUNT ; i++ )
 | 
						|
        if ( args[ i ] == 0 )
 | 
						|
            break;
 | 
						|
        else
 | 
						|
            free( args[ i ] );
 | 
						|
    return ret_code;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// int build_lib( options &opts )
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  opts       :  The options object.  I need this because it holds
 | 
						|
//                the linker command.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  0 if things didn't go well, otherwise a 1.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  After all of the files for the library have been built, we call
 | 
						|
//  this routine.  It's job is to take all the OBJ files and turn it
 | 
						|
//  into a useful library.  This might mean just running the librarian,
 | 
						|
//  but it also might mean running lib, then the linker, then implib.
 | 
						|
//  Either way, it happens here.  I just execute all of the LIB commands
 | 
						|
//  from the INI file, one after another.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A    : First release
 | 
						|
//
 | 
						|
//   January 5, 1995  3.0A : Added the call to process(), so that linker
 | 
						|
//                           command gets full format processing.
 | 
						|
//
 | 
						|
//   January 26, 1995 4.0A : The librarian work used to be divided up
 | 
						|
//                           between this function, and another one that
 | 
						|
//                           took care of linking.  Now it all happens
 | 
						|
//                           here.  All of the commands should be grouped
 | 
						|
//                           under the LIB command.
 | 
						|
//
 | 
						|
 | 
						|
int build_lib( options &opts )
 | 
						|
{
 | 
						|
    bstring *linker_string = opts.find_value( "Lib" );
 | 
						|
    if ( linker_string == 0 || *linker_string == "" )
 | 
						|
        return 1;
 | 
						|
 | 
						|
    bstring command_string;
 | 
						|
    command_string = linker_string->get_first_token( "^" );
 | 
						|
    while ( command_string != "" ) {
 | 
						|
        bstring buffer;
 | 
						|
        process( buffer, command_string, "", opts, 0 );
 | 
						|
        if ( execute( bstring( buffer ), opts.dry_run ) != 0 )
 | 
						|
           return 0;
 | 
						|
        command_string = linker_string->get_next_token( "^" );
 | 
						|
    }
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// int cleanup( file_list &files, options &opts )
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  files      :  A list of the files that were processed.  This function
 | 
						|
//                needs the list in order to delete all of the object
 | 
						|
//                files.
 | 
						|
//
 | 
						|
// opts        :  The options object.  I need this because it contains
 | 
						|
//                the DeleteFiles options, which tells me the names of
 | 
						|
//                the other files to delete.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  Always returns a 1, indicating success.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This function is called by main() after all the steps in the build
 | 
						|
//  have completed.  Its job is to delete all the objects that have been
 | 
						|
//  left around, then any other files the BUILD.INI parameters may have
 | 
						|
//  specified.  Deleting the objects is simply a matter of churning through
 | 
						|
//  the list of files, deleting them one by one.  The list of files
 | 
						|
//  to delete specified in DeleteFiles is pretty easy too, except I 
 | 
						|
//  expand them first for wildcards.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
int cleanup( file_list &files, options &opts )
 | 
						|
{
 | 
						|
//
 | 
						|
// The first half of this routine is dedicated to deleting all
 | 
						|
// the files specified in the DeleteFiles option of BUILD.INI
 | 
						|
//
 | 
						|
    bstring *delete_files = opts.find_value( "DeleteFiles" );
 | 
						|
    if ( delete_files ) {
 | 
						|
        for ( bstring wild_file = delete_files->get_first_token( "^ " );
 | 
						|
              wild_file != "";
 | 
						|
              wild_file = delete_files->get_next_token( "^ " ) ) {
 | 
						|
//
 | 
						|
// The funny cast of wild_spec is to accomodate Symantec C.  They
 | 
						|
// foolishly use char * instead of const char * for this arg.
 | 
						|
//
 | 
						|
#if __OS2__
 | 
						|
            APIRET rc;
 | 
						|
            HDIR handle;
 | 
						|
            FILEFINDBUF3 buffer = { 0 };
 | 
						|
            ULONG count;
 | 
						|
            handle = 1;
 | 
						|
            count = 1;
 | 
						|
            rc = DosFindFirst( wild_file,
 | 
						|
                               &handle,
 | 
						|
                               0,
 | 
						|
                               (PVOID) &buffer,
 | 
						|
                               sizeof( buffer ),
 | 
						|
                               &count,
 | 
						|
                               FIL_STANDARD );
 | 
						|
            while ( rc == 0 ) {
 | 
						|
                cerr << "DEL " << buffer.achName;
 | 
						|
                if ( opts.keep_files )
 | 
						|
                    cerr << "... Kept!";
 | 
						|
                else
 | 
						|
                    unlink( buffer.achName );
 | 
						|
                cerr << "\n";
 | 
						|
                count = 1;
 | 
						|
                rc = DosFindNext( handle,
 | 
						|
                                  (PVOID) &buffer,
 | 
						|
                                  sizeof( buffer ),
 | 
						|
                                  &count );
 | 
						|
            }
 | 
						|
#else
 | 
						|
            struct find_t ffblk;
 | 
						|
            unsigned int result;
 | 
						|
            result = _dos_findfirst( (char *) (const char *) wild_file, 0, &ffblk );
 | 
						|
            while ( result == 0 ) {
 | 
						|
                cerr << "DEL " << ffblk.name;
 | 
						|
                if ( opts.keep_files )
 | 
						|
                    cerr << "... Kept!";
 | 
						|
                else
 | 
						|
                    unlink( ffblk.name );
 | 
						|
                cerr << "\n";
 | 
						|
                result = _dos_findnext( &ffblk );
 | 
						|
            }
 | 
						|
#endif
 | 
						|
        }
 | 
						|
    }
 | 
						|
//
 | 
						|
// The second half of this routine is dedicated to deleting all of the
 | 
						|
// objects left behind after the compile completed.
 | 
						|
//
 | 
						|
    bstring *file = files.get_first_file();
 | 
						|
    while ( file ) {
 | 
						|
        bstring obj_name = file->name() + ".OBJ";
 | 
						|
        cerr << "DEL " << obj_name;
 | 
						|
        if ( opts.keep_files )
 | 
						|
            cerr << "... Kept!";
 | 
						|
        else
 | 
						|
            unlink( obj_name );
 | 
						|
        cerr << "\n";
 | 
						|
        file = files.get_next_file();
 | 
						|
    }
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// bstring::bstring( const char *init = "" )
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  init        :  The character string to initialize the string to.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  No returns.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This is one of the constructors for my string class.  It initializes
 | 
						|
//  the tokenizer pointer to 0 (since we don't need a tokenizer copy
 | 
						|
//  until somebody calls get_first_token()).  It allocates space for
 | 
						|
//  the initial string, then copies it.
 | 
						|
//
 | 
						|
//  Note that this guy generates an assertion error if the memory
 | 
						|
//  allocation fails.  There should never be a time when a valid bstring
 | 
						|
//  object has a null pointer in the string member.  Doing things this
 | 
						|
//  way simplifies my life a little.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
bstring::bstring( const char *init )
 | 
						|
{
 | 
						|
    if ( init == 0 )
 | 
						|
        init = "";
 | 
						|
    string = new char[ strlen( init ) + 1 ];
 | 
						|
    assert( string );
 | 
						|
    strcpy( string, init );
 | 
						|
    tokenizer = 0;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// bstring::bstring( const bstring& rhs )
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  rhs        :  The bstring object being used to construct this object.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  No returns from constructors.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This is one of the constructors for my string class.  It initializes
 | 
						|
//  the tokenizer pointer to 0 (since we don't need a tokenizer copy
 | 
						|
//  until somebody calls get_first_token()).  It allocates space for
 | 
						|
//  the initial string, then copies it.
 | 
						|
//
 | 
						|
//  Note that this guy generates an assertion error if the memory
 | 
						|
//  allocation fails.  There should never be a time when a valid bstring
 | 
						|
//  object has a null pointer in the string member.  Doing things this
 | 
						|
//  way simplifies my life a little.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
bstring::bstring( const bstring& rhs )
 | 
						|
{
 | 
						|
    const char *p = rhs;
 | 
						|
    string = new char[ strlen( p ) + 1 ];
 | 
						|
    assert( string );
 | 
						|
    strcpy( string, p );
 | 
						|
    tokenizer = 0;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// bstring::~bstring()
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  None.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  None. 
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  The string destructor just has to free memory.  There will always
 | 
						|
//  be memory allocated for the "string" member.  There may or may not
 | 
						|
//  be memory allocated in the tokenizer member.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
bstring::~bstring()
 | 
						|
{
 | 
						|
    delete[] string;
 | 
						|
    if ( tokenizer )
 | 
						|
        delete[] tokenizer;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// bstring& bstring::operator = ( const bstring& rhs )
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  rhs        :  Another string object that is going to be copied into this.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  A reference to this.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This operator is used to copy one bstring object to another.  Note
 | 
						|
//  once again the assertion error that occurs in the event of an
 | 
						|
//  allocation failure.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
bstring& bstring::operator = ( const bstring& rhs )
 | 
						|
{
 | 
						|
    if ( (const char *) rhs == string )
 | 
						|
        return *this;
 | 
						|
    delete[] string;
 | 
						|
    string = new char[ strlen( rhs ) + 1 ];
 | 
						|
    assert( string );
 | 
						|
    strcpy( string, rhs );
 | 
						|
    return *this;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// bstring& bstring::operator = ( const char *s )
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  s          :  The character string to copy into this.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  A reference to this.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This copies a character string into a bstring.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
bstring& bstring::operator = ( const char *s )
 | 
						|
{
 | 
						|
    if ( string == s )
 | 
						|
        return *this;
 | 
						|
    delete[] string;
 | 
						|
    string = new char[ strlen( s ) + 1 ];
 | 
						|
    assert( string );
 | 
						|
    strcpy( string, s );
 | 
						|
    return *this;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// bstring& bstring::upper()
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  None.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  A reference to this.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  Converts a string to all upper case.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   January 5, 1995  3.0A : First release.
 | 
						|
//
 | 
						|
 | 
						|
bstring& bstring::upper()
 | 
						|
{
 | 
						|
    for ( int i = 0 ; string[ i ] ; i++ )
 | 
						|
        string[ i ] = toupper( string[ i ] );
 | 
						|
    return *this;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// bstring bstring::operator + ( const char *s )
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  s          :  A pointer to a character string.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  A bstring that results from adding s to this.  "Add" means concatenate.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This operator adds a character string to a bstring, creating a
 | 
						|
//  new bstring.  This is very useful in a program like this, where
 | 
						|
//  we are adding strings together all the time.  Note the assertion
 | 
						|
//  error that takes place if a memory allocation fails.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
bstring bstring::operator + ( const char *s )
 | 
						|
{
 | 
						|
    assert( s );
 | 
						|
    char *p = new char[ strlen( string ) + strlen( s ) + 1 ];
 | 
						|
    assert( p );
 | 
						|
    strcpy( p, string );
 | 
						|
    strcat( p, s );
 | 
						|
    bstring new_guy( p );
 | 
						|
    delete[] p;
 | 
						|
    return new_guy;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// bstring bstring::operator + ( const bstring &rhs )
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  rhs        :  A bstring that is going to be added to this.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  A bstring that results from adding s to this. "Add" means concatenate.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This operator adds a bstring to a bstring, creating a
 | 
						|
//  new bstring.  This is very useful in a program like this, where
 | 
						|
//  we are adding strings together all the time.  Note the assertion
 | 
						|
//  error that takes place if a memory allocation fails.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
bstring bstring::operator + ( const bstring &rhs )
 | 
						|
{
 | 
						|
    char *p = new char[ strlen( string ) + strlen( rhs ) + 1 ];
 | 
						|
    assert( p );
 | 
						|
    strcpy( p, string );
 | 
						|
    strcat( p, rhs );
 | 
						|
    bstring new_guy( p );
 | 
						|
    delete[] p;
 | 
						|
    return new_guy;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// int bstring::operator != ( const char *s ) const
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  s          :  Pointer to a character string.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  True or false.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This is a wonderful function that we use all the time.  It lets
 | 
						|
//  us compare bstrings using the != operator.  One major deal to note
 | 
						|
//  is that string comaprisons are case insensitive.  If you don't know
 | 
						|
//  this things aren't going to work quite like you expect.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
int bstring::operator != ( const char *s ) const
 | 
						|
{
 | 
						|
    return stricmp( string, s );
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// int bstring::operator == ( const char *s )
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  s          :  Pointer to a character string.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  True or false.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This is a another function that we use all the time.  It lets
 | 
						|
//  us compare bstrings using the == operator.  One major deal to note
 | 
						|
//  is that string comaprisons are case insensitive.  If you don't know
 | 
						|
//  this things aren't going to work quite like you expect.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
int bstring::operator == ( const char *s )
 | 
						|
{
 | 
						|
    return !stricmp( string, s );
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// int bstring::operator == ( const bstring &rhs )
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  rhs          :  Reference to the bstring argument being compared to this.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  True or false.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This is a another function that we use all the time.  It lets
 | 
						|
//  us compare bstrings using the == operator.  One major deal to note
 | 
						|
//  is that string comaprisons are case insensitive.  If you don't know
 | 
						|
//  this things aren't going to work quite like you expect.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
int bstring::operator == ( const bstring& rhs )
 | 
						|
{
 | 
						|
    return !stricmp( string, rhs );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
//
 | 
						|
// int bstring::first( char c )
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  c        :  The character to search for.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  An index, ranging from 0 to ( strlen( this ) -1 ) if the character
 | 
						|
//  is found, or -1 if it isn't found.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This function finds the first appearance of character c in bstring this.
 | 
						|
//  It returns the index of the character.
 | 
						|
//
 | 
						|
// One really bad deal here is that this search *is* case sensitive, which
 | 
						|
// really differs from the rest of the library.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
int bstring::first( char c )
 | 
						|
{
 | 
						|
    char *p = strchr( string, c );
 | 
						|
    if ( p == 0 )
 | 
						|
        return -1;
 | 
						|
    else
 | 
						|
        return (int) ( p - string );
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// int bstring::first( const char *s )
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  s        :  The string to search for.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  An index, ranging from 0 to ( strlen( this ) -1 ) if the substring
 | 
						|
//  is found, or -1 if it isn't found.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This function finds the first appearance of substring s in bstring this.
 | 
						|
//  It returns the index of the first character of the substring.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
//   January 26, 1995  4.0A : Modified so this guy would be case insensitive.
 | 
						|
//                            This routine could use some work!
 | 
						|
//
 | 
						|
 | 
						|
int bstring::first( const char *s )
 | 
						|
{
 | 
						|
    char *x = strdup( string );
 | 
						|
    char *y = strdup( s );
 | 
						|
    assert( x );
 | 
						|
    assert( y );
 | 
						|
    for ( int i = 0 ; i < strlen( x ) ; i++ )
 | 
						|
        x[ i ] = toupper( x[ i ] );
 | 
						|
    for ( i = 0 ; i < strlen( y ) ; i++ )
 | 
						|
        y[ i ] = toupper( y[ i ] );
 | 
						|
    char *p = strstr( x, y );
 | 
						|
    free( x );
 | 
						|
    free( y );
 | 
						|
    if ( p == 0 )
 | 
						|
        return -1;
 | 
						|
    else
 | 
						|
        return (int) ( p - x );
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// int bstring::last( char c )
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  c        :  The character to search for.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  An index, ranging from 0 to ( strlen( this ) -1 ) if the character
 | 
						|
//  is found, or -1 if it isn't found.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This function finds the last appearance of character c in bstring this.
 | 
						|
//  It returns the index of the character.
 | 
						|
//
 | 
						|
// One really bad deal here is that this search *is* case sensitive, which
 | 
						|
// really differs from the rest of the library.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
int bstring::last( char c )
 | 
						|
{
 | 
						|
    char *p = strrchr( string, c );
 | 
						|
    if ( p == 0 )
 | 
						|
        return -1;
 | 
						|
    else
 | 
						|
        return (int) ( p - string );
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// int bstring::remove_after( int index )
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  index      :  The point in the string which will now be the last
 | 
						|
//                character.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  0 if the index is no good, otherwise 1.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This function lets you chop off the end of a bstring.  It deletes
 | 
						|
//  everything after a specific index.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
int bstring::remove_after( int index )
 | 
						|
{
 | 
						|
    if ( index >= (int) strlen( string ) )
 | 
						|
        return 0;
 | 
						|
    if ( index < -1 )
 | 
						|
        return 0;
 | 
						|
    string[ index + 1 ] = '\0';
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// int bstring::remove_before( int index )
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  index      :  The point in the string which will now be the first
 | 
						|
//                character.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  0 if the index is no good, otherwise 1.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This function lets you chop off the start of a bstring.  It deletes
 | 
						|
//  everything before a specific index.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
int bstring::remove_before( int index )
 | 
						|
{
 | 
						|
    if ( index > (int) strlen( string ) )
 | 
						|
        return 0;
 | 
						|
    if ( index < 0 )
 | 
						|
        return 0;
 | 
						|
    char *dest = string;
 | 
						|
    char *source = string + index;
 | 
						|
    while ( ( *dest++ = *source++ ) != '\0' )
 | 
						|
        ;
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// int bstring::operator[]( int index ) const
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  index    :  This indexing operator lets you safely get
 | 
						|
//              a character out of a bstring.  You can pass a no
 | 
						|
//              good index and this function will deal with it.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  The character, or a -1 if the index is no good.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This indexing operator returns a character from the bstring.  If
 | 
						|
//  for some reason the index is no good, it returns a -1.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
int bstring::operator[]( int index ) const
 | 
						|
{
 | 
						|
    if ( index < 0 )
 | 
						|
        return 0;
 | 
						|
    if ( index >= (int) strlen( string ) )
 | 
						|
        return 0;
 | 
						|
    return string[ index ];
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// int bstring::index_from_end( int index ) const
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  index      : The index into the string.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  Either the character from the string, or a -1 if the index is no good.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  Sometimes I want to get a character that is a certain distance from the
 | 
						|
//  end of the string.  This is just like the [] operator, except it 
 | 
						|
//  operates in revers.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
int bstring::index_from_end( int index ) const
 | 
						|
{
 | 
						|
    index = strlen( string ) - index;
 | 
						|
    if ( index < 0 )
 | 
						|
        return 0;
 | 
						|
    if ( index >= (int) strlen( string ) )
 | 
						|
        return 0;
 | 
						|
    return string[ index ];
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// bstring bstring::drive() const
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  None.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  The drive component of a file name.  If the drive component
 | 
						|
//  is not present in the file name, and empty string is returned.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This is one of four bstring functions that help to support 
 | 
						|
//  separation of filenames into components.  If the filename has
 | 
						|
//  a drive component, this guy will strip it out, put it in a 
 | 
						|
//  new string, and return it.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
bstring bstring::drive() const
 | 
						|
{
 | 
						|
    bstring drive_name = *this;
 | 
						|
    if ( drive_name != "" ) {
 | 
						|
        int i = drive_name.first( ':' );
 | 
						|
        if ( i == -1 )
 | 
						|
            drive_name = "";
 | 
						|
        else
 | 
						|
            drive_name.remove_after( i );
 | 
						|
    }
 | 
						|
    return drive_name;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// bstring bstring::path() const
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  None.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  The path component of a file name.  If the path component
 | 
						|
//  is not present in the file name, we return sort of a default
 | 
						|
//  path, "..\\";
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This is one of four bstring functions that help to support
 | 
						|
//  separation of filenames into components.  This function sort
 | 
						|
//  of tries to second guess the best thing to return for the path
 | 
						|
//  component of a file name.  I'm not sure this is as good as it
 | 
						|
//  should be, but it works for BUILD.CPP.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
bstring bstring::path() const
 | 
						|
{
 | 
						|
    bstring path = *this;
 | 
						|
    int i = path.first( ':' );
 | 
						|
    if ( i != -1 )
 | 
						|
        path.remove_before( i + 1 );
 | 
						|
//
 | 
						|
// Now we might have :
 | 
						|
//     \\comp1\\comp2\\name  which should return "\\comp1\\comp2\\"
 | 
						|
//     name                  which should return ".\\"
 | 
						|
//     \\name return         which should return "\\"
 | 
						|
//     \\name\\              which should return "\\name""
 | 
						|
//     comp1\\comp2\\name    which should return "comp1\\comp2\\"
 | 
						|
//
 | 
						|
    i = path.last( '\\' );
 | 
						|
    if ( i != -1 ) {
 | 
						|
        path.remove_after( i );
 | 
						|
        if ( path.first( '\\' ) == 0 )
 | 
						|
            path = bstring( ".\\" ) + path;
 | 
						|
        return path;
 | 
						|
    }
 | 
						|
//
 | 
						|
// Now we might have :
 | 
						|
//     name                  which should return ".\\"
 | 
						|
//
 | 
						|
    return path = ".\\";
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// bstring bstring::name() const
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  None.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  The name component of a file name.  Note that in an 8.3 system,
 | 
						|
//  this function returns the 8, but no decimal point.  The point
 | 
						|
//  gets returned with the extension.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This is one of four bstring functions that help to support
 | 
						|
//  separation of filenames into components.  This function returns
 | 
						|
//  the name portion of the file.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
bstring bstring::name() const
 | 
						|
{
 | 
						|
    bstring name = *this;
 | 
						|
    int i = name.last( '\\' );
 | 
						|
    if ( i >= 0 )
 | 
						|
        name.remove_before( i + 1 );
 | 
						|
    else {
 | 
						|
        i = name.first( ':' );
 | 
						|
        if ( i >= 0 )
 | 
						|
            name.remove_before( i + 1 );
 | 
						|
    }
 | 
						|
    i = name.first( '.' );
 | 
						|
    if ( i >= 0 )
 | 
						|
        name.remove_after( i - 1 );
 | 
						|
    return name;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// bstring bstring::ext() const
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  None.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  The extension component of a file name.  Note that in an 8.3 system,
 | 
						|
//  this function returns the 3 letter extension, along with the
 | 
						|
//  decimal point.  A lot of times you will want to strip that
 | 
						|
//  decimal point.  In fact, I think I just about always strip it.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This is one of four bstring functions that help to support 
 | 
						|
//  separation of filenames into components.  This function returns
 | 
						|
//  the extension portion of the file.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
bstring bstring::ext() const
 | 
						|
{
 | 
						|
    bstring ext = *this;
 | 
						|
    int i = ext.last( '.' );
 | 
						|
    if ( i >= 0 )
 | 
						|
        ext.remove_before( i );
 | 
						|
    else
 | 
						|
        ext = ".";
 | 
						|
    return ext;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// bstring bstring::get_first_token( const char *s = ", " )
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  s   : A string containing all the token delimiters.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  A string containing the first token from the string.  If there
 | 
						|
//  is no token you get an empty string.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  The get_first_token() / get_next_token() interface is really useful.
 | 
						|
//  It lets you suck tokens out of a string, using an arbitrary set of
 | 
						|
//  delimiters.  Usually we are just looking for tokens separated by
 | 
						|
//  spaces, but when parsing the dos PATH, we look for tokens separated
 | 
						|
//  by semicolons.
 | 
						|
//
 | 
						|
//  To make this work properly, the guy makes a little copy of the string,
 | 
						|
//  and mangles it as we go along.  When we are done with it it gets
 | 
						|
//  deleted.  It might be just as easy to just use a pointer into the
 | 
						|
//  string, but this is what I ended up with.  It works.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
bstring bstring::get_first_token( const char *s /* = " ," */ )
 | 
						|
{
 | 
						|
    if ( tokenizer )
 | 
						|
        delete[] tokenizer;
 | 
						|
    tokenizer = new char[ strlen( string ) + 1 ];
 | 
						|
    assert( tokenizer );
 | 
						|
    strcpy( tokenizer, string );
 | 
						|
    return get_next_token(s );
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// bstring bstring::get_next_token( const char *delimiters = " ," )
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  s   : A string containing all the token delimiters.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  A string containing the next token from the string.  If there
 | 
						|
//  is no token you get an empty string.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  The get_first_token() / get_next_token() interface is really useful.
 | 
						|
//  It lets you suck tokens out of a string, using an arbitrary set of
 | 
						|
//  delimiters.  Usually we are just looking for tokens separated by
 | 
						|
//  spaces, but when parsing the dos PATH, we look for tokens separated
 | 
						|
//  by semicolons.
 | 
						|
//
 | 
						|
//  To make this work properly, the guy makes a little copy of the string,
 | 
						|
//  and mangles it as we go along.  When we are done with it it gets
 | 
						|
//  deleted.  It might be just as easy to just use a pointer into the
 | 
						|
//  string, but this is what I ended up with.  It works.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
bstring bstring::get_next_token( const char *delimiters /* = " ," */ )
 | 
						|
{
 | 
						|
    assert( tokenizer );
 | 
						|
    char *temp_src = tokenizer + strspn( tokenizer, delimiters );
 | 
						|
    for ( char *temp_dest = tokenizer ;
 | 
						|
          *temp_src != '\0';
 | 
						|
          *temp_dest++ = *temp_src++ )
 | 
						|
        ;
 | 
						|
    *temp_dest = '\0';
 | 
						|
    if ( *tokenizer == '\0' ) {
 | 
						|
        delete[] tokenizer;
 | 
						|
        tokenizer = 0;
 | 
						|
        return "";
 | 
						|
    }
 | 
						|
    temp_src = tokenizer + strcspn( tokenizer, delimiters );
 | 
						|
    char temp = *temp_src;
 | 
						|
    *temp_src = '\0';
 | 
						|
    bstring result = tokenizer;
 | 
						|
    *temp_src = temp;
 | 
						|
    for ( temp_dest = tokenizer;
 | 
						|
          *temp_src != '\0';
 | 
						|
          *temp_dest++ = *temp_src++ )
 | 
						|
        ;
 | 
						|
    *temp_dest = '\0';
 | 
						|
    return result;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// options::options( char *first, ... )
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  first    : The name of the first option
 | 
						|
//
 | 
						|
//  ...      :  A list of character strings containing the names of
 | 
						|
//              any other options we expect to find.  The list needs
 | 
						|
//              to be terminated with a NULL pointer or an empty string.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  Constructor, no returns
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  The options class holds all the options we need to use in order
 | 
						|
//  to build a library.  The bulk of the information in the options
 | 
						|
//  structure consists of tag names and values that are read out of
 | 
						|
//  BUILD.INI.  A line in BUILD.INI looks like this:
 | 
						|
//
 | 
						|
//      tag=value
 | 
						|
//
 | 
						|
//  This implementation of the options class inists that we define all
 | 
						|
//  the tag names in advance, here in the constructor.  It would be
 | 
						|
//  easy to change the class so that new names encountered in BUILD.INI
 | 
						|
//  could be added dynamically, but I think it would then be harder to
 | 
						|
//  track down typos in the INI file, don't you.
 | 
						|
//
 | 
						|
//  This constructor is in charge of setting up the tag items with blank
 | 
						|
//  values.  When BUILD.EXE is busy processing the files, it will assigne
 | 
						|
//  the values to each of the tags as it encounters them.
 | 
						|
//
 | 
						|
//  There are two additional data members in options that don't get set up
 | 
						|
//  here (although they probably should).  One is the memory model read in
 | 
						|
//  from the command line.  The other is the dry_run member, used to 
 | 
						|
//  tell the program to go through the motions of building a library,
 | 
						|
//  but not to really do it.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 20, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
options::options( char *first, ... )
 | 
						|
{
 | 
						|
    va_list ap;
 | 
						|
    char *arg;
 | 
						|
 | 
						|
    va_start( ap, first );
 | 
						|
    first_item = 0;
 | 
						|
    arg = first;
 | 
						|
    while ( arg != 0 && strlen( arg ) > 0 ) {
 | 
						|
        item *new_item = new item( arg );
 | 
						|
        assert( new_item );
 | 
						|
        new_item->next_item = first_item;
 | 
						|
        first_item = new_item;
 | 
						|
        arg = va_arg( ap, char * );
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// void options::add_option( const bstring &name, const bstring &value )
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  name   :  The name of the new option to be added.
 | 
						|
//
 | 
						|
//  value  :  The initial value of the new options.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  Nothing.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  The deal used to be that you didn't get to add any new options to
 | 
						|
//  the opts structure.  The available options were defined by the ctor,
 | 
						|
//  and if you had something new, that was tough luck.
 | 
						|
//
 | 
						|
//  In 4.0, I added the ability to create as man CMD files as you wanted
 | 
						|
//  to create.  I had to have a way to keep track of the CMD files without
 | 
						|
//  predefining them, so I bit the bullet and changed the behavior of options.
 | 
						|
//  Now you can add new keys to the options any time you want.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   January 31, 1995  4.0B : First release.
 | 
						|
//
 | 
						|
 | 
						|
void options::add_option( const bstring &name, const bstring &value )
 | 
						|
{
 | 
						|
    item *new_item = new item( (const char *) name );
 | 
						|
    assert( new_item );
 | 
						|
    new_item->next_item = first_item;
 | 
						|
    first_item = new_item;
 | 
						|
    new_item->value = value;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// bstring *options::find_value( char const *tag )
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  tag       :  The name of the tag whose value we want.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  A bstring pointer, which points to the actual tag in the list opt option
 | 
						|
//  items.  If the tag could not be found, it returns a null pointer.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This is the function I use to look up a tag value.  It returns a bstring
 | 
						|
//  directly, so it is really easy to use.  It might be even better to try
 | 
						|
//  to overload the [] operator with a string, but I don't know if you can
 | 
						|
//  actually do that.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
bstring *options::find_value( char const *tag )
 | 
						|
{
 | 
						|
    item *current_item;
 | 
						|
    for ( current_item = first_item ; ; current_item = current_item->next_item ) {
 | 
						|
        if ( current_item == 0 )
 | 
						|
            return 0;
 | 
						|
        if ( current_item->name != "" && current_item->name == tag )
 | 
						|
            return ¤t_item->value;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// item *options::find_item( char const *tag )
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  tag       :  The name of the tag we are searching for.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  An item pointer, which points to the actual item in the option object.
 | 
						|
//  If the tag could not be found, it returns a null pointer.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  I need to be able to look up the item pointer for a given keyword so
 | 
						|
//  I can modify the run type from ONE_AT_A_TIME to BATCH.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   January 31, 1995 4.0B : First release.
 | 
						|
//
 | 
						|
 | 
						|
item *options::find_item( char const *tag )
 | 
						|
{
 | 
						|
    item *current_item;
 | 
						|
    for ( current_item = first_item ; ; current_item = current_item->next_item ) {
 | 
						|
        if ( current_item == 0 )
 | 
						|
            return 0;
 | 
						|
        if ( current_item->name != "" && current_item->name == tag )
 | 
						|
            return current_item;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// options::~options()
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  None.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  Nothing.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  The destructor deletes all of the items so there aren't any memory leaks.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   January 31, 1995  4.0B : First release
 | 
						|
//
 | 
						|
 | 
						|
options::~options()
 | 
						|
{
 | 
						|
    item *current_item;
 | 
						|
    item *next_item;
 | 
						|
    current_item = first_item;
 | 
						|
    for ( ; ; ) {
 | 
						|
        if ( current_item == 0 )
 | 
						|
            break;
 | 
						|
        next_item = current_item->next_item;
 | 
						|
        delete current_item;
 | 
						|
        current_item = next_item;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// void options::erase_values()
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  None.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  Nothing.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This function goes through the options list and sets each tag items'
 | 
						|
//  value to the empty string.  I do this when I am scanning the INI
 | 
						|
//  file in dump_sections().  Normally, when you read in an item for a
 | 
						|
//  second time, you would generate an error.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 20, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
void options::erase_values()
 | 
						|
{
 | 
						|
    item *current_item;
 | 
						|
    for ( current_item = first_item ; ; current_item = current_item->next_item ) {
 | 
						|
        if ( current_item == 0 )
 | 
						|
            return;
 | 
						|
        current_item->value = "";
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// bstring *file_list::get_first_file()
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  None.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  A pointer to a bstring.  When there are no more files to return,
 | 
						|
//  we return a null pointer.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  I keep all of the files I am going to compile in a file_list object.
 | 
						|
//  When iterating through the list, I use the get_first_file() 
 | 
						|
//  get_next_file() functions.  This is the get_first_file().  Note that
 | 
						|
//  I keep track of where I am in the list using the internal protected
 | 
						|
//  member current_item.  This value gets set up here for the first
 | 
						|
//  time so that subsequent calls to get_next_file() will know where
 | 
						|
//  to continue the search.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
bstring *file_list::get_first_file()
 | 
						|
{
 | 
						|
    current_item = first_file;
 | 
						|
    if ( current_item )
 | 
						|
        return ¤t_item->file_name;
 | 
						|
    else
 | 
						|
        return 0;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// file_list::~file_list()
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  None.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  Nothing.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  I added the destructor so I could clean up after myself an not have
 | 
						|
//  any memory leaks.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   January 31, 1995  4.0B : First release.
 | 
						|
//
 | 
						|
 | 
						|
file_list::~file_list()
 | 
						|
{
 | 
						|
    file_name_item *current_file;
 | 
						|
    file_name_item *next_file;
 | 
						|
    current_file = first_file;
 | 
						|
    for ( ; ; ) {
 | 
						|
        if ( current_file == 0 )
 | 
						|
            break;
 | 
						|
        next_file = current_file->next_item;
 | 
						|
        delete current_file;
 | 
						|
        current_file = next_file;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// bstring *file_list::get_next_file()
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  None.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  If another file is available in the list, we return a pointer to
 | 
						|
//  it.  If we reached the end of the list, we return a null pointer.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This is the companion function to get_first_file().
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 20, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
bstring *file_list::get_next_file()
 | 
						|
{
 | 
						|
    if ( current_item )
 | 
						|
        current_item = current_item->next_item;
 | 
						|
    if ( current_item )
 | 
						|
        return ¤t_item->file_name;
 | 
						|
    else
 | 
						|
        return 0;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// void file_list::add_file( const char *name )
 | 
						|
//
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  name   :  The name of the file to add to the list.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  Nothing.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  During the startup of this program, we add files to the file list.
 | 
						|
//  This is the function that does it.  It creates a new item, and
 | 
						|
//  assigns it the appropriate name.  Note that any memory allocation
 | 
						|
//  failure causes an assertion error and the whole deal goes to sleep.
 | 
						|
//
 | 
						|
//  If I had bothered to put an end_of_list pointer in the file_list
 | 
						|
//  class, this function would be a lot more efficient, at a very small
 | 
						|
//  price.  Oh well.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 19, 1994  2.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
void file_list::add_file( const char *name )
 | 
						|
{
 | 
						|
//
 | 
						|
// Let's add the new guy to the end of the list!
 | 
						|
//
 | 
						|
    file_name_item *new_guy = new file_name_item( name );
 | 
						|
    assert( new_guy );
 | 
						|
    file_name_item **list = &first_file;
 | 
						|
    while ( *list != 0 )
 | 
						|
        list = &(*list)->next_item;
 | 
						|
    *list = new_guy;
 | 
						|
    new_guy->next_item = 0;
 | 
						|
}
 |