This is the utility from Paul Nankervis as I found it at and which I updated for Linux.

The (original) source uses gets() to read a command (prompted by "$>"). Building the tool with the gcc tool chain gives you warnings like:

ods2.c: In function ‘main’:
ods2.c:1165:17: warning: implicit declaration of function ‘gets’ [-Wimplicit-function-declaration]
             if (gets(str) == NULL) break;
ods2.c:(.text.startup+0x368): warning: the gets' function is dangerous and should not be used.

On other systems you may get other (even run-time) warnings or you may not be able to build the tool.
This patch was sent to me to replace the call to gets with one to fgets.

The current ods2 util, as provided in the zip file and built in a 32-bit environment, is not able to mount disks bigger than 2GB. This is caused by using lseek() to position to disk blocks. With bigger disks the usual error is:
$ ./ods2 mount sdc
ODS2 V1.3hb
lseek : Invalid argument
lseek failed -1
Accessfile status 500
Mount failed with 500

To remove this restriction in a 32-bit environment, one can patch phyunix.c to use lseek() with a 64-bit offset.

Also, the current ods2 util is not able to mount ods2 disks initialized without a primary home block, which is the default on I64 unless /NOGPT is specified and can be requested on Alpha with /GPT.

Changes for Linux:

BIG_ENDIAN conflicts with another already defined macro. Something from BSD, I don't know. So I changed it to

The compiler complained about some string routines therefore in some sources I changed strings.h to string.h.

I got it to work. But copying files to Linux directories didn't work. Yes, because of the '/' being an introducer for a qualifier. So I changed cmdsplit

 * New feature for Unix: '//' stops qualifier parsing (similar to '--'
 * for some Unix tools). This enables us to copy to Unix directories.
 *      copy /bin // *.com /tmp/*
 * is split into argv[0] -> "*.com" argv[1] -> "/tmp/*" qualv[0] -> "/bin"

Next I thought it would be nice to have a /PAGE for the directory command and for the type command and for search. But then I thought that this is Linux and usually there is a good pipe implementation which can do that for me. The only thing I missed was a one-liner to start the ods2 reader with some commands. OK, there is the undocumented '@' and there is input redirection. But both require editing a file. For simple command sequences it seems too much overhead. Having a one-liner in the recall buffer and changing that is much easier. So I added command arguments which are interpreted as chained commands to the ods2 reader.

There are no command line options, yet. So everything is accepted as a command string(s). As an internal delimiter I chose '$'.

 * Parse the command line to read and execute commands:
 *      ./ods2 mount scd1 $ set def [hartmut] $ copy *.com $ exit
 * '$' is used as command delimiter because it is a familiar character
 * in the VMS world. However, it should be used surrounded by white spaces;
 * otherwise, a token '$copy' is interpreted by the Unix shell as a shell
 * variable. Quoting the whole command string might help:
 *      ./ods2 'mount scd1 $set def [hartmut] $copy *.com $exit'
 * If the ods2 reader should use any switches '-c' should be used for
 * the command strings, then quoting will be required:
 *      ./ods2 -c 'mount scd1 $ set def [hartmut] $ copy *.com $ exit'
 * The same command concatenation can be implemented for the prompted input.
 * As for indirect command files (@), it isn't checked if one of the chained
 * commands fails. The user has to be careful, all the subsequent commands
 * are executed!

Now I can use the ods2 reader like

./ods2 mount sda $ dir [...]/file/date $ exit |more
./ods2 mount sda $ dir [...]/file/date $ exit |grep '-2003' |more
./ods2 'mount sda: $set def [hartmut] $copy/bin *.com // /tmp/* $exit'

The single quotes are useful if I want to prevent the Linux shell from expanding it's meta characters: '*' etc. The '/bin' is not useful for text files like command procedure. Here it is used just to show the effect of '//'. So the last example gives:

ODS2 V1.3hb
%MOUNT-I-MOUNTED, Volume SYS073       mounted on sda:
%COPY-S-COPIED, sda:[HARTMUT]CRECRC.COM;38 copied to /tmp/x/CRECRC.COM (59 records)
%COPY-S-COPIED, sda:[HARTMUT]JSTART.COM;4 copied to /tmp/x/JSTART.COM (4 records)
%COPY-S-COPIED, sda:[HARTMUT]LOGIN.COM;2 copied to /tmp/x/LOGIN.COM (71 records)
%COPY-S-COPIED, sda:[HARTMUT]NB.COM;5 copied to /tmp/x/NB.COM (15 records)
%COPY-S-COPIED, sda:[HARTMUT]NBR.COM;1 copied to /tmp/x/NBR.COM (3 records)
%COPY-S-COPIED, sda:[HARTMUT]OLD-NB.COM;1 copied to /tmp/x/OLD-NB.COM (15 records)
%COPY-S-COPIED, sda:[HARTMUT]PCDISK.COM;3 copied to /tmp/x/PCDISK.COM (4 records)
%COPY-S-COPIED, sda:[HARTMUT]START.COM;2 copied to /tmp/x/START.COM (3 records)
%COPY-S-COPIED, sda:[HARTMUT]U2V.COM;41 copied to /tmp/x/U2V.COM (236 records)
%COPY-S-COPIED, sda:[HARTMUT]V2U-OLD.COM;1 copied to /tmp/x/V2U-OLD.COM (36 records)
%COPY-S-COPIED, sda:[HARTMUT]V2U.COM;35 copied to /tmp/x/V2U.COM (59 records)
%COPY-S-COPIED, sda:[HARTMUT]X.COM;1 copied to /tmp/x/X.COM (91 records)
%COPY-S-COPIED, sda:[HARTMUT]XLOGIN.COM;1 copied to /tmp/x/XLOGIN.COM (89 records)
%COPY-S-NEWFILES, 13 files created

These examples always have an exit command at the end. But that's not necessary. If there is none, the ods2 reader continues reading commands from

./ods2 mount sda $ set def [hartmut]
 ODS2 V1.3hb
%MOUNT-I-MOUNTED, Volume SYS073       mounted on sda:
$> dir *.com
Directory sda:[HARTMUT]

CRECRC.COM;38       JSTART.COM;4        LOGIN.COM;2         NB.COM;5
NBR.COM;1           OLD-NB.COM;1        PCDISK.COM;3        START.COM;2
U2V.COM;41          V2U-OLD.COM;1       V2U.COM;35          X.COM;1

Total of 13 files.
$> exit

So on a Linux box an alias can be set up such that I end up on the VMS disk in my directory.
I'm not happy with the (error) messages being written to stdout, they should go to stderr, so I can redirect only the wanted output to a file. Maybe I'll change that, later.
One note on copy/binary. It is not what one might expect. The ods2 reader checks if the VMS file has the attributes FAB$M_CR | FAB$M_PRN | FAB$M_FTN. If present, the read data is assumed to be text and a "\n" is appended to each record when writing the copy. "/binary" suppresses that. If the VMS file has none of the attributes set, the data is copied as is. Executables with fixed length records will be copied as 512 byte records and will not differ if copied with or without "/binary". Files with variable record length will always be copied without the record length, regardless of "/binary". But if "/binary" is used on a file, which has one of the above attributes set, the file may not be copied: the ods2 reader can't handle empty records in "binary" mode. Anyway, I was not able to copy a Java class file and run it on Linux.
On VMS there can be virtual disks (VDAnnn, LDAnnn), which make a disk out of a file. These files, disk images, can be copied to Linux disks. To let ods2 read these virtual disks a simple symbolic link from /dev/diskfile to the file is set up. Then the usual 'mount diskfile' can be used. Sorry, the user needs the right to create the symbolic link in /dev or root has to do that. Or losetup can be used to create a loop device for the disk image, so a 'mount loop0' will work. Again, the needs the right to do that.