MODULE imgdef_scn; !++ ! ! ABSTRACT: ! Image Definition Utility -- produces an options file, a ! link MMS file, and optionally, a build MMS file from an ! image definition file. ! ! USAGE: ! ! $ IMGDEF [/NOLINK] [/NOBUILD] - ! image-definition-file-spec[.IMGDEF] subsystem ! ! (IMGDEF set up as a foreign command, i.e., IMGDEF:==$SOGSTOOLS:IMGDEF) ! ! where: ! /NOLINK don't produce a link MMS file (implies /NOBUILD) ! /NOBUILD don't produce a build MMS file ! ! If the "pseudo-optional" subsystem parameter is present, the output ! files will go to the appropriate subsystem areas, namely: ! ! Options file: subsystem_SOURCE: ! MMS files: subsystem_MMS_BUILD: ! ! Otherwise, the disk/directory of the input file name is used for ! the output files, and the user will get a warning. ! ! NOTE: VMS wildcards are supported, but be very careful when ! using with search list logicals!! ! ! AUTHOR: Lyle Sutton ! ! CREATION DATE: Dec. 23, 1987 ! ! HISTORY: ! March 1988 SPR 13180 Travisano ! Major reformatting changes. ! Removed use of CLI$ routines and FILE_PARSE function; these ! were causing problems, so replaced with in-line SCAN code. ! Major changes to #MMS_BEGIN..#MMS_END matching logic. ! Added support for #! comment directive. ! Added matching for end-of-line for most directives. ! Added file specification scanning, for making MMS files. ! Modified much of the token matching strategy. ! Added procedures for outputting link and build MMS files. ! Added wildcard filename support (instead of DCL repeat loops). ! Modified require and link-switch trees to preserve order. ! Added OPTION_ONLY directive, as safeguard against making ! unusable MMS files for some of the SH special images. ! ! 16-Feb-1989 SPR 15675 Travisano ! Fix bug in nesting level checking; forgot to decrement. !-- CONSTANT SS$_NORMAL EXTERNAL INTEGER; CONSTANT nolink_qualifier = '/NOLINK'; CONSTANT nobuild_qualifier = '/NOBUILD'; CONSTANT default_ext = '.IMGDEF'; CONSTANT nesting_depth = 3; EXTERNAL PROCEDURE lib$get_foreign ( DESCRIPTOR STRING, DESCRIPTOR STRING, REFERENCE INTEGER, REFERENCE INTEGER ) OF INTEGER; EXTERNAL PROCEDURE lib$find_file ( DESCRIPTOR STRING, DESCRIPTOR STRING, REFERENCE INTEGER, DESCRIPTOR STRING, DESCRIPTOR STRING, REFERENCE INTEGER, REFERENCE INTEGER ) OF BOOLEAN; EXTERNAL PROCEDURE lib$find_file_end ( REFERENCE INTEGER ); EXTERNAL PROCEDURE lib$stop ( VALUE INTEGER ); EXTERNAL PROCEDURE write_link_mms( ); EXTERNAL PROCEDURE write_build_mms( ); EXTERNAL PROCEDURE log( STRING ); EXTERNAL PROCEDURE debug( STRING ); EXTERNAL PROCEDURE put_error( STRING ); FORWARD PROCEDURE interpret_command_line( ); FORWARD PROCEDURE initialize_file( STRING ); FORWARD PROCEDURE write_option_file( INTEGER, FIXED STRING (256) ); ! ! --- Global data --- ! DECLARE description_file_spec : STRING; ! possibly a wildcard filespec DECLARE description_file_name : STRING; DECLARE option_file_name : GLOBAL STRING; DECLARE link_file_name : GLOBAL STRING; DECLARE build_file_name : GLOBAL STRING; DECLARE target_name : GLOBAL STRING; DECLARE subsystem : STRING; DECLARE make_option_file : BOOLEAN; DECLARE make_build_mms : BOOLEAN; DECLARE make_link_mms : BOOLEAN; DECLARE option_only : BOOLEAN; DECLARE option_file : FILE; DECLARE current_depth : INTEGER; ! Include file nesting depth DECLARE do_file_scan : BOOLEAN; DECLARE in_mms_block : STRING; ! MMS build and link statements are stored in trees for output at end. DECLARE mms_build : GLOBAL TREE ( INTEGER ) OF STRING; DECLARE mms_build_cnt : GLOBAL INTEGER; DECLARE mms_link : GLOBAL TREE ( INTEGER ) OF STRING; DECLARE mms_link_cnt : GLOBAL INTEGER; DECLARE mms_files : GLOBAL TREE ( INTEGER ) OF STRING; DECLARE mms_files_cnt : GLOBAL INTEGER; ! Options files that must precede or follow the generated options file. DECLARE require_before : GLOBAL TREE ( INTEGER ) OF STRING; DECLARE require_before_cnt : GLOBAL INTEGER; DECLARE require_after : GLOBAL TREE ( INTEGER ) OF STRING; DECLARE require_after_cnt : GLOBAL INTEGER; ! The link switches from every #LINK statement. DECLARE link_switch_tree : GLOBAL TREE ( INTEGER ) OF STRING; DECLARE link_switch_cnt : GLOBAL INTEGER; ! The symbol is the subscript with the replacement string being the value. DECLARE define_symbols : TREE ( STRING ) OF STRING; ! ! --- Set definitions --- ! SET blank ( ' ' ); SET horizontal_tab ( s'ht' ); SET form_feed ( s'ff' ); SET end_of_line ( s'eol' ); SET end_of_stream ( s'eos' ); SET pound_sign ( '#' ); SET open_paren ( '(' ); SET close_paren ( ')' ); SET open_bracket ( '[' ); SET close_bracket ( ']' ); SET ampersand ( '&' ); SET asterisk ( '*' ); SET colon ( ':' ); SET semi_colon ( ';' ); SET equal ( '=' ); SET comma ( ',' ); SET period ( '.' ); SET single_quote ( '''' ); SET double_quote ( '"' ); SET underscore ( '_' ); SET hyphen ( '-' ); SET dollar_sign ( '$' ); SET numeric_char ( '0'..'9' ); SET lower_alpha_char ( 'a'..'z' ); SET upper_alpha_char ( 'A'..'Z' ); SET alpha_char ( lower_alpha_char OR upper_alpha_char ); SET white_space ( blank OR horizontal_tab OR form_feed ); SET non_white_space ( NOT ( blank OR horizontal_tab OR form_feed OR end_of_line OR end_of_stream ) ); SET any ( NOT ( end_of_line or end_of_stream ) ); ! ! --- Token definitions --- ! TOKEN ignore_spaces IGNORE { { white_space }... }; TOKEN comment_token { '#!' }; TOKEN require_token CASELESS { '#REQUIRE' }; TOKEN before_token CASELESS { 'BEFORE' }; TOKEN after_token CASELESS { 'AFTER' }; TOKEN include_token CASELESS { '#INCLUDE' }; TOKEN define_token CASELESS { '#DEFINE' }; TOKEN substitute_token { '#(' }; TOKEN end_substitute_token { ')' }; TOKEN mms_begin_token CASELESS { '#MMS_BEGIN' }; TOKEN link_token CASELESS { 'LINK' }; TOKEN build_token CASELESS { 'BUILD' }; TOKEN mms_end_token CASELESS { '#MMS_END' }; TOKEN target_token CASELESS { '#TARGET' }; TOKEN option_only_token CASELESS { '#OPTION_ONLY' }; TOKEN link_switches_token CASELESS { '#LINK_SWITCHES' }; TOKEN link_switch_token { { '/' { alpha_char | underscore }... [white_space]... }... }; TOKEN replace_token { double_quote any... double_quote }; TOKEN symbol_token { { alpha_char | numeric_char | hyphen | underscore }... }; TOKEN eol { end_of_line }; ! VMS file specification tokens. ! TOKEN disk { { alpha_char | numeric_char | underscore | dollar_sign } [ { alpha_char | numeric_char | underscore | dollar_sign | hyphen }... ] colon }; TOKEN directory { open_bracket { { alpha_char | numeric_char | underscore | dollar_sign } [ { alpha_char | numeric_char | underscore | dollar_sign | hyphen }... ] [ period ] }... close_bracket }; TOKEN file_spec { { alpha_char | numeric_char | underscore | dollar_sign } [ { alpha_char | numeric_char | underscore | dollar_sign | hyphen }... ] [ period { alpha_char | numeric_char | underscore | dollar_sign } [ { alpha_char | numeric_char | underscore | dollar_sign | hyphen }... ] ] }; ! ! --- MACRO definitions --- ! ! ! MACRO to recognize the "#!" comment delimeter, and ignore to end of line. ! MACRO comment TRIGGER { comment_token FIND(s'eol') }; END MACRO /* comment */; ! ! MACRO to recognize the "#()" symbol substitution clause. ! MACRO symbol_statement TRIGGER { substitute_token symbol: symbol_token end_substitute_token }; CALL debug( 'Symbol: ' & symbol ); ! Look up the symbol in the replacement tree and ! insert the replacement string. All symbol names are ! stored in upper case, i.e., no case sensitivity. symbol = UPPER( symbol ); IF EXISTS( define_symbols( symbol ) ) THEN ANSWER VALUE( define_symbols( symbol ) ); ELSE CALL put_error( symbol & ' is not a defined symbol; ignored.' ); ANSWER ''; END IF; END MACRO; /* symbol_statement */ ! ! MACRO to recognize the "#DEFINE" symbol definition clause. ! MACRO define_statement TRIGGER { define_token symbol: symbol_token replace: replace_token eol }; CALL debug( '#define ' & symbol & ' ' & replace ); ! Place the symbol and replacement string into the replacement tree. ! Symbol names are NOT case sensitive, although the case of the ! the replacement string will be maintained. define_symbols( UPPER(symbol) ) = replace[ 2..(LENGTH(replace) - 1) ]; END MACRO; /* define_statement */ ! ! MACRO to recognize the "#REQUIRE [BEFORE AFTER]" clause. ! MACRO require_statement TRIGGER { require_token where: { before_token | after_token } option_file_name: { [disk] [directory] file_spec } eol }; ! Place the option_file_name into the before or after tree. IF UPPER( TRIM( where ) ) = 'BEFORE' THEN require_before_cnt = require_before_cnt + 1; require_before( require_before_cnt) = TRIM( option_file_name ); ELSE require_after_cnt = require_after_cnt + 1; require_after( require_after_cnt ) = TRIM( option_file_name ); END IF; END MACRO; /* require_statement */ ! ! MACRO to recognize the "#INCLUDE" clause. ! MACRO include_statement TRIGGER { include_token include_file_name: { [disk] [directory] file_spec } eol }; DECLARE full_file_name : STRING; DECLARE context : INTEGER; CALL debug( 'Include file: ' & include_file_name ); IF current_depth >= nesting_depth THEN CALL put_error( 'Maximum nesting depth exceeded (' & STRING(nesting_depth) & '). File: ' & include_file_name ); ELSE context = 0; IF lib$find_file( include_file_name, full_file_name, context, *,*,*,* ) THEN current_depth = current_depth + 1; START SCAN INPUT FILE full_file_name OUTPUT PROCEDURE write_option_file; current_depth = current_depth - 1; ELSE CALL put_error( 'Include file ' & include_file_name & ' not found.' ); END IF; CALL lib$find_file_end( context ); END IF; END MACRO; /* include_statement */ ! ! MACRO to recognize the #MMS_BEGIN clause. ! MACRO mms_begin_statement TRIGGER { mms_begin_token which: { link_token | build_token } eol }; in_mms_block = UPPER( TRIM( which ) ); END MACRO; /* mms_begin_statement */ ! ! MACRO to recognize the #MMS_END clause. ! MACRO mms_end_statement TRIGGER { mms_end_token eol }; in_mms_block = ''; END MACRO; /* mms_end_statement */ ! ! MACRO to recognize the "#LINK_SWITCHES" clause. ! MACRO link_statement TRIGGER { link_switches_token link_switches: link_switch_token eol }; ! Store the link switches until the link statement is written ! in the MMS file. link_switch_cnt = link_switch_cnt + 1; link_switch_tree( link_switch_cnt ) = link_switches; END MACRO; /* link_statement */ ! ! MACRO to recognize the "#TARGET" clause. ! MACRO target_statement TRIGGER { target_token target: { [disk] [directory] file_spec } eol }; CALL debug( 'Target: ' & target ); ! Store the target, until the link MMS file is written. target_name = TRIM( target ); END MACRO; /* target_statement */ ! ! MACRO to recognize the OPTION_ONLY directive. ! MACRO option_only_statement TRIGGER { option_only_token eol }; CALL log( ' OPTION_ONLY found; not creating MMS output files.' ); option_only = TRUE; END MACRO; /* option_only_statement */ ! ! MACRO to recognize an arbitrary file specification. If found, save for ! the LINK and/or BUILD MMS files, and ANSWER it back for the options file. ! MACRO filespec_statement TRIGGER { f: { [disk] [directory] file_spec } }; IF do_file_scan ! Only during file scanning THEN mms_files_cnt = mms_files_cnt + 1; mms_files( mms_files_cnt ) = f; END IF; ANSWER f; END MACRO; /* filespec_statement */ ! ! --- PROCEDURE definitions --- ! ! ! Main PROCEDURE. ! PROCEDURE imgdef_main MAIN; DECLARE nextfile : STRING; DECLARE context : INTEGER; CALL interpret_command_line(); context = 0; WHILE lib$find_file( description_file_spec, nextfile, context, *,*,*,*); CALL initialize_file( nextfile ); IF make_option_file THEN OPEN FILE ( option_file ) AS option_file_name FOR OUTPUT; ELSE OPEN FILE ( option_file ) AS 'NL:' FOR OUTPUT; END IF; ! Start the scanning of the input file. Note that any ! tokens/text not matched by the MACROs declared above ! will be simply passed through to the output, just as ! we want to happen. START SCAN INPUT FILE description_file_name OUTPUT PROCEDURE write_option_file; CLOSE FILE ( option_file ); IF make_link_mms AND NOT option_only THEN CALL write_link_mms( ); END IF; IF make_build_mms AND NOT option_only THEN CALL write_build_mms( ); END IF; IF in_mms_block <> '' THEN CALL put_error( 'Missing #MMS_END statement.' ); END IF; END WHILE; CALL lib$find_file_end( context ); END PROCEDURE; /* imgdef_main */ ! ! PROCEDURE to write the output from the START SCAN statement ! to the options file. ! PROCEDURE write_option_file( length: INTEGER, out_line: FIXED STRING (256) ); DECLARE out : STRING; ! If we're in the middle of an MMS block, put the line into ! the mms_link or mms_build tree. Otherwise, output to the ! options file. IF in_mms_block <> '' THEN IF in_mms_block = 'LINK' THEN mms_link_cnt = mms_link_cnt + 1; mms_link( mms_link_cnt ) = out_line[ 1..length ]; ELSE mms_build_cnt = mms_build_cnt + 1; mms_build( mms_build_cnt ) = out_line[ 1..length ]; END IF; ELSE ! Filter out blank lines -- unnecessary in options files. out = TRIM( out_line[ 1..length ] ); IF out <> '' THEN IF out[1] <> s'eol' THEN ! First, search for file specifications. ! We don't really care about the output, ! just that the filespec_statement macro ! is triggered when necessary. Don't do ! this scanning within option file comments. IF out[1] <> '!' THEN do_file_scan = TRUE; START SCAN INPUT STRING out_line[ 1..length ] OUTPUT STRING out; do_file_scan = FALSE; END IF; WRITE FILE (option_file) out_line[ 1..length ]; END IF; END IF; END IF; END PROCEDURE; /* write_option_file */ ! ! PROCEDURE to interpret the command line, setting some global variables ! needed for the image-definition conversion. ! PROCEDURE interpret_command_line( ); DECLARE command_line : STRING; DECLARE command_length : INTEGER; DECLARE status, ix : INTEGER; status = lib$get_foreign( command_line, *, command_length, * ); IF status <> SS$_NORMAL THEN CALL lib$stop( status ); END IF; ! Defaults for the output files are: ! make_option_file = TRUE; make_link_mms = TRUE; make_build_mms = TRUE; ! Search for the optional command line qualifiers. If found, ! set the appropriate flags, strip them out, and continue. command_line = UPPER( TRIM ( command_line ) ); ix = INDEX( command_line, nolink_qualifier ); IF (ix <> 0) THEN make_link_mms = FALSE; command_line = command_line[ 1..ix-1 ] & command_line[ ix+LENGTH(nolink_qualifier).. ]; END IF; ix = INDEX( command_line, nobuild_qualifier ); IF (ix <> 0) THEN make_build_mms = FALSE; command_line = command_line[ 1..ix-1 ] & command_line[ ix+LENGTH(nobuild_qualifier).. ]; END IF; ! We now have the description file specification, and optionally, ! the subsystem. command_line = TRIM( command_line ); ix = INDEX( command_line, ' ' ); IF (ix <> 0) THEN description_file_spec = command_line[ 1..ix-1 ]; subsystem = TRIM( command_line[ ix+1.. ] ); ELSE description_file_spec = command_line; subsystem = ''; END IF; IF description_file_spec = '' THEN CALL log( 'Usage: IMGDEF [/NOLINK] [/NOBUILD] ' & 'image-definition-file-spec' & ' subsystem'); RETURN; END IF; ! Add the default file extension, if needed. IF INDEX( description_file_spec, default_ext ) = 0 THEN description_file_spec = description_file_spec & default_ext; END IF; END PROCEDURE; /* interpret_command_line */ PROCEDURE initialize_file( nextfile: STRING ); DECLARE full_file_name : STRING; DECLARE file_name_only : STRING; DECLARE ix : INTEGER; full_file_name = nextfile; ! use local buffer description_file_name = full_file_name; full_file_name = ! strip extension full_file_name[ 1..INDEX(full_file_name,default_ext)-1 ]; ix = INDEX( full_file_name, ']' ); IF ix = 0 THEN ix = INDEX( full_file_name, ':' ); END IF; file_name_only = full_file_name[ ix+1 .. ]; ix = INDEX( file_name_only, ']' ); ! in case [ROOT.][SUB.SOURCE] IF ix <> 0 THEN file_name_only = file_name_only[ ix+1 .. ]; END IF; ! Make all the file names (even if some of the files won't ! be used). If the subsystem is specified, use it, else ! use the default directory of the input file. Also, set ! the default target name of the executable image. IF subsystem = '' THEN option_file_name = full_file_name & '.OPT'; link_file_name = full_file_name & '_LNK.MMS'; build_file_name = full_file_name & '_BLD.MMS'; target_name = full_file_name & '.EXE'; CALL put_error( 'Warning: No subsystem given; ' & 'using default directory.' ); CALL put_error( ' Check output files for validity.' ); ELSE option_file_name = subsystem & '_SOURCE:' & file_name_only & '.OPT'; link_file_name = subsystem & '_MMS_BUILD:' & file_name_only & '_LNK.MMS'; build_file_name = subsystem & '_MMS_BUILD:' & file_name_only & '_BLD.MMS'; target_name = subsystem & '_EXE:' & file_name_only & '.EXE'; END IF; IF NOT make_link_mms THEN link_file_name = ''; make_build_mms = FALSE; ! no BLD w/out LNK END IF; IF NOT make_build_mms THEN build_file_name = ''; END IF; ! Give the user some useful information. CALL log( ' ' ); CALL log( 'Image Definition File: ' & description_file_name ); CALL log( ' ' ); CALL log( ' Option File [output]: ' & option_file_name ); CALL log( ' Link MMS File [output]: ' & link_file_name ); CALL log( ' Build MMS File [output]: ' & build_file_name ); CALL log( ' ' ); ! We need to initialize some global data structures, ! since we may be processing more than one file. current_depth = 0; do_file_scan = FALSE; option_only = FALSE; in_mms_block = ''; PRUNE mms_build; mms_build_cnt = 0; PRUNE mms_link; mms_link_cnt = 0; PRUNE mms_files; mms_files_cnt = 0; PRUNE require_before; require_before_cnt = 0; PRUNE require_after; require_after_cnt = 0; PRUNE link_switch_tree; link_switch_cnt = 0; PRUNE define_symbols; END PROCEDURE; /* initialize_file */ END MODULE; /* imgdef_scn */