OpenVMS Notes: GNV (Gnu Not Vms)

  1. The information presented here is intended for educational use by qualified OpenVMS technologists.
  2. The information presented here is provided free of charge, as-is, with no warranty of any kind.

Edit: 2022-01-06 (fixed a typo; thanks to Peter Coghlan)

What is GNU?

In the 1980s, there were several companies claiming ownership of UNIX. Around this time, MIT programming guru Richard Stallman proposed rewriting all UNIX software which would be put it into the public domain. He called his project GNU which is a recursive (self referential) acronym which stands for Gnu Not Unix.

What is GNV?

As part of HP's Unix Portability Initiative for OpenVMS, lots of UNIX code was ported to OpenVMS including GNU which was renamed to GNV (Gnv Not Vms). You can't say programmers don't have a sense of humor.

Hacking with GNV 2.1-3

note: GNV-3.0-1 is further down this page
Legend:

<ur> user response (what you type)
<sr> system response (what you should see)

First, a little DCL to see what version of GNV is installed on your VMS system.

<sr> $					! this is my DCL prompt
<ur> prod show prod gnv			! display installed products on your system
<sr> ------------------------------------ ----------- ---------
     PRODUCT KIT TYPE STATE
     ------------------------------------ ----------- ---------
     DEC AXPVMS GNV V2.1-3 Full LP Installed
     ------------------------------------ ----------- ---------

     1 item found
     $ 
Okay if GNV is installed then the BASH command should work
<sr> $					! this is my DCL prompt
<ur> bash				! we are starting BASH in the GNC environment 
<sr> tcgetattr called
     tcgetattr called
     bash$				# this is my BASH prompt
<ur> exit				# exit this shell
<sr> $					! we are back to DCL 

UNIX Shells

I don't hate UNIX/Linux command shells, I just prefer DCL (Digital Command Language). Why? In the VMS environment there is only one DCL (with a single early variant) to learn but in the UNIX/Linux world there are at least a dozen shells which go by names like:

This situation was so messy that UNIX programmers developed something called the sharp-bang hack (also known as shebang) so scripts requiring a specific shell could invoke it (providing the shell was not already running). Here is an example script:

Caveat: remember that the sharp-bang hack only works on line #1 of your script.

#!/bin/sh
# =========================================================
# title  : demo.sh
# author : Neil Rieck
# created: 1999-12-31
# caveat : this script only runs under the Bourne Shell
# =========================================================
echo "Starting demo.sh"

comment: Canadians might have named this the octothorpe-exclamation hack which is much more accurate and the abbreviation would be known as octex

Fortunately, UNIX shells only come in four general flavors, and there is only one UNIX shell on VMS which is called BASH:

1 Bourne
Shell
Oldest and simplest shell written by Steve Bourne at Bell Labs.
There are numerous Bourne-like shells.
Local shell variable declaration example:
    yada=hello
Environment variable declaration examples:
1. export yada (promotes local shell variable to global)
2. export yada=hello (creates a variable then promotes it in one labor saving command)
Invoked by executing command: /bin/sh
comment: since it is called the Bourne Shell then why didn't they invoke it with "/bin/bs" rather than "/bin/sh" ???
2 Korn
Shell
Written by Dave Korn at Bell Labs, this is a Bourne shell with a zillion additional features.
There are multiple Korn-like shells.
Local shell variable declaration example:
    yada=hello
Environment variable declaration examples:
1. export yada (promotes local shell variable to global)
2. export yada=hello (creates a variable then promotes it in one labor saving command)
Invoked by executing command: /bin/ksh
3 C
Shell
Written by Bill Joy when he was at Berkeley, the c-shell requires much less memory so many UNIX Admins prefer it as their default when logging onto a limping system
There are a few shells resembling the C-Shell.
Local shell variable declaration example:
    set yada=hello
Environment variable declaration example: setenv yada=hello
Invoked by executing command: /bin/csh
4 BASH Bourne Again SHell is published by the Free Software Foundation so comes with GNU and GNV.
People like this shell because it does almost everything asked of it.
Local shell variable declaration examples:
    yada=hello
    set yada=hello
      ("set" is superfluous)
Environment variable declaration examples:
1. export yada (promotes variable to Environment variable)
2. export yada=hello (creates a variable then promotes it in one labor-saving statement)
Invoked by executing command: /bin/sh (same as Bourne Shell above)

What is an Environment Variable?

VMS Environment (a review so we can discuss the UNIX environment)

