Porting DOS Applications to Linux

Trying to port a DOS application to Linux? Alan Cox gives you hints and practical help.
Multitasking Politely

In general, the kernel automatically blocks programs, thus avoids having them use CPU time when they are waiting for I/O. A device can be opened with the extra option O_NDELAY to indicate that an error EWOULDBLOCK should be returned to indicate the lack of ready data (or for writing lack of buffer space). When a program is using I/O in this manner, it must take great care not to sit in a tight loop.

Avoid the following DOS-style constructs:


Linux instead provides the very useful select() system call, which allows you to wait for multiple I/O events, a timeout, or both, in a manner that avoids polling and enables the kernel to avoid allocating processor resources to the task in question.

select() allows you to wait for a given time, or wait until one of a set of files has data ready to read or space to write, or wait until an exceptional condition occurs on that file. As the Linux system sees everything within reason as a file, this is extremely flexible.

Listing 1 shows is a “trivial” example. This is an implementation of kbhit(). For exact DOS behaviour, it assumes the terminal is already in raw mode, which we will discuss later. Otherwise, it will return 1 after ENTER is pressed, which is when data becomes available in the line-by-line cooked mode.

Sharp-eyed DOS programmers might wonder, “What happens with this kbhit() if we redirect the input of the program from a file? It's not a keyboard, but input is available.” The answer is simple enough—a disk file is always ready for reading, and at this level, there is no difference between reading a keyboard and reading a file. The program carries on and works fine. Indeed, you could redirect a program to run reading input from the mouse and select() would still behave consistently.

Files and Devices

File I/O under Linux is somewhat simpler than DOS. DOS emulates the Unix low level (open(), close(), read(), and write()) and high level “stdio” facilities, but DOS C libraries have their own ascii/binary awareness to handle the carriage return/line feed differences. Under Linux these are gone and there is no need to worry about specifying these (although the ascii/binary specification will be accepted). All of the DOS device names are different under Linux. Linux systems keep their devices in /dev. Here is a rough conversion chart:

    CON:         /dev/tty
    LPT1:        /dev/lp0
    LPT2:        /dev/lp1
    LPT3:        /dev/lp2
    COM1:        /dev/ttyS0 /dev/cua0
    COM2:        /dev/ttyS1 /dev/cua1
    COM3:        /dev/ttyS2 /dev/cua2
    COM4:        /dev/ttyS3 /dev/cua3
    NUL:         /dev/null

Note that it is normal to print by queueing jobs via the printing service (lpr) rather than writing directly to ports. On typical Linux systems the /dev/lp* files are protected so that a normal user cannot access them directly.

Terminal Input

Terminal I/O is distinctly different in Linux than it is under DOS. First, the POSIX terminal system is more modal than DOS. To switch from one-character-at-a-time mode (getch() in DOS) to a line-based editing mode requires an actual termios request, which gives the new terminal parameters to use. In addition, a program is responsible for restoring the terminal state before it runs other programs and when it exits. If you forget to do this, you may well need to switch screens and kill the process, or you may find that the shell gets confused by your terminal state and logs you out (which also fixes the problem).

Listing 2 includes some sample code for managing terminal I/O settings.

Terminal Output

On output, Unix programs traditionally avoid using direct cursor control codes and cannot write directly to video memory. The reasons for this are obvious. The terminal in question may be a different type of machine, in a different part of the world. Handling all the different terminal types by hand is unpleasant, so a library called curses is available. A more modern library called ncurses, which has such things as colour support, is also available for Linux. Older versions of this have had many bugs, but the latest appears very good indeed. See article “ncurses: Portable Screen Handling for Linux”, Linux Journal issue 17, September, 1995, for an introduction.

ncurses provides you with simple output control, colour (if the terminal supports it), function keys, and other manipulations in a terminal-independent manner. In addition, it optimises the updates it does to minimize traffic over slow networks or serial links. It is free and comes with a nice set of examples and good documentation. As it is an implementation of System V curses, you can pick up a book on curses from a library and use that as a reference or tutorial (as appropriate).

Should you decide to use ncurses to do your output, it will also provide all the routines necessary to do DOS style character-by-character input via the functions cbreak(), nocbreak(), echo(), and noecho(). The ncurses documentation explains all four.



Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Error in listing 2

Graeme's picture

The second call to signal in term_ctrlz should be a call to kill, i.e. kill( getpid(), SIGSTOP );