VMS Argument Converter


Previous Contents

Chapter 3
Adapting a Sample Application

This chapter describes each step in adapting a sample application that accepts Unix style arguments to alternatively accept a standard VMS style command line. It is a trivial program, which simply enumerates the arguments it is given. Before modification, the program simply echoes back the same Unix style arguments it is given. After modification, it accepts VMS arguments, and enumerates the equivalent Unix arguments that correspond to these. Thus by the time Parse_VMS_Args returns, the command line is as the rest of the program has been written to expect. Although this example is trivial, there is no difference in the effort involved in performing a similar adaptation to a much larger program.

3.1 Outline of the steps

The steps involved in the process are as follows:
  1. List the range of expected Unix command line arguments.
  2. Decide on equivalent VMS parameters and arguments.
  3. Write a .CLD file defining the VMS qualifiers, and compile it.
  4. Write a .MAP file defining the mapping between the two sets.
  5. Run the .MAP file through the Generator program to produce a C file.
  6. Compile this C file.
  7. Write a .HLP file for the interactive help, and compile it.
  8. Modify the application main program to call Parse_VMS_Args and compile.
  9. Relink the application with PARSE-VMS, and the files compiled above.

3.2 List the expected Unix command line arguments

You will need to know the various expected command line arguments so that you can provide mappings to them. This information is normally obtained from the application documentation or online help (try -h or -? to the original application). Write out these values. For example, our sample application might expect the following:


command [-db=debug_level] [-q] [-b] [-d mmddyy] filename 
where .
debug level: one of the values 1, 2, 3.
-q: suppress logging (logging is on by default)
-b: binary mode.
-d: date in numeric form mmddyy.
filename: name of file to process.

3.3 Decide on VMS Equivalents

Having listed the Unix arguments, you then decide on what you want the equivalent VMS arguments to be. Let us assume that we choose:


command [/DEBUG=level] [/LOG] [/BINARY] [/SINCE=date] filename 
where
/DEBUG_LEVEL is one of MINIMUM, DEFAULT, MAXIMUM If no value is specified, assume DEFAULT.
/LOG is on by default, and if negated (/NOLOG) corresponds to -q.
/BINARY corresponds to -b
/SINCE corresponds to -d, but the date argument will be in either VMS or ISO 8601 format
/HELP will be an extra qualifier which will invoke a HELP session.
Note that a debug level of MINIMUM will be translated to "1", a value of DEFAULT to "2" and MAXIMUM to "3". Any other value will be rejected as an error.

3.4 Write a .CLD file defining the VMS arguments

You need to create a .CLD file which specifies the syntax of the VMS style arguments. For a full description of CLD files see the VMS Command Definition Utility manual. Here is the sample .CLD file that ships with this kit.


!   CLD Definitions for SAMPLE.C 
 
 Module VARG_CLD 
 
 Define Type DEBUG_TYPES 
  Keyword MINIMUM 
  Keyword DEFAULT, default 
  Keyword MAXIMUM 
 
 Define Verb VARG 
  Parameter P1, Label=FILE 
  Qualifier LOG, Default 
  Qualifier BINARY 
  Qualifier DEBUG, Value (type=DEBUG_TYPES) 
  Qualifier SINCE, Value (type=$datetime) 
  Qualifier HELP 
Notes:

  1. The DEBUG_TYPES specify the valid arguments to the /DEBUG qualifier, with DEFAULT as the value to be assumed if the qualifier is entered without a corresponding value.
  2. The /LOG qualifier is on by default, which means it will be assumed unless the user explicitly enters /NOLOG
  3. The /SINCE qualifier takes a VMS style date argument.
  4. The Choice of Module Name must be VARG_CLD.
  5. The verb name must be VARG.
  6. Omit the type=$datetime if you wish to allow ISO 8601 style dates (e.g. 2023-04-28).

3.4.1 Compiling the CLD file

The .CLD file must be compiled into a .OBJ file. This is done by the following command (assuming the CLD file is called SAMPLE-CLD.CLD)


 $ Set Command /object SAMPLE-CLD 
 $ 