In the VMS world, "environment variables" (originally a UNIX term) are stored two locations:

  1. a memory structure which can be accessed from DCL via the lexical f$environment() or from high level languages via system calls: f$getjpi() or lib$getjpi()
    1. variables local to the current script environment are declared with a single equal sign ("=")
    2. variables global to all scripts are declared with a double equal sign ("==")
    3. variable declarations beginning with a colon (":==") are used to group everything on a line as if the line was quoted. For example:
      This line:            say :== write sys$output
      Is Equal to this:  say == "write sys$output"
       
  2. in logical name tables which include things like home directory, current input device, current output device, etc.

Playing with DCL lexical f$environment()

<ur> say :== write sys$output			! create DCL symbol (which is also an alias)
<sr> $						! system displays prompt with no error message (so success)
<ur> say f$environment("default")		! use a lexical to display my current location
<sr>    CSMIS$ROOT3:[USR.ADMCSM]		! system displays your current default directory
     $						! ...then displays a prompt with no error message (success)
<ur> set prompt = "neil> "			! change my prompt to something else
<sr> neil>					! system displays new prompt with no error message (success)
<ur> say f$environment("prompt")		! use a lexical to programmatically display my prompt
<sr>    neil>					! system displays data from lexical "f$environment()"
     neil>					! ...then displays prompt with no error message (success)
<ur> set prompt ="$ "				! restore my prompt back to default value
<sr> $						! system displays prompt with no error message (success)

Peeking at Process-level and Job-level logical names tables

<ur> show log/process					! show process-level logical names
<sr> (LNM$PROCESS_TABLE)

     "SYS$COMMAND" = "_KAWC99$NTA1:"			<<<--- DCL get commands from here (my terminal)
     "SYS$DISK" = "CSMIS$USER3:"
     "SYS$ERROR" = "_KAWC99$NTA1:"			<<<--- s/w sends errors here (my terminal)
     "SYS$INPUT" = "_KAWC99$NTA1:"			<<<--- s/w gets data input here (my terminal)
     "SYS$OUTPUT" [super] = "_KAWC99$NTA1:"		<<<--- s/w sends data output here (my terminal)
     "SYS$OUTPUT" [exec] = "_KAWC99$NTA1:"
     "TT" = "NTA1:"
     $
<ur> show log/job					! show job-level logical names
<sr> (LNM$JOB_81EDFC40)

     "SYS$LOGIN" = "CSMIS$USER3:[ADMCSM.NEIL]"		<<<--- my home directory
     "SYS$LOGIN_DEVICE" = "CSMIS$USER3:"
     "SYS$SCRATCH" = "CSMIS$USER3:[ADMCSM.NEIL]"
     $
<ur> create yada.com					<<<--- create a file titled "yada.com"
     $write sys$output f$environment("PROCEDURE")	<<<--- enter this DCL command into the file
     $show log/proc					<<<--- enter this DCL command into the file
     <ctrl-z>						<<<--- now hit <control-Z> to finish creation
<sr> EXIT
     $
<ur> @yada.com						<<<--- execute the DCL script
<sr> CSMIS$USER3:[ADMCSM.NEIL]YADA.COM;1		<<<--- notice the current "PROCEDURE" name
     (LNM$PROCESS_TABLE)				<<<--- this comes from "show log/proc"

     "SYS$COMMAND" = "_KAWC99$NTA1:"			<<<--- can still do a <control-c> from here
     "SYS$DISK" = "CSMIS$USER3:"
     "SYS$ERROR" = "_KAWC99$NTA1:"
     "SYS$INPUT" [super] = "_KAWC99$DKA100:"		<<<--- notice that SYS$INPUT has changed to disk name
     "SYS$INPUT" [exec] = "_KAWC99$NTA1:"
     "SYS$OUTPUT" [super] = "_KAWC99$NTA1:"
     "SYS$OUTPUT" [exec] = "_KAWC99$NTA1:"
     $

Each one of your processes will have its own PROCESS logical-name table but they will all share (and could communicate via) a common JOB logical-name table. Your process can also access information stored in GROUP, SYSTEM and CLUSTER name tables but this fact is beyond the scope of this introduction. DCL (a command interpreter which can also executes scripts) stores its variables in two symbol tables private to your process.

  1. One "local" symbol table is created for every script instance. It is named "local" because it is "local" to the DCL script.
     
  2. The other table is called the "global" symbol table but it is global to only your scripts.

Local symbol declaration is done with a single equal sign ("=") while global symbol declaration is done with two equal signs ("=="). Placing a colon out in front is similar to quoted string declaration. Here are some rambling demos:

