USER'S GUIDE TO THE SUNGRAPH FORMAT DISK I/O ROUTINES JULY 1988 TABLE OF CONTENTS 1.0 INTRODUCTION 2.0 GENERAL INFORMATION 2.1 Supported Data Types 2.2 The Concept of Data Blocks 2.3 Files Associated with Disk I/O 2.4 Calling the Disk I/O Routines from C 2.5 Calling the Disk I/O Routines from FORTRAN 2.6 Error Reporting 3.0 USING THE DISK WRITE ROUTINES 3.1 Description of the Disk Write Routines 3.1.1 Open File Routine 3.1.2 Define Variable Routine 3.1.3 Save Variable Routine 3.1.4 End Block Routine 3.1.5 Close File Routine 3.1.6 Disk Write Error Routines 4.0 USING THE DISK READ ROUTINES 4.1 Description of the Disk Read Routines 4.1.1 Open Variable Channel Routine 4.1.2 Open Block Channel Routine 4.1.3 Read Format Routine 4.1.4 File Length Routine 4.1.5 Goto Sample Routine 4.1.6 Read Variable Routine 4.1.7 Goto Block Routine 4.1.8 Read Block Routine 4.1.9 Close Channel Routine 4.1.10 Disk Read Error Routines 1.0 INTRODUCTION A set of routines to transfer data between programs and disk was developed as part of the SUNGRAPH project. The primary use of these routines is to transfer data from a program to SUNGRAPH. In this case, the user's program uses only the disk write routines. Another use for these routines is to pass data between user's programs in situations where disk storage is needed, but display with SUNGRAPH is not necessary. In this case, the user will need to use the disk read routines as well. This document describes each disk I/O routine and how to use them. It does not describe the actual format of the disk files which the routines use. 2.0 GENERAL INFORMATION The disk I/O routines are written in C and are available on the Alliant and the Suns. Calling them from C programs is just like calling any C function. Porting the routines to other machines should be straightforward. A FORTRAN interface to the I/O routines is provided for the Alliant and Sun machines. These interfaces are machine dependent and therefore don't port easily to other machines. 2.1 Supported Data Types The disk I/O routines enable users to write and read variables of several data types. The current version supports two and four byte integers and four byte floating point variables. Other data types can be added quite easily as the need arises. 2.2 The Concept of Data Blocks A data file can contain the values of one or more variables. The files and the routines which read and write them are organized around the concept of data blocks. A data block is the elementary unit of a data file and consists of one or more values of each variable stored in the file. When a new file is created, the user decides how many variables the file will have and how many values of each variable will be saved per data block. A data file consists of an arbitrary number of concatenated data blocks. 2.3 Files Associated With Disk I/O Data files created by the disk write routines have the extension .sg_data. These files consist entirely of data saved by the user; there is NO header or record information. Associated with each data file is a format file having the extension .sg_format. The format file (which has the function of a small header) has information about the data file. The format file contains the following: a) a number indicating data file type b) the number of variables in the data file c) for each variable in the file: 1) data type 2) number of values per block 3) name The description for the read_format routine contains additional information about the format file. Any file written with the disk write routines can be read with the disk read routines. One other type of file can be read. This data file must have the extension .spd (SPeech Data). These files usually originate by A/D conversion of a signal of interest, such as speech. To be an spd file, the following must hold: a) the data consists entirely of 16 bit integers stored in the binary format of the Alliant or Sun (or machine with equivalent representation). In FORTRAN, this is an integer*2. In C, it is a short int. b) the sample values are from a single data source. i.e. the file has values of a single variable; it is not multiplexed. 2.4 Calling the Disk I/O Routines from C All object code that you need to link to is contained in one archive file. "disk_io.h" is a header file which contains definitions of some parameters in the read and write routines. You may want to use some of these definitions in your program, but this is not necessary. The files are located as follows: diskio/disk_io.h diskio/disk_io.a 2.5 Calling the Disk I/O Routines from FORTRAN Most of the disk I/O routines can be called from FORTRAN; exceptions are noted in the routine descriptions. When calling these routines from Alliant FORTRAN, string arguments passed to the I/O routines are required to be terminated with a null. Put the null right after the last valid character. All object code that you need to link to is contained in one archive file. The location of the object files is as follows: diskio/disk_io.a The "released" directory also contains source code for all object files in the archive file. 2.6 Error Reporting The disk I/O routines check for all but the most unlikely error conditions. There are two error reporting methods which the user can select from. The default method is that when an error is detected, an error message is printed. Program execution is then halted. The default method is called auto error reporting. The other error reporting method is for the disk I/O routine to return a negative number. (Disk I/O routines return zero or a positive number when successful.) The user checks the return value and takes appropriate action. All return values are type int (integer*4 in FORTRAN). A routine to turn an error number into an error message is provided. The following routine allows the user to change the error handling method. disk_io_erh(flag) flag is an int (integer*4 in FORTRAN) When 'flag' is zero, auto error reporting is disabled When 'flag' is not zero, auto error reporting is enabled. The error handling method can be changed as often as you want, thus allowing some error conditions to be handled automatically and others to be handled by the user. 3.0 USING THE DISK WRITE ROUTINES There are five routines in the disk write collection: 1) open_file - opens a file for writing; 2) def_variable - defines attributes of a variable to be saved; 3) save_variable - saves the values of a variable; 4) end_block - declares that all values of the variables have been saved for this block; 5) close_file - ends the use of a file For each file that you want to save variables in, you use a process similar to the following. First, open the file with open_file. Then, for each variable, declare its name, data type, number of values saved per block, and default value with the def_variable routine. These two routines set up the file so that data can be saved and don't get called again for this file. Next, save the values of variables with save_variable. When all values of a block of file variables have been saved, call end_block. Repeat the process of saving variables and ending a block for as much data as you want to save. After saving the last block, close the file with close_file. If you use the auto error reporting method, a disk I/O error halts execution and causes an error message to be printed. If you don't use this method, a disk I/O routine indicates an error via a negative return value. This condition should be tested after using any of these routines and appropriate action taken if an error occurs. The disk write error routines are provided to turn the return value into an error message. 3.1 Description of the Disk Write Routines 3.1.1 Open File Routine: file_id = open_file ( filename, open_type ) argument | C type | FORTRAN type ------------+----------+---------------- filename | char * | character*80 (or shorter) open_type | int | integer*4 This routine opens the file "filename" so that data may be written to it. The parameter "open_type" specifies the following: 1 - Open the file unconditionally. Discard any prior data in the file. 2 - Append to an existing file. 3 - Open a new file. If the file already exists, don't open it. (An error message will indicate that the file already exists.) The data will be written to a file named "filename.sg_data" and information about this file's format will be written to "filename.sg_format". Since the variable definition process determines part of the file format information, the format file is not written until this process terminates (with the first call to end_block, described below). When appending to an existing file, the current format must match that of the appended file. This is checked in the first call to end_block. Returned value: If there are no errors, the returned value is a file id which is used to identify this open file when using several other of the disk write routines. A valid file id is >= 0. If an error occurs, the returned value is < 0. The following errors are detected: - file "filename" is already open - filename is too long - the number of files currently open is the maximum - could not create a format file - could not create a data file - could not open filename.sg_format for reading - could not open filename.sg_data for writing - file "filename" exists and open_type prohibits overwriting - invalid open_type 3.1.2 Define Variable Routine: var_id = def_variable ( file_id, name, type, number, def_val ) argument | C type | FORTRAN type ------------+----------+---------------- file_id | int | integer*4 name | char * | character*16 (or shorter) type | int | integer*4 number | int | integer*4 def_val | "type" * | "type" (see description below) This routine defines parameters of a variable to be saved. The parameters are the variable's name, data type, number per block, and default value. Variables in a particular file may be defined until the first call to end_block for that file; i.e. calling end_block ends the variable definition process for a file. The string used as the variable's name does not have to be the name the calling program uses to reference the variable's values. It can be some other string of the programmer's choosing. This name is used to select the variable for reading by the disk read routines. It is also used to select and label variables in SUNGRAPH. This routine does NOT check for duplication of variable names, so be sure that each file variable has a unique name. The default value is written if the user has saved fewer than "number" values per block when end_block is called. In FORTRAN, the default value's type must be the saved variable's type. (e.g. If type=3, the default value must be a real*4.) In C, the default value is a POINTER to a value of the type specified in the argument list. (e.g. If type=3, the default value must be a pointer to a float.) The argument "type" refers to data types according to the following table: TYPE # | C TYPE | # of BYTES | FORTRAN TYPE -------------------------------------------------- 1 | short | 2 | integer*2 2 | int | 4 | integer*4 3 | float | 4 | real*4 Returned value: When no error occurs, the returned value is a variable id which is used to identify this variable when saving its values with the save variable routine. A valid variable id is >= 0. If an error occurs, the returned value is < 0. The following errors are detected: - 'file_id' is out of range - 'file_id' corresponds to an unopened file - the file variables have already been defined - invalid data type specified - invalid number of values specified - the maximum number of variables have already been defined - variable name is too long - could not get a buffer for saved values 3.1.3 Save Variable Routine: status = save_variable ( var_id, values, num ) argument | C type | FORTRAN type ------------+----------+---------------- var_id | int | integer*4 values | varies | varies | (array) | (array) num | int | integer*4 This routine saves values of a variable that has been defined. "values" is the array of which "num" values will be saved. The data type of the values array must match that declared when the variable was defined. The routine does NOT test for such a match. The number of values saved per call can be from 1 to the number per block specified when the variable was defined with def_variable. The total number of values saved per variable cannot exceed the number per block specified when the variable was defined. Note to C users: When saving a single value, be sure that "values" is a POINTER to that value. Returned value: The returned value indicates the error status of the routine. If no error occurs, the returned value is 0. If an error occurs, the value is < 0. The following errors are detected: - 'num' < 1 - 'var_id' is out of range - 'var_id' corresponds to an undefined variable - all values of this variable have been saved in the current block - saved less than the specified number of values because the current block became full 3.1.4 End Block Routine: status = end_block ( file_id ) argument | C type | FORTRAN type ------------+----------+---------------- file_id | int | integer*4 This routine terminates the saving of variables for a block. This is the routine which writes the block values to the data file. The first call to this routine terminates the variable definition process, i.e. def_variable can no longer be called for this file_id. When a file is open for appending, the first call to this routine checks to see that the variable definition of the appended file matches the current definition. An error is returned if not. Returned value: The returned value indicates the error status of the routine. If no error occurs, the returned value is 0. If an error occurs, the value is < 0. The following errors are detected: - 'file_id' is out of range - 'file_id' corresponds to an unopened file - current variable definition differs from that of appended file - error when writing format file - error when writing data buffer to disk - no variables have been defined 3.1.5 Close File Routine: status = close_file ( file_id ) argument | C type | FORTRAN type ------------+----------+---------------- file_id | int | integer*4 This routine closes the file associated with "file_id". In the event that more files need to be written than can be opened simultaneously, this routine provides a way to close a file so that another can be opened. Returned value: The returned value indicates the error status of the routine. If no error occurs, the returned value is 0. If an error occurs, the value is < 0. The following errors are detected: - 'file_id' is out of range - 'file_id' corresponds to an unopened file 3.1.6 Disk Write Error Routines: print_disk_write_error ( err_num ) (Callable from FORTRAN) error_string = disk_write_error ( err_num ) (No FORTRAN interface yet) argument | C type | FORTRAN type ------------+----------+---------------- err_num | int | integer*4 The disk write routines return a value which, when less than zero, corresponds to an error number. The disk write error routines provide a way to turn the error number into an appropriate error message. The disk_write_error routine returns brief error message corresponding to "err_num". The "print_disk_write_error" routine prints a more elaborate error message. These routines return "No error" for arguments zero or greater and "Bad value for error number" if "err_num" is not a value from one of the write routines. 4.0 USING THE DISK READ ROUTINES The disk read routines are structured to provide "channels" through which the file variables flow. There are two types of channels - variable channels and block channels. A variable channel provides access to just one of the variables in a file, independent of how many variables the file may contain. A block channel provides access to all of the variables in a file. The values of a variable are read on a variable channel via the read_variable routine. This routine can return any number of the variable's values in a single call. Values are read on a block channel via the read_block routine. This routine returns one block of values per call. The reading is done sequentially on each channel type. Two routines are provided to modify the point from which data is read - the goto_sample routine and the goto_block routine. There is also a routine to read the file format and one to close channels. The disk read routines consist of the following: 1) read_format - reads and returns information about the data file format. 2) open_var_channel - opens a channel to access one variable in a data file. 3) read_variable - reads and returns values of a variable. 4) goto_sample - re-positions the reading process so that read_variable will read from a different sample in the file. 5) open_block_channel - opens a channel to access all variables in a data file. 6) read_block - reads and returns the values of one block of data. 7) goto_block - re-positions the reading process so that read_variable and read_block will read from a different place in the file. 8) file_length - return the length of an open file. 9) close_channel - close an open channel. To set-up a variable channel do the following. First, call open_var_channel to open a channel and get a channel id. Then use read_variable to read values and if necessary, use goto_sample or goto_block to read non-sequentially. Call close_channel when you no longer want to read values of the variable on that channel. To use a block channel, do the following. First, call open_block_channel to open a channel and get a channel id. Then use read_block to read one block's worth of values. Use goto_block if you need to read other than sequentially. The read_format routine is provided to read format information in situations when the file format is not known at the time your program is being written. If you use the auto error reporting method, a disk I/O error halts execution and causes an error message to be printed. If you don't use this method, a disk I/O routine indicates an error via a negative return value. This condition should be tested after using any of these routines and appropriate action taken if an error occurs. The disk read error routines are provided to turn the return value into an error message. 4.1 Description of the Disk Read Routines 4.1.1 Open Variable Channel Routine: chan_id = open_var_channel ( filename, var_name ) argument | C type | FORTRAN type ------------+----------+---------------- filename | char * | character*80 (or shorter) var_name | char * | character*16 (or shorter) This routine is called to establish a new channel to access the values of one of the variables in a data file. "filename" is the name of the file and "var_name" is the name of the variable whose values are to be read. The variable's values are accessed with the read_variable routine. This function returns a channel id which is used to identify the opened channel when calling other disk read routines. "filename" can have an extension, but it is not mandatory. If "filename" has an extension: a) The extension is tested for validity. (e.g. Is it .sg_data or one of the other types that can be read by these routines?) Valid extensions are: .sg_data .spd b) If the extension is valid, open_var_channel tries to open "filename". If the extension is not valid, an error is returned. If "filename" does not have an extension: a) Each valid extension is concatenated in turn with a copy of filename and the resulting filename is tested for existence. The order is: .sg_data, .spd. b) If this file exists, open_var_channel tries to open it. If none of these files exist, an error is returned. If the file to be opened is a .sg_data file, the format information must reside in a corresponding .sg_format file. There is no format file associated with .spd files. Returned value: If there are no errors, the returned value is a channel id which is used to identify the open channel when using other disk read routines. A valid channel id is >= 0. If an error occurs, the returned value is < 0. The following errors are detected: - filename is too long - all channels are open - there is a format file problem (use read_format to determine the problem) - the specified variable was not found - invalid extension in filename - no data file found - the data file could not be opened 4.1.2 Open Block Channel Routine: chan_id = open_block_channel ( filename ) argument | C type | FORTRAN type ------------+----------+---------------- filename | char * | character*80 (or shorter) This routine is called to establish a new channel to access the values of all of the variables in a data file. The values of the data file variables are accessed with the read_block routine. This function returns a channel id which is used to identify the opened channel when calling other disk read routines. The same rules concerning filename extensions described in open_var_channel apply to open_block_channel. Returned value: If there are no errors, the returned value is a channel id which is used to identify the open channel when using other disk read routines. A valid channel id is >= 0. If an error occurs, the returned value is < 0. The following errors are detected: - filename is too long - all channels are open - there is a format file problem (use read_format to determine the problem) - invalid extension in filename - no data file found - the data file could not be opened 4.1.3 Read Format Routine: status = read_format ( filename, format, vnames ) argument | C type | FORTRAN type ------------+----------+---------------- filename | char * | character*80 (or shorter) format | int | integer*4 | (array) | (array) vnames | char ** | character*16 (see description) | | (array) This routine reads the format file associated with file "filename" and returns this information. As with the filename argument in the open channel routines, read_format accepts filenames with or without extensions. It uses the same process that the open channel routines use to determine for what data file the format information is sought. Hence, for a given filename, read_format always gets the format which corresponds to the data file opened by an open channel routine. (However, read_format accepts filenames that end with .sg_format. Such a filename would cause an error if given to an open channel routine.) When calling from FORTRAN, vnames should be declared: character*16 vnames(n) n should be equal to or greater than the number of variables in the file. A safe choice for n is the maximum number of variables permitted in a file (currently 36). The format array contains the following information: format[0] - file type indicator current meaning (could be expanded later): 1 if .sg_data file allows sample & block positioning; 0 if this is NOT allowed format[1] - number of variables/block in .sg_data file format[2*n] - data type of variable n in .sg_data file 1 - short; 2 - int; 3 - float (integer*2; integer*4; real*4 in FORTRAN) format[2*n+1] - number of values/block of variable n in .sg_data file "vnames" is an array of pointers to the names of variables saved in the data file. Thus, vnames[0] points to the name of the first variable, vnames[1] points to the name of the second variable, etc. In FORTRAN, vnames is an array of character strings. vnames(1) has the name of the first variable, vnames(2) has the name of the second variable, etc. The format information comes from a .sg_format file when the data file is a .sg_data file. Other data file types have fixed formats which return a set of parameters stored in this routine. Listed below are the format parameters for each of these other data file types. Format parameters for .spd data files: format[0] = 1 format[1] = 1 format[2] = 1 format[3] = 512 vnames[0] = "speech_data" (No other data file types are currently defined.) Returned value: The returned value indicates the error status of the routine. If no error occurs, the returned value is 0. If an error occurs, the value is < 0. The following errors are detected: - filename is too long - invalid extension in filename - no data file found, so can't determine format - the format file could not be opened - the format file is not complete - invalid value read for file type - invalid value read for variables/block - invalid value read for data type - invalid value read for values/block 4.1.4 File Length Routine: status = file_length ( chan_id, bytes, samples, blocks ) argument | C type | FORTRAN type ------------+----------+---------------- chan_id | int | integer*4 bytes | int * | integer*4 samples | int * | integer*4 blocks | int * | integer*4 This routine returns the length of the file connected to "chan_id" in terms of bytes, samples and blocks. I don't know what it should do with real-time data files (sockets) yet. Returned value: The returned value indicates the error status of the routine. If no error occurs, the returned value is 0. If an error occurs, the value is < 0. The following errors are detected: - channel number is outside valid range - specified channel is not open 4.1.5 Goto Sample Routine: position = goto_sample ( chan_id, sample_num, how ) argument | C type | FORTRAN type ------------+----------+---------------- chan_id | int | integer*4 sample_num | int | integer*4 how | int | integer*4 This routine changes the sample number that the next call to read_variable on "chan_id" reads from. When "how" is 0, "sample_num" is measured from the beginning of the file (i.e. absolute positioning). When "how" is not 0, "sample_num" is measured from the current file position (i.e. relative positioning). The first sample in the file is called sample 1. This routine can be called only if the specified channel is a "variable channel", that is one opened via open_var_channel. This routine does not return an error if the sample to be read is beyond the end of file; this condition is reported when reading. Returned value: If there are no errors, the returned value indicates the current file position (the sample number of the next sample to be read by read_variable). A valid sample number is 1 or greater. If an error occurs, the returned value is < 0. The following errors are detected: - channel number is outside valid range - specified channel is not open - this routine is for use only with variable channel types - the file connected to this channel prohibits positioning - effective sample_num is < 1 4.1.6 Read Variable Routine: position = read_variable ( chan_id, values, num, num_read ) argument | C type | FORTRAN type ------------+----------+---------------- chan_id | int | integer*4 values | varies | varies | (array) | (array) num | int | integer*4 num_read | int * | integer*4 This routine returns the next "num" values of the variable connected to channel number "chan_id". "values" is an array where the returned values are placed. In the calling routine, this array type should match that of the variable being returned. The size of "n" is limited only by the array size in the calling routine. The actual number of values read is returned in "num_read". "num_read" will equal "num" unless an error (specified below) occurs; in this case, it may be zero to num-1. This routine can be called only if the specified channel is a "variable channel", that is one opened via open_var_channel. Returned value: If there are no errors, the returned value indicates the current file position (the sample number of the next sample to be read by read_variable). A valid sample number is 1 or greater. If an error occurs, the returned value is < 0. The following errors are detected: - error during the file read operation - EOF encountered on the data file - channel number is outside valid range - specified channel is not open - this routine is for use only with variable channel types 4.1.7 Goto Block Routine: position = goto_block ( chan_id, block_num, how ) argument | C type | FORTRAN type ------------+----------+---------------- chan_id | int | integer*4 block_num | int | integer*4 how | int | integer*4 This routine changes the place from which data is read in the file connected to "chan_id". When "how" is 0, "block_num" is measured from the beginning of the file (i.e. absolute positioning). When "how" is not 0, "block_num" is measured from the current file position (i.e. relative positioning). It can be used on block channels and on variable channels. The first block in a file is called block 1. This routine does not return an error if positioned beyond the end of file; this condition is reported when reading. On block channels, the repositioning is as follows: If absolute positioning, the block number read by the next read_block is "block_num". If relative positioning, the block number read by the next read_block is changed by "block_num" blocks. On variable channels, the repositioning is as follows: If absolute positioning, the first sample read by the next read_variable is sample number: 1 + (block_num - 1) * (# of values of variable per block) If relative positioning, the first sample read by the next read_variable is changed by "block_num" blocks. Returned value: If there are no errors, the returned value indicates the current file position. On variable channels, the returned value is the sample number of the next sample to be read by read_variable. On block channels, the returned value is the block number of the next block to be read by read_block. A valid sample number or block number is 1 or greater. If an error occurs, the returned value is < 0. The following errors are detected: - channel number is outside valid range - specified channel is not open - the file connected to this channel prohibits positioning - effective block_num < 1 4.1.8 Read Block Routine: position = read_block ( chan_id, val_pntr ) argument | C type | FORTRAN type ------------+----------+---------------- chan_id | int | integer*4 val_pntr | pointer | integer*4 (see below) | array | array This routine returns one block of values from the file connected to channel "chan_id". "val_pntr" is an array of pointers having one element for each block variable. The calling routine sets each pointer in the array to specify where the values for each block variable are to go. Thus, val_pntr[0] is the address where the first value of the first block variable will be stored; val_pntr[1] is the address where the first value of the second block variable will be stored; etc. Subsequent values of the variables are stored in sequential locations following the address of the first value. For FORTRAN (ugh) users, here's what you do with val_pntr. This array is set up to contain for each block variable, the ADDRESS of where you want the first value of each variable to be placed. A routine for getting the address of a variable is included in the disk I/O package. To illustrate the process, suppose you have two variables in a block and you want to store them in x and y. The following code fragment shows what you do: INTEGER*4 GET_ADDRESS ! get_address returns an address of type integer*4 REAL*4 X(13) ! there are 13 values of X per block INTEGER*4 Y(8) ! there are 8 values of Y per block VAL_PNTR(2) ! there are 2 variables per block VAL_PNTR(1) = GET_ADDRESS( X(1) ) VAL_PNTR(2) = GET_ADDRESS( Y(1) ) STATUS = READ_BLOCK(CHAN_ID, VAL_PNTR) If the X and Y arrays are stored at fixed memory addresses, the val_pntr array can be set just once, rather than before each use of read_block. If the arrays move, val_pntr must be recalculated. Returned value: If there are no errors, the returned value indicates the current file position (the block number of the next block to be read by read_block). A valid block number is 1 or greater. If an error occurs, the returned value is < 0. The following errors are detected: - channel number is outside valid range - specified channel is not open - this routine is for use only with block channel types - error during the file read operation - EOF encountered on the data file 4.1.9 Close Channel Routine: status = close_channel ( chan_id ) argument | C type | FORTRAN type ------------+----------+---------------- chan_id | int | integer*4 This routine closes the specified channel. Doing so makes the channel id available for re-assignment. Returned value: The returned value indicates the error status of the routine. If no error occurs, the returned value is 0. If an error occurs, the value is < 0. The following errors are detected: - channel number is outside valid range - specified channel is not open 4.1.10 Disk Read Error Routines: print_disk_read_error ( err_num ) (Callable from FORTRAN) error_string = disk_read_error ( err_num ) (No FORTRAN interface yet) argument | C type | FORTRAN type ------------+----------+---------------- err_num | int | integer *4 The disk read routines return a value which, when less than zero, corresponds to an error number. The disk read error routines provide a way to turn the error number into an appropriate error message. The disk_read_error routine returns a brief error message corresponding to "err_num". The "print_disk_read_error" routine prints a more elaborate error message. These routines return "No error" for arguments zero or greater and "Bad value for error number" if "err_num" is not a value from one of the read routines.