This produces a file SAMPLE-CLD.OBJ. If any errors occur, correct them and recompile before continuing.

Note

Be careful that you choose a name for the .CLD file that will not cause a conflict of names with the .OBJ file. For example, if the main program is called SAMPLE.C, don't call your .CLD file SAMPLE.CLD. If you do, then you will end up with two SAMPLE.OBJ files, one from the C compiler from SAMPLE.C and the other from your .CLD file.

3.5 Write a MAP file

The .MAP file is a text file that you create specifying how the VMS qualifiers map into Unix qualifiers, and the order in which the Unix qualifiers appear in the command line.

Note

The order of the various MAP statements in the file is important. The translator looks for the VMS arguments in the order you present them in this file, and replaces them with the equivalent Unix arguments. If some Unix arguments must precede others, then you must provide MAP statements in the order in which the output Unix arguments are to appear.
The .MAP file corresponding to what we want is below:


!   Mapping of VMS qualifiers for SAMPLE.C to the Unix ones supported by the 
!   program itself. 
 
Action Return /Allow_Unix 
 
Map HELP /help=SAMPLE /library=SYS$DISK:[]SAMPLE-HLP 
Map DEBUG "-db=" /vlist=(MINIMUM,DEFAULT,MAXIMUM) /ulist=(1,2,3) /append 
Map LOG  /negative="-q" 
Map BINARY "-b" 
Map SINCE "-d" /Date="$N$D$y" 
Map FILE $ 
Notes

  1. The ACTION keyword specifies that the translation routines return to the main program when the arguments are converted. It also specifies that if Unix arguments are detected, no changes are made.
  2. The /HELP qualifier triggers an interactive HELP session, using the library SAMPLE-HLP.HLB in the current directory, starting with help keyword SAMPLE.
  3. The DEBUG qualifier maps to "-db=" with the value appended without spaces. The strings MINIMUM, DEFAULT & MAXIMUM are replaced with 1, 2 & 3 respectively.
  4. If the qualifier /LOG is present then there is no equivalent Unix argument. If it is omitted, then it is replaced by "-q".
  5. The /SINCE qualifier is replaced by "-d" and its translated value follows in the next argument cell. The date is translated into mmddyy format (e.g. 25-OCT-2020 is replaced by 102520).
  6. The FILE parameter (this is a VMS Parameter not a qualifier) specifies that its value is inserted in the next argument cell (an argument without a leading "-" keyword). A Unix argument of '$' does not appear in the output arguments.

3.5.1 Compiling the MAP file

The MAP file must be run through the GEN-MAPPING program. This program translates the .MAP file into a C source (.C) file which is called by the Parse_VMS_Args routine. Assuming the name of your MAP file is SAMPLE-MAP.MAP, you compile it by:


 $ genvms :== $ddcu:[dirname]GEN-MAPPING 
 $ genvms SAMPLE-MAP 
where ddcu:[dirname] is where you have placed the GEN-MAPPING.EXE program. This generates the file SAMPLE-MAP.C. Check that you correct any errors before proceding. Once this is done, compile the program using:


 $ cc SAMPLE-MAP 
which will generate a SAMPLE-MAP.OBJ

Note

Be careful that you choose a name for the .MAP file that will not cause a conflict of names with the .C file. For example, if the main program is called SAMPLE.C, don't call your .MAP file SAMPLE.MAP. If you do, then you will end up with two SAMPLE.OBJ files, one from the GEN-MAPPING program, and the other from the original program source.

3.6 Write a .HLP file for interactive help

This step is only required if you want to provide an interactive help session. The capabilities of Help files are described in the VMS Help Utility manual. The sample .HLP file provided is:


1 Sample 
 
 This is a sample program.  It doesn't do anything useful, only writes out 
 the Unix equivalents of the arguments you entered. 
 
2 Parameters 
 
 Filename:  name of a file to process. 
 
2 Qualifiers 
 