<ur> say == "write sys$output"			! this DCL symbol is similar to a UNIX alias 
<sr> $						! system displays prompt with no error message (so success)
<ur> say :== write sys$output			! this DCL symbol is similar to a UNIX alias
<sr> $						! system displays prompt with no error message (so success)
<ur> write sys$output f$logical("sys$login")	! "write" is a DCL command
<sr>    CSMIS$USER3:[ADMCSM.NEIL]
     $
<ur> say f$logical("sys$login")			! "say" is a DCL symbol we defined above
<sr>    CSMIS$USER3:[ADMCSM.NEIL]
     $
<ur> set default sys$login			! this command works like the "cd" command in UNIX
<sr> $ 

<ur> show symbol/glo/all			! show all global symbols
<sr> $FACILITY == "%X00000003"
     $IDENT == "%X00001028"
     $RESTART == "FALSE"
     $SEVERITY == "0"
     $STATUS == "%X00038140"
     ED*IT == "EDIT/EDT"
     FTP == "$TCPWARE:FTP"
     PING == "$TCPWARE:PING"
     PING2 == "$TCPWARE:PING_V2"
     TELNET == "$TCPWARE:TELNET"
     $
<ur> say = "write sys$system"			! create local symbol (which is like a UNIX alias)
<sr> $						! system displays prompt with no error message (so success)
<ur> say == "write sys$system"			! create global symbol (which is like a UNIX alias)
<sr> $
<ur> show sym/loc s*				! show local symbols beginning with "s"
<sr>   SAY = "write sys$system"
     $
<ur> show sym/glo s*				! show global symbols beginning with "s" 
<sr>   SAY == "write sys$system"
     $
<ur> yada = "hello"				! define a string variable called "yada"
<sr> $
<ur> demo = 123					! define a numeric variable called "demo"
<sr> $
<ur> neil = "sys$login"				! define a string variable called "yada"
<sr> $
<ur> say yada					! display the contents of the symbol "yada"
<sr>   hello
     $
<ur> say demo					! display the contents of the symbol "demo"
<sr>   123
     $
<ur> sho symbol demo				! show the contents of the symbol "demo" (different than display)
<sr>   DEMO = 123 Hex = 0000007B Octal = 00000000173
     $
<ur> say neil					!
<sr>   sys$login
     $
<ur> dir 'neil'					! use the contents of "neil" in a directory command
<sr> Directory CSMIS$USER3:[ADMCSM.NEIL]
     a.txt b.txt c.txt
     $

Warning: While installing new software, many installation scripts will delete all your symbols before they begin. This in done so that your aliases don't interfere with the installation script.

$ del /symbol/all				!
So if you've just invoked @sys$update:vmsinstal or $product install then find that your process is not behaving as expected, you might wish to log off then back out to restore your symbol environment.

UNIX Environment

In UNIX/Linux each shell instance supports two variable tables: Environmental and Symbol. Scripts store their variables in the symbol table while important stuff, like your terminal device name, goes into the environmental table.

In the "C" shell, these two tables are manipulated using different commands.

In most other shells...

Executing a shell script in UNIX actually spawns a new process. This means that anything changed by the script (including setting variables or changing your default directory) will be only happen in that spawned process. Once the script exits, the spawned process is destroyed and you will be auto-magically dropped back to the original process. This means that you will not be able to use a script to make quick temporary changes unless you export your changes from the local symbol table to the environmental table.

So once you think you know "global" variables from "local" variables, BASH introduces a new command called "local" which is only used to "create a variable which is local to a function". When will the madness end?

More to come... (not necessarily any time soon)

Introduction to BASH (the GNV command shell)

Why would we ever want a UNIX shell on OpenVMS. Well, take a peek into directory "AXIS2$ROOT:[bin]" and you will see two scripts:

$ dir wsdl2*/date/size

Directory DISK$USER1:[000000.axis2.bin]

wsdl2java.bat;1            6   6-DEC-2007 13:22:08.79 <<<--- provided by AXIS2 team for Windows use
wsdl2java.sh;1             2   6-DEC-2007 13:22:08.83 <<<--- provided by AXIS2 team for UNIX use

Total of 2 files, 8 blocks.

The file with the ".bat" extension is meant for use on Windows while the other file with the ".sh" extension is meant for use on UNIX. Learn how to use GNV/BASH and you'll be able to run the UNIX script on OpenVMS. The Apache-AXIS2 team didn't think it was necessary to provide a DCL script (and would probably have confused the non-VMS world by publishing a script with a ".com" extension).

