Several years ago Frank Lees contributed a program useful for capturing characters sent to a Unix system over an asynchronous serial line. I decided to update the program to add POSIX support and a time-out feature designed with help from Ray Swartz. With this program, you can upload files to a Unix system using error-correcting modems and any terminal emulation program that can read or transmit files.
Dear Editor:
I use the upload C program [see Part A of the Listing] on my Unix system to capture files sent from PCs, intelligent terminals, or data-recording devices. It reads standard input and writes to a file, terminating when there is no input for a specified time, or 10 seconds by default. The Unix system asynchronous serial device driver is placed in ``raw'' mode so all characters can be collected without change. By default, Control-M and Control-Z characters are not stored in the output file but can be stored if so desired.
To upload a file using a communication program from another system, first invoke upload on the destination machine so it will begin storing characters that it reads from the serial port that is used for communication with the source system. Next request that the source system send the contents of the desired file through the communication port to the destination system. The upload program will ``time out'' after all the data has been transferred, finally closing the output file and restoring the original serial driver modes before exiting.
You must specify an argument on the upload
-command
line that names the output file. If this file exists,
upload
displays an error message and terminates.
This feature prevents accidental overwriting of existing
data.
Several options can be used to modify the default actions.
Specify -b
(for binary) to store all eight bits of
every character received; otherwise, the most significant bit is
stripped. Use -m
to store Control-M's and
-z
to store Control-Z's if you're not using binary mode.
(DOS-based systems terminate lines with Control-M's and end files
with Control-Z's.)
Normally, if no characters are received for 10 seconds,
upload
will close the output file and terminate.
You can change this time-out period using the -q
(for quit-time) option. Values will be rounded up to the next
multiple of 10 (11 through 20 become 20, 21 through 30 become 30,
and so forth). Also, values less than 10 seconds aren't allowed
because the basic timing loop requires at least 10 seconds to
complete.
This program performs no error detection or correction.
You'll have to rely on underlying protocols to provide error-free
transmission. I developed upload
on a Tektronix
workstation running a BSD 4.2-like operating system.
Usage Notes. Ensure that your Unix system input
buffer doesn't overflow. The upload
program sets
the device driver on the destination system so it won't recognize
XON (Control-Q) and XOFF (Control-S) characters, but the driver
can still send XON and XOFF characters to the source system. The
source system or communication program should recognize these
characters and suspend transmission when requested by the
destination machine to prevent loss of data.
You'll want to use binary mode to store all eight bits of every
character received, which is necessary for compiled programs or 8-bit
data. If the sending system includes parity bits when they
send 7-bit data, they will be stored as the most significant bit
in the output file. If you want to upload all characters of a
text file, including Control-M's and Control-Z's, specify the
-m
and -z
options with text mode (not
binary mode) so any parity bits will be stripped.
Some of you may wonder: Why bother with upload
when cat -u > output-file
could do the
job? Simply put, upload
provides more options with
only one command invocation. Otherwise, you'll need to use stty
to set and then
reset the device-driver modes and provide some sort of time-out
facility.
Implementation Notes. As usual the program starts
with constant definitions and variable declarations. However,
provision has been made for portability between two major ways to
control serial drivers--the POSIX ``termios'' structure and the
System V ``termio'' structure-by using ``pound define''
constructs to select between two groups of definitions,
declarations, and header-file ``include'' directives. Lines 2-5
will be compiled if TERMIO
(for ``termio''
structure) is defined on the command line [see Part B of the Listing] or in a Makefile.
Otherwise, we assume a POSIX environment, where lines 7-13 are
employed. Note that many Unix versions besides System V support
the ``termio'' structure, such as recent BSD and SunOS
releases.
Line 7 defines _POSIX_SOURCE
so any header
files that are included subsequently will be interpreted with
this constant in effect. This definition ensures that vendor
extensions won't be used because only those symbols defined by
the base standard or enabled by a specific #define feature test
will be employed.
Line 79 rounds up the user-specified quit-time value to the
next highest multiple of 10. On line 98 we open the output file
so that if the file already exists, an error is returned. This
approach prevents overwriting of data on the destination system
disk. Part C shows one way to change
the open()
call so an existing file doesn't cause an
error condition.
POSIX provides a portable approach to get and set the modes of
an asynchronous serial driver. The implementation-specific
ioctl()
call has been replaced by
serial-driver-specific subroutines because (1) you can't declare
ioctl()
with a standard C function prototype because
the third argument varies in size and type depending on the
second argument, (2) the calling semantics are different on
different systems, and (3) international environments aren't
supported adequately.
Serial-driver parameters are manipulated through a data
structure. POSIX uses a structure named ``termios'' [Part D of the Listing], which is
virtually identical to its predecessor, ``termio'' [Part E]. The primary difference is that
``termios'' doesn't include the ``termio'' line-discipline
member, c_line
, which wasn't used anyway. Also, many
data types were made generic for portability. For instance,
tcflag_t
is specified as the data type for all the
flag members instead of the more common unsigned short or
unsigned long integer data types, and cc_t
is used
instead of an unsigned character.
Two nested loops do the work. The outer ``while''loop (Part A, lines 141-154) determines how long the program idles after the last character has been received. Each iteration of that loop decrements a loop count- which was originally set by line 140-to one-tenth the quit-time value. When this loop exits, the output file is closed, the serial-line I/O modes reset to their original values, and the program terminates after an end-of-transfer message is displayed for the operator.
Each iteration of the inner loop [lines 142-153] reads a
character and then tests whether it's to be written to the output
file. Lines 146-147 perform several tests separated by the
logical OR (||
) operator. If any of these tests is
true, the overall logical expression of the ``if'' statement is
true, so the character will be saved. Otherwise, the character
won't appear in the output file.
If no input character is available, the ``read'' call [line
142] won't be satisfied (will return control to the calling
program) until the basic time-out period (10 seconds) has
expired. This circumstance arises by using ``noncanonical mode''
(by disabling the ICANON
structure flag). The time
period is determined by the VTIME
element of the
c_cc[]
serial-driver structure member. Multiply
this value by 10 to get the period in seconds. Line 127 sets
this value to 100 for a 10-second period. The Table lists all the ``termio'' or
``termios'' structure flags manipulated by this program.
Every time a character is read, the outer loop-count value is reset by line 152. This way, after the last character is read, the total quit time will have the correct value. This operation doesn't seem to slow down the loop enough to effect data transmission, even at 9,600 bits per second.
We're looking for readers who have the inclination, time, and resources to help us test ``Wizard's Grabbag'' contributions. This way we can provide a more portable program for our readers. In particular, interested readers who have access to one or more of the following Unix implementations: SCO Xenix 2.3.2, SCO Unix 3.2.x, DECstation Ultrix 4.1, or HP 700 HP-UX 8.07 should contact me via e-mail.