/DEBUG 
 /DEBUG 
 /NODEBUG (D) 
 
 Specifies debug level.  Must be one of MINIMUM, DEFAULT or MAXIMUM. 
 If no value is given, DEFAULT is assumed. 
 
/LOG 
 /LOG (D) 
 /NOLOG 
 
 Specifies whether log output is generated. 
 
/BINARY 
 /BINARY 
 /NOBINARY (D) 
 
 Specifies whether binary mode I/O is used. 
 
/SINCE 
 /SINCE=VMS Date/Time 
 
 Specifies an optional date and/or time.  Files created before this date 
 are not processed.  The date/time is specified in standard VMS format. 
Notes:

  1. The HLP file defines a hierarchical level of help two levels deep.
  2. It is important that the first position of each line should be reserved for the numeric level number or the slash ("/") character in qualifier definitions. All other lines should be indented by at least one space.
  3. Don't extend lines too far to the right margin of the screen, as they will be wrapped at runtime, which will produce untidy output.

3.6.1 Compiling the HLP file

The .HLP file is compiled into a .HLB library as follows (assume the file is called SAMPLE-HLP.HLP):


 $ Library /help /create SAMPLE-HLP SAMPLE-HLP 
The .HLB library can be placed anywhere convenient that is read accessible to the users. If it is not placed in the SYS$HELP directory, you will need to specify a full path to the file in the /LIBRARY qualifier in the MAP file.

3.7 Modifying the Application Code

You now have to make a modification to the original code so that it calls the Parse_VMS_Args routine. This call should be put in as the first executable statement in the program, to ensure that the format of arguments is translated before the program tries to do anything with them. The following is the code of the sample program (SAMPLE.C) with the two added lines indicated.


 
/* 
 Sample C program which simply prints out the arguments that 
 it was given in the argc/argv interface 
*/ 
 
 #include <stdio.h> 
 #include <stdlib.h> 
 
main (int argc, char **argv) 
{ 
 int i; 
 int Parse_VMS_Args ();     /*  line added  */ 
 
 Parse_VMS_Args (&argc, &argv);  /*   line added */ 
 
 printf ("arg count = %d\n", argc); 
 
 for (i=0; i <= argc-1; i++) 
 printf ("arg [%d] = '%s'\n", i, argv [i]); 
 
 exit (0); 
} 
Notes:

  1. The first line declares the integer routine Parse_VMS_Args.
  2. The second line calls the routine to translate the command line arguments.

3.7.1 Recompile the main program

You then need to recompile the main program using:


 $ CC SAMPLE 
which will produce the file SAMPLE.OBJ.

3.8 Relinking the Application

Having made the changes, you now need to relink the entire application. You will need to include the following files in addition to any used by the application already: You don't need to include the .HLB file in the link command, as it will be accessed at runtime.

3.8.1 Relinking the Sample Program

The unchanged sample program is linked as follows:


 $ Link SAMPLE 
which takes the SAMPLE.OBJ and produces a SAMPLE.EXE. The altered sample program needs to be linked as follows:


 $ Link SAMPLE,SAMPLE-CLD,SAMPLE-MAP,PARSE-VMS 
which also produces a SAMPLE.EXE file. The application is now adapted for use in a VMS environment.

3.9 Debugging

If you need to verify that VMSARG is working correctly, you can define the logical name VMSARG_DEBUG, e.g.

Note

$ Define VMSARG_DEBUG 1
This will cause the Parse_VMS routing to list both the original and modified argument lists.


Chapter 4
Using VMSArg when Original Program Sources are not Available

When the original program sources are not available, you can still provide DCL like syntax, by using an initial dummy program. This program is provided as NULLMAIN.C. It accepts VMS style arguments, and translates them into the Unix equivalents. It then exits, either returning the new arguments in a DCL symbol, or directly activating the original program and passing the translated arguments to it. The examples described below shows how to do this with be supplied SAMPLE.C program.

4.1 Direct Image Activation

This would be the preferred way to activate the original program, with translated arguments.