Documentation Caveat: for some reason the GNV package seems to be poorly tacked onto OpenVMS. It works properly if you already know what you are doing, but locating documentation can be troublesome. Here are a few beefs.

High Value GNV-related Documents:

The Bourn Again Shell (BASH) comes with HP's GNV package which you should consider installing if you want to develop middleware on the OpenVMS platform. There are many differences between DCL and BASH but I'm only going to describe a few things about DCL symbols (BASH variables)

command DCL BASH Notes
declare a local symbol yada = "HELLO" yada=HELLO The BASH command must not include a space
declare a global symbol yada == "HELLO" export yada
(yada must already exist)
these commands have "slightly" different meanings
reference a symbol write sys$output yada echo $yada
echo ${yada}
informal method
formal method
declare a symbol with dollar yada = "sys$login" yada=sys\$login since a dollars is used to dereference a BASH symbol, you must escape them with a backslash
caveat: be aware that everything you type at a UNIX/Linux command prompt is not the same as what you may type within a BASH script. For example:
this will only work from within a BASH script:
set yada=this\ is\ a\ test
echo $yada
but from BASH command line you must type this:
export yada=this\ is\ a\ test
echo $yada
why? everything executable from the command line forks a new process
(but not commands like: source (to only name one of many)

Invoking script "WSDL2Java.sh" from GNV
(caveat: this script executes JAVA commands so JAVA must be installed)

!==============================================================================
! using GNV on OpenVMS to run AXIS2 tools like "WSDL2Java.sh"
!
! 1) GNV must be installed to use bash
! 2) this example works with JAVA5 (a.k.a. Java 1.5)
! 3) dollar symbols must be escaped ("\")
! 4) the first DCL command learns where JAVA is installed
! 5) Legend: <ur> = user response (what YOU typed)
!            <sr> = system response (what the system displayed)  
!==============================================================================

<ur> @SYS$MANAGER:JAVA$150_SETUP.COM			! init Java for this process
<sr> $							!
<ur> show sym javac					! see where Java lives
<sr>  JAVAC == "$ SYS$COMMON:[JAVA$150.bin]java$javac"	! this symbol can only be used by DCL
     $							!
<ur> set term/wrap					!
<sr> $							!
<ur> set def AXIS2$ROOT:[000000.BIN]			! navigate to here
<sr> $							!
<ur> bash						! start BASH
<sr> bash$						#
<ur> echo $JAVA_HOME					# see current definition of JAVA_HOME
<sr> bash$						#
<ur> JAVA_HOME=/SYS\$COMMON/JAVA\$150/			# define JAVA_HOME (notice the backslashes escaping the dollars)
<sr> bash$						#
<ur> export JAVA_HOME					# make it global 
<sr> bash$						#
<ur> echo $JAVA_HOME					# ensure still defined (use dollar to dereference the variable)
<sr> /SYS$COMMON/JAVA$150/				#
     bash$						#
<ur> ls -la						# get a detailed directory
<sr> total 48
     -rwxrwxrwx   1 APACHE$W 128         27256 Aug 12 12:00 INCIDENT.WSDL
     -rwxr-xr-x   1 APACHE$W 128          2771 Jul  7 11:53 axis2.bat
     -rwxr-xr-x   1 APACHE$W 128          1669 Jul  7 11:53 axis2.sh
     -rwxr-xr-x   1 APACHE$W 128          2942 Jul  7 11:53 axis2server.bat
     -rwxr-xr-x   1 APACHE$W 128          1270 Jul  7 11:53 axis2server.sh
     -rwxr-xr-x   1 APACHE$W 128          2777 Jul  7 11:53 java2wsdl.bat
     -rwxr-xr-x   1 APACHE$W 128           688 Jul  7 11:53 java2wsdl.sh
     -rwxr-xr-x   1 APACHE$W 128          3325 Jul  7 11:53 setenv.sh
     -rwxr-xr-x   1 APACHE$W 128          2708 Jul  7 11:53 wsdl2java.bat <<<--- provided by AXIS2 team for Windows use
     -rwxr-xr-x   1 APACHE$W 128           686 Jul  7 11:53 wsdl2java.sh  <<<--- provided by AXIS2 team for UNIX use
     bash$						#
