Converting MS-DOS Programs to Run on UNIX or Other Non-DOS Systems

Gary Schmidt - UNIX Guru - Powerflex Corporation

A version of this article was originally published in POWERlines Volume 6, No. 2.

First Things You Need to Know

The first thing you need to know is that most of your code is portable to Unix without any change. In most applications, more than 95 per cent of the code does not know or care what operating system it is running on, and works without change. The areas that need to be examined deal with file input and output, where support for printers and file names and paths may have been hard coded.

It is also important to know that Powerflex has a lot of built-in support to make it easier to write code that will run on the various operating systems that it supports, without having to produce different PTC files for each target system.

First Things to Do

The first thing to do when writing portable code is to switch off the Caps Lock key. This is because we do not want to have to deal with too many problems caused by assuming that the target system is case-insensitive. Unix is case-sensitive for everything, not only the file system, so coding in a sensible case is a good habit to get into anyway. Under Unix, the file names "Fred" and "FRED" refer to two totally different files.

By contrast, Windows 95 is case-insensitive, but case-preserving; this means that although the file names "Fred" and "FRED" are considered to be identical, the name is stored as just you typed it.

What Should I Avoid Doing?

Obvious things to avoid are those that are specific to MS-DOS (or Windows, or anything), such as direct writing to printer ports, or knowing that something exists — disk drive c: for example. Also avoid those Powerflex commands that are not supported on UNIX. For a list of Powerflex commands that are not supported under Unix, see Listing 1 below.

Support for Portable Programs

Powerflex supplies four levels of support for producing portable programs.

  1. At compile time, through the use of the $$UNIX$$ compiler symbol.

  2. At compile time using a command line macro, /DUNIX and #IFDEF UNIX inside your code.

  3. At run time, through GET_ENV("OperatingSystem").

  4. At run time, through the use of the configuration item iFileCaseMode to force file names and paths to lower case.

Using the $$UNIX$$ compiler symbol to control the code which is compiled is useful if you only have a few source files which require different behaviour under Unix, and are willing to ensure that the correct files are distributed for each target operating system.

To use the $$UNIX$$ symbol you will need to do parallel development under a Unix system, which is necessary if you are targeting a RISC-based system, but not if you are interested in an Intel-based system.

The #IFDEF UNIX symbol is another easy way to conditionally compile code for different targets, with the same provisos as using $$UNIX$$.

GET_ENV("OperatingSystem") is best used by setting an indicator at the start of the program to tell which operating system you are running under. Some example code for doing this is in Listing 2. Using an indicator to control which code path is taken allows you to generate completely portable PTC files, where it is necessary to have non-portable code (for example, when accessing a printer).

The iFileCaseMode configuration item is used to deal with any filenames that you may have hard-coded in upper case, particularly where the parameter to a CHAIN WAIT is in upper case. See Listing 3 for the behaviour of iFileCaseMode.

What Will I Probably Have to Change?

A major area that you may want to change is that for handling the printing of files. If you use DIRECT_OUTPUT "LPT1:" you will need to change the code, as under Unix this will create the file LPT1: somewhere on your data path, and write to it. If you have used the command DIRECT_OUTPUT "LST:" then you have an easier time of it, you simply set the configuration items sPrintPath=SPOOL: and sSpoolCommand=lp -s and output will go to the default system printer.

If you have written any code that recognises keystrokes directly (for example, you look for the sequence Ctrl+Alt+Shift+F12, and fire off a special tool), you will either have to change the keystroke, or live without the tool.

What Will I Definitely Have to Change?

I have seen only one fragment of code (which is not operating-system dependent) that absolutely has to be changed when porting to Unix. It is the code fragment in Listing 4, and shows a simple way to absorb all the CPU power available on any multi-user or multi-processing system. Please don't do it! (or anything vaguely like it.)

Listings

Listing 1:
Powerflex Commands Not Supported under Unix

CALL_BINARY

INP

PEEKW

INTDOS

LOAD_BINARY

OUTP

POKEB

INT86X

UNLOAD_BINARY

PEEKB

POKEW

Listing 2:
Using GET_ENV ("OperatingSystem")


string os_type
string path_char 1
indicator is_msdos is_msdosrm is_msdos286 is_msdos386 ;
          is_os2 is_os2pm ;
          is_unix ;
          is_windows is_win16 is_win32 is_winnt ;
          is_cmode is_gmode
number    os_vers
integer   os_pos

   // get the path separating character '/' or '\' for 
   // assembling paths
   path_char = get_env("cPathChar")

   // the following code expects a string formatted as:
   // OSTYPE/OSCLASS major.minor
   // eg: MSDOS/32 6.2
   //     WINDOWS/32 3.1
   // note: Unix strings are the *odd* ones out
   os_type    = get_env("OperatingSystem")
   is_msdos   = "MSDOS" in os_type
   is_msdosrm = is_msdos and "RM" in os_type
   is_msdos286 = is_msdos and "16" in os_type
   is_msdos386 = is_msdos and "32" in os_type

   is_os2     = "OS/2" in os_type
   is_unix    = "UNIX" in os_type
   is_windows = "WINDOWS" in os_type

   os_pos = pos(" ",os_type)
   os_vers = mid(os_type,99,os_pos)
   if is_windows begin
      is_winnt = "WINDOWS/NT" in os_type
      is_win32 = ("WINDOWS/32" in os_type) or is_winnt
      if is_win32 begin
         os_type = "WINDOWS32"
      end
      else begin
         is_win16 = true
         os_type = "WINDOWS16"
      end
   end

   if is_os2 begin
      is_os2pm = "PM" in os_type
   end

   if is_msdos os_type = "MSDOS"
   if is_os2   os_type = "OS/2"
   if is_unix  os_type = "UNIX"

   is_cmode = "C" in get_env("UiMode")
   is_gmode = "G" in get_env("UiMode")


Listing 3:
Using iFileCaseMode

iFileCaseMode can take 3 values:

0 The default. Leave the case of all filenames and paths alone.
1 Change all filenames and paths to lower case just before they are physically accessed in the commands: OPEN, COPYFILE, ERASEFILE, RENAMEFILE, CHAIN, and when using the FILE: driver for DIRECT_INPUT, DIRECT_OUTPUT, and APPEND_OUTPUT.
2 As for Case 1, but also change filenames and paths in the DIRECTORY command, and when using the DIR: device for DIRECT_INPUT.

Listing 4:
How to use up spare CPU cycles
(or how to bring a Cray to its knees)


// The code is deceptively simple, it is called 
// a busy-wait loop, and is not a problem on a 
// single-user MS-DOS machine, but is very nasty 
// on a UNIX or other multi-user system.
   doit = TRUE
   WHILE doit
      // ... process stuff ... (or do nothing at all)
      INKEY$		// or KEYCHECK
   LOOP

KEYPROC KEY.ESCAPE
   doit = FALSE
   RETURN
// The process never pauses, and no other process 
// can run until it uses up its time slice, or the 
// scheduler recognises that it has a do-nothing 
// process, and drops it down the list.


Back to FAQ Page