4.1.1 Building a CLD file

The CLD file is prepared in exactly the same way as you would for a modified source approach. This file defines the VMS style commands, as described in the Command Definition Utility. The CLD file is then compiled into an .OBJ file.

4.1.2 Preparing the MAP file

The .MAP file is prepared in the same way as described in the chapter on Defining a Map file. This MAP file describes the way each VMS parameter or qualifier corresponds to a Unix argument. The only difference is that the ACTION command specifies that the translated arguments are passed to a different image.

4.1.3 Action Command for Direct Image Activation

You use an EXIT action, and specify the /COMMAND and /IMAGE qualifiers.


 ACTION EXIT  /image=SAMPLE.EXE /symbol=SAMPLE_UNIX 
The program will translate the VMS syntax into Unix syntax, then define a local DCL symbol original-command to be a Foreign Command specifying the original-image-name. It will then execute this command, passing it the Unix arguments that the program is expecting. It does this via Lib$Do_Command, rather than Lib$Spawn, as the former uses less resources. The only overhead is an additional image activation.

4.1.4 Building a Dummy Main Program

The dummy main program will call Parse_VMS to translate the arguments, and then activate the original program with the Unix style arguments it expects. VMSARG provides such a dummy program called NULLMAIN.C. This program must be compiled, and linked with the output of the compiled .CLD file, and the compiled .C output from the .MAP file.


 $ cc nullmain 
 $ Set Command /object MYMAP-CLD.CLD 
 $ genvms :== $ddcu:[dirname]GEN-MAPPING 
 $ Genvms mymap-map.map ! creates .C file 
 $ cc mymap-map.c 
 $ Link /notrace /exec=vms-sample nullmain,parse-vms,mymap-map,mymap-cld 
You can then define a symbol for users to activate the VMS-SAMPLE.EXE image.


 $ sample = "$ddcu:[directory]VMS-SAMPLE" 
Thus the SAMPLE command activates your dummy image, which translates the arguments, then activates the original image with the modified parameters.

4.2 Specifying a DCL Symbol for the Translated Arguments

Sometimes, you might want to do further processing on the Unix command output before passing it to the original program. In this case, the approach would be to define the user command to activate a DCL command procedure rather than the dummy image. This command procedure would call this image, and have it return the translated arguments in a DCL symbol, rather than activate the original application. Your command procedure can then process this symbol before passing it to the original application program. This has more overhead as you are activating two images and a DCL command procedure, but it does allow you to do additional processing that VMSArg may not provide.

4.2.1 ACTION for a DCL Symbol

For this approach, the ACTION parameter in the MAP file would specify EXIT, along with a /SYMBOL qualifier.


Action EXIT /symbol=UNIX_ARGS 

4.2.2 Building the Dummy Main Program

The dummy main program will call Parse_VMS to translate the arguments, and then exits to DCL with the DCL symbol UNIX_ARGS set to the translated arguments. As before, compile NULLMAIN and link its .OBJ with the output of the compiled .CLD file, and the compiled .C output from the .MAP file.


 $ cc nullmain 
 $ Set Command /object MYMAP-CLD.CLD 
 $ genvms :== $ddcu:[dirname]GEN-MAPPING 
 $ Genvms mymap-map.map ! creates .C file 
 $ cc mymap-map.c 
 $ Link /notrace /exec=sample-symb nullmain,parse-vms,mymap-map,mymap-cld 
You can then define a symbol for users to activate your DCL command file.


 $ sample = "@ddcu:[directory]VMS-SAMPLE" 
Your DCL command procedure will look something like


 $ translate = "$ddcu:[dirname]SAMPLE-SYMB" 
 $ translate /binary /nolog foo.txt 
 $ !  program exits with DCL symbol UNIX_ARGS set to "-b -q foo.txt" 
 $ ... ! additional optional processing 
 $ sample_unix = "$ddcu:[dirname]SAMPLE" ! original unchanged image 
 $ sample_unix 'unix_args' 

Previous Contents Contents