<ur> mkdir ./yada					#
<sr> bash$						#
<ur> wsdl2java.sh -?					# execute the desired script
<sr> { help is displayed }				#
<ur> wsdl2java.sh -uri INCIDENT.WSDL -o ./yada		# execute the desired script
<sr> Using AXIS2_HOME:   /AXIS2$ROOT
     Using JAVA_HOME:       /SYS$COMMON/JAVA$150/
     [INFO] The ./yada/src/devenvironment/amx/Dispatcher_100201007131331_serviceBinding_
            IncidentInService_IncidentBLInbound_IncidentBLSOAPServiceBindingStub.java
            file cannot be overwritten.
     bash$						#

mnt (mount) and umnt (unmount)

GNV is a work in progress and is getting more UNIX-like every release. The first release of GNV did a really good job processing VMS-style file specifications and a not so good job of processing UNIX-style file specifications. As time goes on, you will be expected to deal with only UNIX-style file specs. This will be done by mounting VMS volumes into the POSIX root.

The built-in help for mnt and umnt are terrible and this may be because these commands are still a work in progress (at least from an OpenVMS point of view). You can learn more about these commands by searching through scripts located in SYS$STARTUP especially:

Experimental Hack #1

*** do not do this ***
*** do not do this ***
*** do not do this ***
*** do not do this ***

! Legend: <ur> = user response (what YOU typed)
!         <sr> = system response (what the system displayed)
! Note:   DISK$USER1 is a logical pointing to disk KAWC99$DRA1:
!==============================================================================
<sr> $						! this is our DCL prompt
<ur> dir psx$root:[000000]			! see what is in our posix root
<sr> Directory PSX$ROOT:[000000]
     BIN.DIR;1           dev.DIR;1           DOC.DIR;1           ETC.DIR;1
     home.DIR;1          INCLUDE.DIR;1       LIB.DIR;1           man.DIR;1
     mnt.DIR;1           SRC.DIR;1           USR.DIR;1
     $
<ur> dir psx$root:[mnt]				! see what is in posix/mnt
<sr> %DIRECT-W-NOFILES, no files found
     $
<ur> sho dev d/mount				! see our disks
<sr> Device                  Device           Error    Volume         Free  Trans Mnt
      Name                   Status           Count     Label        Blocks Count Cnt
     KAWC99$DRA0:            Mounted              0  ALPHASYS      15205120   463   1
     KAWC99$DRA1:            Mounted              0  CSMISUSER1    21715760   167   1
     $
<ur> bash					! invoke bash
<sr> bash$					# this is our BASH prompt
<ur> mnt					# see what is mounted
<sr> DISK$ALPHASYS:[000000]PSX$ROOT.DIR;1 on disk$alphasys:[vms$common.gnv]
     bash$
<ur> mnt -v -F KAWC99\$DRA1:[000000] /mnt	# see how I escaped the dollar symbol?
<sr> mnt version V2.8
     
     mnt: Mounted: DISK$USER1:[000000] on /mnt
     bash$
#
# DISK$USER1 came up because that is the VMS mount logical for disk DRA1:
#
<ur> mnt					# see what is mounted
<sr> DISK$USER1:[000000] on /mnt
     DISK$ALPHASYS:[000000]PSX$ROOT.DIR;1 on disk$alphasys:[vms$common.gnv]
     bash$
<ur> ls /mnt
<sr> BACKUP.SYS CORIMG.SYS   CSMIS
     BADBLK.SYS INDEXF.SYS   BADLOG.SYS SAVESETS
     BITMAP.SYS SECURITY.SYS CONTIN.SYS VOLSET.SYS
     bash$
<ur> exit					# leave GNV/Bash
<sr> $
<ur> dir psx$root:[mnt]				! see what is in posix/mnt
<sr> Directory PSX$ROOT:[mnt]
     000000.DIR;1 BACKUP.SYS;1   BADBLK.SYS;1   BADLOG.SYS;1 
     BITMAP.SYS;1 CONTIN.SYS;1   CORIMG.SYS;1   CSMIS.DIR;1 
     INDEXF.SYS;1 SAVESETS.DIR;1 SECURITY.SYS;1 VOLSET.SYS;1 
     $
! oops we didn't want this mess.
! It is okay for one disk volume but no good for two or more.
! Let's undo everything.
!============================================================ 
<ur> bash					! invoke bash
<sr> bash$					# this is our BASH prompt
<ur> umnt -v /mnt				# unmount this whole file system
<sr> umnt version V2.8

     umnt: Unmounted: DISK$USER1:[000000] on /mnt
     bash$					# no errors so all went well
