Porting Issuelist

Updated 2021-03-20, by Martin Borgman

This is a list of known areas where compatibility problems occur when porting from Unix to OpenVMS.


sequential number (1...31 from cov list, rest locally generated)
severity to our port (VMS encoded..)/our priority rating (VMS encoded..)
The severity and priority are encoded from the Openoffice porting group viewpoint.
(default = I/0)
VMS encoded activity from the Openoffice group viewpoint.
CUR: activly being worked on.
COM: no local activity due to lack of local resources.
LEF: waiting for something under local control (f.i. media).
CEF: waiting for something under external control (f.i. patch from VMS-enginering).
(default = LEF)
Short description of the issue.
Explanation of the issue and known workarrounds, fixes and viewpoints.
issue# sev/prio status description explanation, status
1. I/0 COM stat and st_ino On Unix stat.h has:
ino_t st_ino;
but on OpenVMS it has
ino_t st_ino[3];
consequently the st_ino part of the stat structure is a value on Unix and a pointer on OpenVMS. Generally this means you have to replace something like
if(inode == foo.st_ino)
if(inode[0] == foo.st_ino[0] &&
inode[1] == foo.st_ino[1] &&
inode[2] == foo.st_ino[2[)
2. I/0 COM write() to tcp/ip socket write() is supposed to send as much of a buffer as it can to the output device and then return that value. On OpenVMS write() will fail if it attempts to write more than 64k bytes to a socket. So even though write() is usually used in a loop that cycles through until a buffer is written out it won't work correctly on OpenVMS if the buffer is too big. The workaround is to put in an #ifdef that restricts the transfer to < 64k bytes at one time.
3. I/0 COM use of select() OpenVMS select() only works on sockets, it does NOT work on files. Therefore code which uses select() to synchronize IO for both sockets and files will not function.
The fix is reasonably complex. For an example see:
4. E/16 CUR foo.tar.gz Unix doesn't care what a file is, OpenVMS does. ODS2 will only accept one ".extension" in a file name. ODS5 will accept pretty much anything that Unix will. So, if a program from unix assumes that it can append ".whatever" and have a valid file name it will fail frequently on OpenVMS. Currently the system disk cannot be ODS5, so even if a program does this and works on an ODS5 user volume, it will fail if it is run on the system disk.
Workaround: Hunt through the code and use some other method to rename the files. My presonal preference is:
blah.foo + .whatever -> blah_foo.whatever
(OO) using crtl_init.c from the GNV-tree promisses a (more) simple port when using only ODS5 disks. At this moment (apr-2003) we plan to do a ODS5 only port.
5. W/4 COM A zillion warnings on compilation Bad news Bucky, most software is crap and you're looking at it. The OpenVMS compilers are much pickier by default than those on other platforms and show you where the problems are. Your best bet in general is to fix the warnings you see, and make the compiler air out all the code's dirty laundry. Get your code to compile cleanly with
and you'll save yourself a lot of trouble later on. It will also make your code more portable.
(OO) we know from the porting newsgroup and from direct contact that there will be lots of 64 bit issues and typical compiler version specific workarounds in the code. We'll try to produce clean code, but won't modify general code just for cleanlyness.
6. E/16 CUR use of unix paths Most Unix code thinks that it can always take a path and add "/blah/foo.bar" to get a relative path. This will work on OpenVMS as well so long as the initial path is in Unix format. However if somebody entered: "USRDISK:[JOE.TEST]" then the full file spec will be a hybrid: "USRDISK:[JOE.TEST]/blah/foo.bar" which won't fly. Compaq C provides functions for converting back and forth between OpenVMS and Unix file/path specs. In general, you want to stick with Unix file paths since compilers on some other nonUnix platforms support them, but OpenVMS file paths are only useful on OpenVMS.
Ultimate fix: a standard set of C routines which define a file/path structure something like:
char * nodename;
struct ACCESSSTRU access;
char * device;
struct PATHSTRU path;
char * name;
char * type;
char * version;
which will hide all the delimiter information (so no more explicit testing of []:/\. etc. in the code). And even then programmers will have to stick to the routines which manipulate this structure. So long as all this information is lumped into one character string inside the code there will always be portability problems. This was a design flaw in the C language which should have been handled back when "fopen" was first defined.
(OO) using crtl_init.c from the GNV-tree promisses a (more) simple port when using only ODS5 disks. At this moment (apr-2003) we plan to do a ODS5 only port.
7. I/0 LEF stream-lf and long writes The default file type produced by the C RTL on OpenVMS is stream-lf. It is very similar to the text file format on Unix EXCEPT that records can't go above 32767 bytes. Well, they can, but they get split and basically it doesn't quite work like it does on Unix.
Workaround: If possible, use binary file types. Also complain loudly and bitterly (and most likely, futilely) to Compaq and maybe someday they'll fix it.
8. I/0 LEF what about X11? That's a whole other subject. See: "X11/Motif portability concerns, Unix to OpenVMS" at http://seqaxp.bio.caltech.edu/www/X11_VMS_NOTES.TXT
9. W/4 COM data types Ironically this will get you most often when you port from Tru64. "long int" there is 64 bits, but it's only 32 bits on OpenVMS. Tru64 also has a long long type which is 128 bits. For "long int" you can usually #ifdef in "unsigned __int64" on the OpenVMS version. For "long long" best hope that the code doesn't really need 128 bits, because you can't get it on this platform!
(OO) we know from the porting newsgroup and from direct contact that there will be lots of 64 bit issues and typical compiler version specific workarounds in the code. We'll try to produce clean code, but won't modify general code just for cleanlyness.
10. I/0 LEF ioctl for terminals If you encounter ioctl() calls being used to control a terminal you are out of luck. The only solution is to rewrite the code using QIO's. You will usually need to access the documentation from the source Unix, as ioctl isn't all that portable among unix's either.
11. E/16 CUR makefile OpenVMS has no "make". You can rewrite makefile to descrip.mms and use either MMS (part of DECset) or MMK (free) and obtain similar functionality. Your other choice is to install GNV from http://gnv.sourceforge.net/. It has bash and gmake. Use:
$ @gnv:setup
$ bash
bash> make -n > make_vms.com
bash> exit
Then use an editor (I prefer NEDIT for this work) to convert the unix commands to their OpenVMS equivalents.
(OO) We know we have to have DMAKE. This MAKE version is the achilles-heel of this port. The whole project depends on the availability of DMAKE. We have a alpha version working. Continued improvements in the CRTL and the porting library will make DMAKE a more stable and reliable product.
12. I/0 LEF lines are too long to view Many of the OpenVMS tools don't like the wide records which are often found in source code and other files originating on Unix systems. For instance, you can't TYPE many files, and EDIT/EDT chokes on long records too. I usually use NEDIT to handle these sorts of file problems.
13. I/0 LEF why does it run so slowly? After you complete your port you will often find that the OpenVMS version is much, much slower than the Unix version if it is very IO intensive. The ratio can be as high as 100X faster for Unix over OpenVMS. Most often this is due to the file caching speedup from Unix, plus the 2-6X slowdown that RMS imposes on record oriented files (as measured on a RAMdisk.) Binary IO speeds will be closer, the IO slowdown is primarily a problem for text/record oriented files. You can improve performance by using the SET RMS command to increase the block and buffer sizes. Also increase tbe /extend value to avoid a flurry of short extends. If you know ahead of time the final size of the output file set extend to that. (You can also set these parameters in the code by using OpenVMS specific extensions.) Improvements in the OpenVMS file caching system are in late stages of development, an dthose should help once they appear on production systems.
That said, watch out for the use of, umm, unusual file techniques on programs from Unix. For instance, you may find frequent calls to freopen(), or other program methods which cause the disk heads to fly back and forth between the front of the file and the end. These sorts of bad design are masked on Unix by the file caching system, but are exposed in all their glory on OpenVMS.
Programs may also run slowly on OpenVMS, or not at all, if they expect to be able to grab vast amounts of memory. User accounts on OpenVMS are usually allocated a relatively small amount of virtual and physical memory.
(OO) We hope to reach a stadium we can experience this problem ourselves ;-).
14. I/0 LEF why do I get two (or more) copies of the output file? Many Unix programs try to determine if they can write an output file by doing an fopen, and if it succeeds, doing another. On Unix the second one overwrites the first and no one's the wiser. On OpenVMS you get two versions. Sometimes scratch files are used in a similar manner, only for those they tend to be opened and closed many more times, resulting in a long string of files.
Fix: find the offending second fopen() and #ifdef it and the fclose() before it out of the code. For scratch files, either reopen the existing file or be sure to delete the file after the fclose().
15. I/0 LEF fseek/ftell don't work right These assume a Unixlike file organization which may or may not be present on files being read by a C program in OpenVMS.
Fix: rewrite the code to use fgetpos() and fsetpos().
16. W/4 CUR linker can't resolve ReadDir By default Unix is case sensitive pretty much everywhere. OpenVMS is not. So the symbol "ReadDir" goes to "readdir" which is the standard function. You'll also see this a lot where people have a variable "foobar" and a function "Foobar". It's generally a problem any place code uses case to distinguish between variables and/or functions. (Constants, which only the compiler sees, tend not to cause so much havoc.) Code written this way is really a pain to port because you (usually) didn't write it, and so tend not to notice these minor case differences. The bugs which can result if the linker does manage to resolve things, but incorrectly, can be very messy.
Preferred fix: rewrite the code so that all symbols are unique when uppercased.
Other fix: use the compiler switch /names=as_is, which preserves case.
(OO) We'll try to produce clean code, but won't modify general code just for cleanlyness.
17. I/0 LEF compiler gives an implicit function warning for a common function This happens either because the appropriate header has been omitted or because the function in question is #ifdef'd off by language standard specific defines. Most Unix compilers are really sloppy about which function is in which language spec. Ok, they're not sloppy, but the programmers never put them into a mode where they check. So some function which is an XOPEN extension compiles in without warnings on Unix but raises a warning on OpenVMS.
Fix: Use /define=(_XOPEN_SOURCE,_XOPEN_SOURCE_EXTENDED,_POSIX_SOURCE) as required to get the right lines of the header files processed.
18. W/2 COM where are the standard header files? They always live in the text library:
but may also have been expanded into
(OO) We installed two platforms (solaris and LINUX) just for refence.
19. E/4 COM what's wrong with #include <../../foo/woo/blah.h>? Putting paths into includes is BAD, EVIL, YUCKY, etc., and unfortunately, fairly common. The example shown here is the worst case - you'll only ever have a hope of compiling this if the default directory is in exactly the right place.
Preferred fix: Whenever possible eliminate the paths from the code and use /include=([-],[],[.foo]) and the like to tell the compiler where to find them. (These are the equivalent of using on Unix: -I.. -I. -Ifoo)
Desperate measures fix: For an include like </foo/woo/moo/blah.h> you can define a concealed logical "foo" which points to the correct place in the directory structure on your system and Compaq C will be able to look in the [.woo.moo] subdirectory of it and find the file blah.h.
(OO) We made a oovms.com file containing all discovered 'concealed logicals'
20. I/0 LEF the compiler includes "foo.h" but not "Foo.h" OpenVMS is case insensitive. Even ODS5, which will preserve case, will not distinguish between these two files. Usually this shows up long before the compiler gets a shot at it when the archive is unpacked and complains/warns that it is overwriting FOO.H.
Fix: rewrite the code to use case invariant file names.
(OO) We'll try to produce clean code, but won't modify general code just for cleanlyness.
21. I/0 LEF how do I find a memory access problem? Often code which ran "correctly" on one platform will fail miserably on another if it access memory out of bounds. Such "working" code will usually fail when you port it to OpenVMS just because variables will be located in memory differently.
One method of dealing with this is to do:
#ifdef __VMS
#define free myfree
int decc$free(void *ptr); /* prototype for decc$free */

   void myfree( void *ptr) {

#include <lib$routines.h>
#include <ssdef.h>

   if(decc$free(ptr) != 0){
      (void) printf("illegal free() operation\n");
      (void) lib$signal(SS$_ACCVIO);
#endif /* __VMS */
decc$free will complain if you try to free a region of memory, whereas free() cannot warn you about the mayhem which is being committed. Many memory errors begin or end with an invalid free() and this will catch them.
Here is a method suggested by Hoff (Stephen) Hoffman and posted in comp.os.vms
Because of the likelyhood of application programming errors -- trampling past the end of the allocated storage being most common -- I almost never call malloc and free directly. Further, by intercepting the memory allocation calls, I can call lib$get_vm and similar, particularly using VM zones, and can tailor the particular behaviour most appropriate. With a "temporary mempory pool" VM zone, I can also flush all allocations in that zone in a single call, so that I can easily reset the pool... Also available with VM zones are the tools to traverse and report on the VM zones.
Jacket your calls to malloc and free. Code the malloc jacket to add a quadword at the front of the allocated area and a quadword at the back and then call the actual malloc asking for the requested size plus sixteen bytes. Before returning the allocated storage -- the base address of the allocated block plus eight -- fill the front and back quadword with a known (and no zero bytes in the quadword) pattern, possibly based on the address and size, etc. Code the free jacket to check for the patterns and scream if it finds errors, and to call free if not -- remembering to back up the base of the buffer by eight bytes from what the caller passed in. (I typically refer to these quadwords as "fenceposts" -- straying from the expected behaviour is quite easy to track down.)
If you were on more recent OpenVMS Alpha version, you could use the heap analyzer in the OpenVMS Debugger to poke around.
22. E/24 CEF fork() OpenVMS vfork() doesn't work like fork(). Search your code for fork() and if you find it, refer to gnuplot, or some other already ported application, for an example of how to replace the code.
(OO) We know we have to have DMAKE. This MAKE version is the achilles-heel of this port. The whole project depends on the availability of DMAKE. We have a alpha version (non-distributed version) working. The distibuted version depends on the fork() function. Continued improvements in the CRTL and the porting library will make DMAKE a more stable and reliable product.
23. I/0 LEF where do I look for help? Post to the newsgroup comp.os.vms
Use dejanews power search at
to search that newsgroup. (Be aware that the default setting only goes back about a year, use the date fields on the bottom to look back further.)
"Ask the wizard" at
Get a copy of the freeware CD or other already ported programs and scan them for solutions to the current problem. The freeware CD is on the net at:
24. I/0 LEF system() system() passes a command line to a subprocess, executes it, and then returns. Since the command line is by definition OS specific, any instances of system() in ported code must inevitably be rewritten. Ideally they should be removed entirely - find some way to do perform the desired action in code rather than by running a subprocess.
25(JEM). I/0 LEF getenv() behavior The behavior of getenv() depends on the version and ECO of the DEC C RTL and possibly logical names defined on your system.
Fix: use sys$trnlnm() instead.
(DRM comment: I've also observed instances where symbols and logicals interfered with the expected behavior. This happened primarily when the symbol/logical involved was a "standard" symbol that getenv handled differently. For instance, if "term" was previously defined in DCL than getenv("term") would return the user defined value, which often was not at all the desired quantity, and the program would fail.)
26(JEM). I/0 LEF mmap() on text files mmap() will open files in a binary mode. That is fine so long as the file is stream-lf or binary. However, if it is some other type of RMS file the program doing this will likely fail when it encounters the embedded RMS information.
Fix (DRM): require input files to be either stream-lf or binary, or if that is not possible, do not use mmap(). Either way will likely require extensive recoding.
27(JEM). I/0 LEF /tmp and others map to locations, overridden by logical names Apparently some versions of the C RTL map /tmp to SYS$SCRATCH /dev/null to NLA0:, and perhaps some others are translated as well. In the case of /tmp if you have defined a logical TMP and try to write to /tmp/foo.txt it will go to the directory set by the logical. This is consistent with the usage of logicals for paths which are not "special". Just be aware that the logical TMP or DEV may have been defined with some other meaning, so that /dev/null might end up creating a real file NULL in the directory pointed to by the logical DEV!
28(JEM). I/0 LEF setuid() The setuid() function is a stub and just returns 0 to indicate "success". That may not be optimal, since it really failed (the UID did not change.) User written setuid() is possible, but when the transfer is to an account which does not have read access to the calling processes job table, then SYS$SCRATCH and SYS$LOGIN will not be available.
29(JEM). I/0 LEF uid_t/gid_t size inconsistencies getuid() returns 16 bits (the member part of a UIC) with some compiler flags or on VMS 6.x and under, and 32 bits (the whole UIC) otherwise. stat() returns a 32 bit uid and a 16 bit gid. setuid() (user written) will need a 32 bit uid value.
30. I/0 LEF Files produced by the C RTL have invalid RMS attributes This isn't a portability problem per se, but rather a general C RTL problem which will affects all ported programs. The problem is that sequential text files written by the C RTL default to having a Longest Record value of 32767, and this can cause havoc with some other programs which foolishly believe that the value is accurate. For instance, SORT will allocate 32767 bytes/record for any such file, so that it cannot easily sort even a relatively small file. ( The work around in this instance is to either use SORT/PROCESS=tag or to figure out how big LRL is and repair the file with SET FILE/ATTRIB=(lrl:whatever) before sorting it.)
Partial fix: (This is really a sad excuse for a solution but it's all there is)
$ define/user decc$default_lrl 100
will set the LRL value to 100 instead of 32767. However, the 100 can be just as wrong as the 32767 - write a record of length 16k into that file and the RMS value will stay at 100. The C RTL should really just keep track of the records it writes and put the REAL value.
Section 1.6 of the Compaq C Run-Time Library Reference Manual is the only place this logical is mentioned:
In OpenVMS Version 7.0 the default LRL value on
stream files was changed from 0 to 32767. This
change caused significant performance degradation
on certain file operations such as sort.

This is no longer a problem. The Compaq C Run-Time Library now lets you define the logi- cal DECC$DEFAULT_LRL to change the default record-length value on stream files.
The Compaq C Run-Time Library first looks for this logical. If it is found and it translates to a numeric value between 0 and 32767, that value is used for the default LRL.
I disagree emphatically that "this is no longer a problem". The value of LRL set by DECC$DEFAULT_LRL is every bit as wrong as the default LRL. Its only advantage is that it can be "small and wrong" rather than "large and wrong."
31. I/0 LEF Where do I obtain tools and libraries for porting? bash,gmake, and many others are part of GNV http://gnv.sourceforge.net/
qt 2.1 http://www.saalhausen.de/lehrig/qt21_bck.zip
xforms 0.88 (warning, bugs in some versions of the C RTL and some graphics drivers are triggered by this software! Includes a port of XPM in shareable format) http://world.std.com/~xforms/
parallel programming: pvm, tcgmsg (maybe MPICH too, but it wasn't there as this was written) ftp://v36.chemie.uni-konstanz.de/
OpenVMS Porting Library from Compaq (released 22-JUN-2000) http://www.openvms.digital.com/openvms/products/ips/porting.html
Many libraries may be found in already completed ports see:
Use advanced search from http://www.dejanews.com to look for posts in comp.os.vms concerning the package of interest.

Maintained by the OpenOffice on OpenVMS porting Project.