Wednesday, April 29, 2009

Printing from DOS in VirtualBox

VirtualBox doesn't support LPT ports. DOS has limited ability to print to USB printers, and the program I am using definitely doesn't support it. Thus I need to find an automatic, or at least semi-automatic, user friendly way to print from DOS.

----------------------------------- The Mount Method -------------------------------------

My first approach was to print to a file in DOS, then simply mount the .vdi and read/print the file.

This has three drawbacks:

1. You need sudo to mount the .vdi.
2. You can't use it with snapshots or non fixed sized disks.
3. I don't like mounting a disk that is already in use.

The first problem can be solved by adding an entry in the fstab or using FUSE. The second and third problems can be solved by printing to a floppy image and then mounting that instead.

So the basic process is go to Devices and mount a floppy image, print to the floppy using filename "print", unmount the floppy, then run this script in Linux:

#we need recode (sudo apt-get install recode)
#add the floppy image to fstab to avoid sudo.
#mount writable and erase the print file so DOS doesn't prompt to overwrite?

mkdir -p ~/dosprint/printtemp
sudo mount -o loop,ro ~/dosprint/flopppy.img ~/dosprint/printtemp/
cp ~/printtemp/print ~/dosprint/printing
recode IBM437/CR-LF ~/dosprint/printing
lpr ~/dosprint/printing
umount ~/dosprint/printtemp
rmdir ~/dosprint/printtemp
rm ~/dosprint/printing

Notice that I am calling recode to convert the old DOS formatting. Obviously you will have to change floppy.img to match the floppy image you printed to.

This isn't a terrible option, but it does make the user have to remember the exact filename to print to, or else the script won't work. The whole mount/unmount process is a bit tedious too.

----------------------------------- The Serial Port Method -------------------------------------

To fix this I started looking at VirtualBox's COM emulation. It can connect the VM's COM port to a socket on the filesystem (it calls it a pipe, but it is really a socket... I probably would have preferred a real pipe). Anyway, after fiddling with it a bit I came up with a, hopefully, better solution.

After enabling COM1 on the VM to output to a "pipe" start up the VM. Then run socat -u UNIX-CONNECT:$HOME/dosprint/printpipe GOPEN:$HOME/dosprint/printfile in a terminal. You may have to do a 'sudo apt-get install socat' if it isn't already installed. The DOS VM now can print to COM1. After printing in DOS simply run this script:


#check to make sure the file exists and is not empty.
if [ ! -f $printfile ] || [ "`stat -c %s $printfile`" -le "1" ]; then
zenity --error --text "The Print File is Empty or Does Not Exist\!"
echo "" > $printfile
exit 1

lpr $printfile

#check to make sure it printed
if [ "$?" -ne "0" ]; then
zenity --error --text "Error Printing!"
echo "" > $printfile
exit 1

echo "" > $printfile
zenity --info --text "Printing\!"

There are probably some better ways to use socat here, but this worked the best so far. I would prefer to use a pipe, but it didn't work as expected. Also notice I didn't have to use recode, which was nice; this however is dependent on the printer driver DOS is using. In one incarnation I had to use dos2unix; unix2dos; then recode to get it to print properly. (In Ubuntu dos2unix is in the package 'tofrodos'.)

The biggest drawback here is that socat has to be run every time the VM is started, and you can't even run it as a startup script because it returns immediately unless the VM is running. At least it gets rid of the mounting and naming issues.

Here is a simple script to start the VM and run socat automatically:

VirtualBox --startvm DOS&
echo "" > ~/dosprint/printfile
sleep 5
socat -u UNIX-CONNECT:$HOME/dosprint/printpipe GOPEN:$HOME/dosprint/printfile

You can create a launcher (right click desktop or nautilus window) if you want to be able to open this script without being prompted to run in terminal or display.

P.S. Zenity is very nice :).

Thanks to Maciej BliziƄski for some nice info on VirtualBox's serial port pipes.

Update: I ended up using iconv rather than recode, since recode had some problems with some special characters. Careful with iconv, it changed between the version on Ubuntu 8.04 and 8.10 (mainly the overwrite in place functionality).

1 comment:

Unknown said...

Thanks this worked well. It is a real PIA to deal with old DOS apps. You wrote this procedure and another M$ box bites the dust for man-kind!