<ur> mnt					# see what is mounted
<sr> DISK$ALPHASYS:[000000]PSX$ROOT.DIR;1 on disk$alphasys:[vms$common.gnv]
     bash$
<ur> exit					# leave GNV/BASH
<sr> $
<ur> dir psx$root:[mnt]				! see what is in posix/mnt
<sr> %DIRECT-W-NOFILES, no files found
     $

Experimental Hack #2 (we need a script for mounting root directories)

We need a script which will make the necessary directory entries into PSX$ROOT:[mnt]

$!====================================================================
$! title  : mnt_hack.com
$! author : Neil Rieck
$! edit   : 2011-03-13
$! notes  : For pedagogical use only. This is not a production script.
$! params : P1 = POSIX filespec
$!          P2 = VMS device name
$! example: @mnt_hack "CSMISUSER1" "DRA1"
$!====================================================================
$ say := write sys$output
$ dq[0,8]=34
$ say "mnt_hack"
$ say "========"
$ if p1 .eqs. ""
$ then
$       say "-e- oops, missing P1 (POSIX filespec)"
$       goto display_help
$ endif
$ if p2 .eqs. ""
$ then
$       say "-e- oops, missing P2 (vms device)"
$       goto display_help
$ endif
$       p1 = p1 - ":" - ":" - ":" -":"  ! no colons for now
$! List existing mount points.
$       mnttmpfile = "sys$scratch:psx$$start.tmp;"
$       define/user sys$output 'mnttmpfile'
$       mnt
$!      Append a space to each line to remove ambiguity from the search.
$       close/nolog tin
$       open/read tin 'mnttmpfile' /error=write_anyway
$write_anyway:
$       close/nolog tout
$       open/write tout 'mnttmpfile'
$mnt_top:
$       read tin line /error=mnt_bot
$       write tout "''line' "
$       goto mnt_top
$mnt_bot:
$       close tin /error=close_out
$       close_out:
$       close tout
$!----------------------------------------
$       call DoOneMountpoint 'p1' 'p2'
$       del/nolog/noconf PSX$$START.TMP;*
$       exit
$display_help:
$       say "-i-example syntax:"
$       say "    @mnt_hack ",dq,"CSMISUSER1",dq," ",dq,"DRA1",dq
$       say "-i-note: use double quotes to preserve case"
$       exit
$!===========================================================================
$! the following code was lifted from script:
$!        sys$startup.com:PSX$UP_STARTUP.COM
$!===========================================================================
$DoOneMountpoint: subroutine
$!
$!      Generate a mountpoint in /mnt/'P1' for P2
$!
$! P1 - Pseudo Device name
$! P2 - Real Device name or rooted directory spec
$!
$ mntpoint = p1
$ filesys = p2
$! Volume labels may have non-filename characters in them.  Check.
$! Note that for some unusual characters, this will succeed and dirname
$! will have those characters quoted by a preceding ^ character.
$ dirname = f$parse("PSX$ROOT:[mnt]''mntpoint'.DIR;",,,"name","syntax_only")
$ if dirname .eqs. ""
$ then  ! the volume name is syntactically objectionable, use the device name.
$       mntpoint = f$parse("PSX$ROOT:[mnt]''filesys'.DIR;",,,"name","syntax_only")
$       ! Dollar signs are common in VMS disk names, and though they are valid name
$       ! characters, but are annoying in Posix because the shells use $ to indicate
$       ! variable name substitution.  Remove them for convenience.
$       mntpoint = mntpoint - "$" - "$" - "$" - "$"
$ endif
$ if f$parse("PSX$ROOT:[mnt]''mntpoint'.DIR;") .eqs. ""
$ then  ! If that's not good, give up and say so.
$       write sys$output "Not creating mount point directory ""PSX$ROOT:[mnt.''mntpoint']"""
$       write sys$output "Fabricated directory name not valid."
$       exit
$ endif
$
$ if f$search ("PSX$ROOT:[mnt]''mntpoint'.DIR;") .eqs. ""
$ then
$       write sys$output "Creating directory ""PSX$ROOT:[mnt.''mntpoint']"""
$       create/dir /OWNER=SYSTEM/PROT=(S:REW,O:REW,G:REW,W:RE) PSX$ROOT:[mnt.'mntpoint']
$ endif
$
$! Have we already done this one?
$!!! Oops.  Avoid bug from this call.
$!! if filesys .eqs. (f$file("PSX$ROOT:[mnt]''mntpoint'.DIR","dvi") - "_" - ":") .and. -
$!!     "(4,4,0)" .eqs. f$file("PSX$ROOT:[mnt]''mntpoint'.DIR","fid") -
$!!  then exit
$! Have we already got this mounted?
$ search /output=nl:/nowarnings 'mnttmpfile' " on /mnt/''mntpoint' "
$ if $status .eq. 1 then exit   ! It is already there, so pass.
$
$ on error then continue
$ ! v = f$verify (1)
$ mnt -v 'filesys':[000000] PSX$ROOT:[mnt.'mntpoint']
$ if .not. $status then delete PSX$ROOT:[mnt]'mntpoint'.DIR;1 /log
$ ! f$verify (v)
$ on error then exit
$ ! If that didn't work, the message printed by mnt will be sufficient.
$ exit
$ endsubroutine ! DoOneMountpoint

Experimental Hack #3 (run the script from DCL)

! Legend: <ur> = user response (what YOU typed)
!         <sr> = system response (what the system displayed)
! Note:   gnv/bash commands mnt and umnt will be executed from DCL
!==============================================================================
<sr> $						! this is my DCL prompt
<ur> @mnt_hack					! no params to force help
     mnt_hack
     ========
     -e- oops, missing P1 (POSIX filespec)
     -i-example syntax:
         @mnt_hack "CSMISUSER1" "DRA1"
     -i-note: use double quotes to preserve case
     $
<ur> @mnt_hack "CSMISUSER1" "DRA1"
<sr> mnt_hack
     ========
     mnt version V2.8

     mnt: Mounted: DISK$USER1:[000000] on /mnt/CSMISUSER1
     $
<ur> bash					! leave DCL
<sr> bash$
<ur> mnt					# see what is mounted
<sr> DISK$USER1:[000000] on /mnt/CSMISUSER1
     DISK$ALPHASYS:[000000]PSX$ROOT.DIR;1 on disk$alphasys:[vms$common.gnv]
     bash$
<ur> ls /mnt/CSMISUSER1				# get a directory
<sr> BACKUP.SYS       CORIMG.SYS CSMIS
     BADBLK.SYS       INDEXF.SYS
     BADLOG.SYS       SAVESETS
     BITMAP.SYS       SECURITY.SYS
     CONTIN.SYS       VOLSET.SYS
     bash$
<ur> umnt -v /mnt/CSMISUSER1			# restore to original state
<sr> umnt version V2.8

     umnt: Unmounted: DISK$USER1:[000000] on /mnt/CSMISUSER1
     bash$
<ur> mnt					# see what is mounted
<sr> DISK$ALPHASYS:[000000]PSX$ROOT.DIR;1 on disk$alphasys:[vms$common.gnv]
     bash$
<ur> exit					# back to DCL
<sr> ? 

Installing GNV 3.0-1

Caveat: this package requires VMS-8.3 or higher

Installation Problems

I've got two fully patched OpenVMS-8.4 Alpha systems but cannot install GNV-3.0-1 on either of them. After unzipping the download, the installation fails at the kit validation step like so:

$ prod inst gnv                
 
   1 - DEC AXPVMS GNV V3.0-1               Layered Product
   2 - DEC AXPVMS GNV V2.1-3               Layered Product
   ? - Help
   E - Exit
 
Choose one or more items from the menu: 1
 
Performing product kit validation of signed kits ...
 
%PCSI-E-VALFAILED, validation of KAWC99$DRA1:[CSMIS.USR.][ADMCSM.NEIL]DEC-AXPVMS-GNV-V0300-001-1.PCSI$COMPRESSED;3 failed
-PCSI-E-HPC_TEXT, validate_finalize: Verification of signed file failed
%PCSIUI-E-ABORT, operation terminated due to an unrecoverable error condition
$ 

I only know of three ways to get around this problem:

1) Unzip again but make sure you use the "-b switch

unzip -b DEC-AXPVMS-GNV-V0300-001-1.ZIP
Note: click OpenVMS Notes: UNZIP to learn why you should always unzip this way on OpenVMS systems.

2) Fix file attributes like so:

set file/attribute=(rfm:fix,lrl:512,mrs:512,org:seq,rat:none) DEC-AXPVMS-GNV-V0300-001-1.PCSI$COMPRESSED

3) Install the kit without validation (not recommended unless it is an emergency):

prod inst gnv /option=novalid

Editing from within BASH

The other day some poor devil who was looking to spend more time in purgatory asked me how to edit a file from within BASH. Since I had been using EDT since the early 1980s on RT-11 and RSX-11, I told him to exit BASH then just use EDIT/EDT from a DCL prompt.

Truth be told, there is no way any self respecting IT professional can exist today without being able to use VI. Someday soon, every VMS person will find themselves on a UNIX or Linux platform with no editor other than VI. (This happens to me all the time, especially when working with contractors; IT life this side of y2k finds us living in a world where the next generation has not taken the time to learn a character-cell editor)

Personal Comments:

  1. Unlike KEYPAD mode found in EDIT/EDT where nothing is hidden from view, you need to do lots of digging in order to learn about VI and VIM. I have always suspected that the lack of online help inside VI (and little or broken online help inside VIM) were part of a conspiracy to protect UNIX admin and UNIX programming jobs. (perhaps their true purpose is to keep out the tempting fingers of management which almost always disastrous).
     
  2. Speaking of job protection, maybe there would be more VMS jobs around today if VMS people were of the same mind set. Perhaps this is what the Swedish hackers were thinking when the created a native version of VIM for VMS.

EDIT/EDT

The neat thing about BASH on VMS is that unrecognized commands in BASH are handed off to DCL (this behavior can be disabled by executing this command: export GNV_DISABLE_DCL_FALLBACK=1). So just typing EDIT from within BASH will usually bring the default editor (usually EVE) unless you have redefined the symbol. My login.com (or sys$manager:sylogin.com) always creates a symbol to redirect EDIT to EDIT/EDT like so:

$!
$! login.com
$!
$ ED*IT == edit/edt

vi (Visual editor)

Just a few basics (buy a used copy of "UNIX for DUMMIES" and "More UNIX for DUMMIES" to dig deeper)

vim (Vi IMproved)

BASH Program Development

Optional Stuff

Hard core programmers might wish to make their DCL environment completely case sensitive before starting BASH and this is how you do it:

<sr> $								!
<ur> SET PROC/CASE=SENS/PARSE=EXTENDED				! now VMS is just like UNIX (case-wise)
<sr> $								!

But some systems will now be so messed up that you might not be able to log off so this is how you restore sanity:

<sr> $								!
<ur> SET PROC/CASE=BLIND/PARSE=TRADITION			! back to pre VMS-7.2 ways
<sr> $								!
Not sure which compiler is being used here:
<sr> $								! my DCL prompt
<ur> cc /version						! first inspect my already installed C compiler
<sr> HP C V7.3-009 on OpenVMS Alpha V8.4			!
     $
<ur> bash							# DCL -> bash
<sr> bash$
<ur> cc --version						# what version of c compiler?
<sr> GNV Nov 14 2011 05:32:53					# perhaps some sort of wrapper?
     HP C V7.3-009 on OpenVMS Alpha V8.4
     bash$
<ur> gcc -version						# what version of gcc?
<sr> GNV Nov 14 2011 05:32:53					# perhaps some sort of wrapper?
     HP C V7.3-009 on OpenVMS Alpha V8.4
     bash$
<ur> gcc --help							# what compiler switches are available
<sr> [...snip...]						# better see for yourself
<ur> exit							# back to DCL
<sr> $   
Let's get on with the program development demo:
<sr> $								! VMS prompt
<ur> bash							! DCL -> bash
<sr> bash$							# GNV/BASH prompt
<ur> vim neil.c							# use vi or vim to create a file
								# beware: this file is probably upper case
enter a program similar to this:
//
// title: neil.c
//
#include <stdio.h>
int main(int argc, char *argv[]){				// code to access command line args
    int rc;							// return code
    int count;							// arg counter
    printf("Hello Neil\n");
    for (count=1;count<=argc;count++){
	printf("%s %d %s\n","-i-arg:",count-1,argv[count-1]);
    } 
    rc = 99;							// this is just to test my logic
    printf("%s%d\n","exiting with code: ",rc);			//
    return(rc);							// in unix, 0 = "no error"
}
Now lets compile, link, then run it:
 <ur> cc neil.c							# compile program
<sr> bash$
<ur> link neil.o						# link program
<sr> bash$
<ur> ./neil 123 456						# run the program (with optional params)
<sr> Hello Neil
     -i-arg: 0 neil						# image name
     -i-arg: 1 123						# p1
     -i-arg: 2 456						# p2
     exiting with code: 99
     bash$
<ur> my_status=$?						# save program exit status 
<sr> bash$
<ur> echo $my_status						# show program exit code
<sr> 123
     bash$							#

Links:


homeBack to Home
Neil Rieck
Waterloo, Ontario, Canada.