Chapter Contents

Previous

Next
I/O Functions

Technical Summaries

This section provides detailed discussions of many of the components of C I/O, such as opening files, file positioning, and using the standard input, output, and error files. There are also sections that address I/O under USS, advanced OS/390 and CMS I/O facilities, and how to perform VSAM keyed I/O in C. The last section attempts to answer some of the most commonly asked I/O questions.


Standard I/O Overview

The standard I/O package provides a wide variety of functions to perform input, output, and associated tasks. It includes both standard functions and augmented functions to support 370-oriented features.

In general, a program that uses standard I/O accesses a file in the following steps:

  1. Open the file using the standard function fopen or the augmented function afopen . This establishes a connection between the program and the external file. The name of the file to open is passed as an argument to fopen or afopen . The fopen and afopen functions return a pointer to an object of type FILE . (This type is defined in the header file <stdio.h> , which should be included with a #include statement by any program that uses standard I/O.) The data addressed by this FILE pointer are used to control all further program access to the file.

  2. Transfer data to and from the file using any of the functions listed in this section. The FILE pointer returned by fopen is passed to the other functions to identify the file to be processed.

  3. Close the file. After the file is closed, all changes have been written to the file and the FILE pointer is no longer valid. When a program terminates (except as the result of an ABEND), all files that have not been closed by the program are closed automatically by the library.

For convenience, three standard files are opened before program execution, accessible with the FILE pointers stdin , stdout , and stderr . These identify the standard input stream, standard output stream, and standard error stream, respectively. For TSO or CMS programs, these FILE objects normally identify the terminal, but they can be redirected to other files by use of command-line options. For programs running under the USS shell, these FILE objects reference the standard files for the program that invoked them. More information on the standard streams is available later in this section.

Standard I/O functions may be grouped into several categories. The functions in each category and their purposes are listed in Standard I/O Functions.

Standard I/O Functions
Function Purpose
Control Functions control basic access to files
fopen+ opens a file
afopen*+ opens a file with system-dependent options
freopen+ reopens a file
afreopen*+ reopens a file with system-dependent options
tmpfile creates and opens a temporary file
tmpnam generates a unique filename
fflush writes any buffered output data
afflush+ forces any buffered output data to be written immediately
fclose+ closes a file
setbuf+ changes stream buffering
setvbuf+ changes stream buffering
Character I/O Functions read or write single characters
fgetc reads a character
getc reads a character (macro version)
ungetc pushes back a previously read character
getchar reads a character from stdin
fputc writes a character
putc writes a character (macro version)
putchar writes a character to stdout
String I/O Functions read or write character strings
fgets reads a line into a string
gets reads a line from stdin into a string
fputs writes a string
puts writes a line to stdout
Array I/O Functions read or write arrays or objects of any data type
fread reads on or more data elements
fwrite writes one or more data elements
Record I/O Functions read or write entire funtions
afread* reads a record
afread0* reads a record (possibly length 0)
afreadh* reads the initial part of a record
afwrite* writes a record
afwrite0* writes a record (possibly length 0)
afwriteh* writes the initial part of a record
Formatted I/O Functions easily read or write formatted data
fprintf writes one or more formatted items
printf writes one or more formatted items to stdout
sprintf formats items into a string
snprintf* formats items into a string (with maximum length)
fscanf reads one or more formatted items
scanf reads one or more formatted items from stdin
sscanf obtains formatted data from a string
vfprintf writes formatted data to a file
vprintf writes formatted data to a standard output string
vsprintf writes formatted data to a string
vsnprintf writes formatted data to a string (with maximum length)
File Positioning Functions interrogate and change the file position
fseek positions a file
fsetpos positions a file
rewind positions a file to the first byte
ftell returns current file position for fseek
fgetpos returns current file position for fsetpos
Keyed Access Functions read, write and position a keyed stream
kdelete*+ delete a record from a keyed file
kgetpos*+ return position of current keyed file record
kinsert*+ add a new record to a keyed file
kreplace*+ replace a new record in a keyed file
kretrv*+ retrieve a record from a keyed file
ksearch*+ search for record in a keyed file
kseek*+ reposition a keyed file
ktell*+ return RBA of current record of keyed file
Error-Handling Functions test for and continue execution after I/O errors and other I/O conditions
feof+ tests for end of file
ferror+ tests for error
clearerr+ resets previous error condition
clrerr*+ resets previous error condition
File Inquiry Functions obtain information about an open file at execution time
fattr*+ returns file attributes
fileno* returns file number
ffixed*+ tests whether a file has fixed length records
fnm*+ returns the name of a file
fterm*+ tests whether a file is the user's terminal

In Standard I/O Functions,


UNIX Style I/O Overview

The UNIX style I/O package is designed to be compatible with UNIX low-level I/O, as described in previous sections. When you use UNIX style I/O, your program still performs the same three steps (open, access, and close) as those performed for standard I/O, but there are some important distinctions.

By convention, UNIX assigns the file numbers 0, 1, and 2 to the standard input, output, and error streams. Some programs use UNIX style I/O with these file numbers in place of standard I/O to stdin , stdout , and stderr , but this practice is nonportable. The library attempts to honor this kind of usage in simple cases, but for the best results the use of standard I/O is recommended.

UNIX style I/O offers fewer functions than standard I/O. No formatted I/O functions or error-handling functions are provided. In general, programs that require elaborately formatted output or control of error processing should, where possible, use standard I/O rather than UNIX style I/O. Some UNIX style I/O functions, such as fcntl and ftruncate are supported only for files in the USS hierarchical file system.

The functions supported by UNIX style I/O and their purposes are listed in UNIX Style I/O Functions. Note that the aopen function is not defined by UNIX operating systems. Also note that some POSIX-defined functions, such as ftruncate , are not implemented by all versions of UNIX.

UNIX Style I/O Functions
Function Purpose
Control Functions opens a file with system-dependent options
aopen+ opens a file with system-dependent options
close+ closes a file
creat creates a file and opens it for output
dup- duplicates a file descriptor
dup2 duplicates a file descriptor
fcntl+- controls file options and attributes
fdopen+- associates a file descriptor with a FILE pointer
fsync forces output to be written to disk
ftruncate- truncates a file
mkfifo- creates an USS FIFO special file
mknod- creates an USS special file


pipe- creates an USS pipe
Character I/O Functions
read+ reads characters from a file
write+ writes charaters to a file
File Postitioning Functions
lseek positions a file
File Inquiry Functions
ctermid returns the terminal filename
isatty+ tests whether a file is the user's terminal
ttyname returns the name of the terminal associated with a file descriptor

In ,


Opening Files

Although there are several different functions that you can call to open a file (for example, fopen , afopen , and open ), they all have certain points of similarity. The filename (or pathname) and an open mode are passed as arguments to each of these functions. The filename identifies the file to be opened. The open mode defines how the file will be processed. For example, the function call fopen("input", "rb") opens the file whose name is input for binary read-only processing.

Some of the open functions enable the caller to specify a C library access method name, such as "rel" , and access method parameters (or amparms) such as "recfm=v" . Access method parameters allow the program to specify system or access-method-dependent information such as file characteristics (for example, record

format) and processing options (for example, the number of lines per page). The details for each of these specifications are described in this section.

General filename specification

The general form of a SAS/C filename is [//] style:name , where the portion of the name before the colon defines the filename style, and the portion after the colon is the name proper. For example, the style ddn indicates that the filename is a DDname, while the style cms indicates that the filename is a CMS fileid or device name.

Note:    The // before the rest of the pathname is optional, except for programs compiled with the posix option. See USS I/O Considerations for a discussion of filenames for posix -compiled programs.  [cautionend]

The style: part of the filename is optional. If no style is specified, the style is chosen as follows:

As an aid to migration of programs between OS/390 and CMS, filenames oriented toward one system, such as cms:user maclib and tso:output.list , are accepted by the other system when a clearly equivalent filename can be established. (See Filename specification under OS/390 for details.)

The rules just described apply only to programs that are not compiled with the posix compiler option. For posix -compiled programs, all pathnames are treated as hierarchical file system names, unless they are preceded by the // prefix, even if they appear to contain a style prefix.

Filename specification under OS/390

The library supports four primary styles of filenames under OS/390: ddn , dsn , tso , and hfs . A ddn -style filename is a DDname, a dsn -style filename is a data set name in JCL syntax, a tso -style filename is a data set name in TSO syntax, and an hfs -style filename is a pathname referencing the USS hierarchical file system.

ddn-style filenames A filename in ddn style is a valid DDname, possibly preceded by leading white space. The filename can be in uppercase or lowercase letters, although it is translated to uppercase letters during processing. The following alternate forms are also allowed, permitting access to PDS members and to the TSO terminal:

*
*ddname
ddname*
ddname(member-name)

A ddn -style filename of * always references the user's TSO terminal. If you use this filename in a batch job, it references the SYSTERM DDname, if the file is being opened for output or append. (See Open modes for more information.) The filename * is not supported for input in batch. For a program executing under the

USS shell, a ddn -style filename of * is interpreted as referencing /dev/tty in the hierarchical file system.

A ddn style filename of *ddname references the terminal for a TSO session or the DDname indicated for a batch job. For example, the filename *sysin requests use of the terminal under TSO or of the DDname SYSIN in batch.

A ddn -style filename of ddname* references the indicated DDname, if that DDname is defined. If the DDname is not defined, ddname* references the TSO terminal or SYSTERM in batch (for an open for output or append). For example, the filename LOG* requests the use of the DDname LOG, if defined, and otherwise, the user's TSO terminal.

A ddn -style filename of ddname(member-name) references a member of the PDS identified by the DDname. For example, the filename SYSLIB(fcntl) requests the member FCNTL of the data set whose DDname is SYSLIB. If the DD statement also specifies a member name, the member name specified by the program overrides it.

With the availability of USS, another ddn -style filename is possible:

ddname/filename

Here, ddname is a valid OS/390 filename, and filename is a valid POSIX filename (not containing any slashes).

For more information on this form, see Using HFS directories and PDS members interchangeably.

Note:    Programs invoked via the USS exec system call do not ordinarily have access to DD statements. A SAS/C extension allows environment variables to be used in place of DD statements, as described in USS I/O Considerations.  [cautionend]
dsn-style filenames A filename in dsn style is a valid, fully qualified data set name (possibly including a member name), optionally preceded by leading white space. The data set name can be in uppercase or lowercase letters, although it is translated to uppercase letters during processing. The data set name must be completely specified; that is, there is no attempt to prefix the name with a userid, even for programs running under TSO. (Programs that want to have the prefix added should use the tso filename style.) For more information on data set names and their syntax, consult the IBM manual MVS/ESA JCL Reference.

The following alternate forms for dsn -style names are also allowed, permitting access to temporary data sets, the TSO terminal, DUMMY files, and SYSOUT files:

*
nullfile
sysout=classtmpname

A dsn -style filename of * always references the user's TSO terminal. If this filename is used in a batch job, it references the SYSTERM DDname, if the file is being opened for output or append. (See Open modes.) The filename * is not supported for input in batch. For a program running under the USS shell, * is interpreted as referencing /dev/tty .

A dsn -style filename of nullfile references a DUMMY (null) data set. Reading a DUMMY data set produces an immediate end of file; data written to a DUMMY data set are discarded.

A dsn -style filename of sysout=class references a SYSOUT (printer or punch) data set of the class specified. The class must be a single alphabetic or numeric character, or an asterisk.

A dsn -style filename of &tmpname references a new temporary data set, whose name is &tmpname . The name is limited to eight characters.

tso-style filenames A filename in tso style is a data set name (possibly including a member name) specified according to TSO conventions, optionally preceded by leading white space. The data set name can be in uppercase or lowercase letters, although it is translated to uppercase letters during processing. If the data set name is not enclosed in single quotation marks, the name is prefixed with the user's TSO prefix (normally the userid), as defined by the TSO PROFILE command. If the data set name is enclosed in single quotation marks, the quotes are removed and the result is interpreted as a fully qualified data set name. For more information on TSO data set names and their syntax, consult the IBM manual TSO Extensions User's Guide.

Note:    tso -style filenames are not guaranteed to be available, except for programs executing under TSO. If you attempt to open a tso -style filename in OS/390 batch (or under the USS shell), the userid associated with the batch job is used as the data set prefix. Determining the userid generally requires RACF or some other security product to be installed on the system. If the userid cannot be determined, the open call will fail.  [cautionend]

The following alternate forms for tso -style names are also allowed, permitting access to the TSO terminal and DUMMY files.

*
'nullfile'

A tso -style filename of * always references the user's TSO terminal. For programs running under the USS shell, it is interpreted as referencing the HFS file called /dev/tty .

A tso -style filename of 'nullfile' references a DUMMY data set. Reading a DUMMY data set produces an immediate end of file; data written to a DUMMY data set are discarded.

cms-style filenames For compatibility with CMS, the OS/390 version of the SAS/C library accepts cms -style filenames, where possible, by transforming them into equivalent tso -style filenames. See the next section, "Filename specification under CMS," for details on the format of cms style filenames.

A cms -style filename is transformed into a tso -style filename by replacing spaces between the filename components with periods, removing the MEMBER keyword, and adding a closing parenthesis after the member name, if necessary. Also, the filenames cms: * and cms: are interpreted as tso: * and tso: 'nullfile' , respectively. For instance, the following transformations from cms -style to tso -style names are performed:

cms: profile exec a1               tso: profile.exec.a1
cms: lc370 maclib (member stdio    tso: lc370.maclib(stdio)
cms: reader                        tso: reader
cms: *                             tso: *
cms:                               tso: 'nullfile'

hfs-style filenames A filename in hfs style is a pathname in the USS hierarchical file system. If the pathname begins with a / , the pathname is an absolute pathname, starting at the root directory. If the pathname does not begin with a / , it is interpreted relative to the current directory.

Note:    You cannot open an HFS directory using fopen or open . The opendir function must be used for this purpose.  [cautionend]

Filename specification under CMS

The library supports five primary styles of filename under CMS: cms , xed , ddn , sf , and sfd . A cms - or xed -style filename is a CMS fileid or device name. A ddn -style file is a DDname (FILEDEF or DLBL name). A sf -style filename is the name of a CMS shared file system file, and a sfd -style filename is a pattern defining a subset of a CMS shared file system directory.

The only difference between the cms and xed styles is that, if a program is running under XEDIT, use of the xed prefix allows reading of the file from XEDIT, rather than from disk.

cms- and xed-style filenames A filename in cms style is a CMS fileid or device name. You can specify fileids in one of two formats: a CMS standard format, or a compressed format. The compressed format contains no blanks, so it can be used in cases in which the presence of blanks is not allowed, such as in command-line redirections. The xed style permits a subset of the valid cms specifications, as described in Advanced CMS I/O Facilities. Here is the standard form for a cms -style filename:

filename  [filetype [filemode]] [(MEMber member-name)]]

The brackets indicate optional components. The filename may be preceded by white space and can be in uppercase or lowercase letters, although it is translated to uppercase letters during processing. Detailed rules for this style of filename are as follows:

Here is the compressed form for a cms -style filename:

filename [.filetype [.filemode]] [(member-name)]

This form of filename is interpreted in exactly the same way as the corresponding standard name. For example, cms: freds maclib (mem smith) and cms: freds.maclib(smith) are equivalent specifications. For more information on CMS fileids, consult the IBM CMS manuals listed in Chapter 1, "Introduction," in the SAS/C Compiler and Library User's Guide.

The following alternate forms for cms -style names are also allowed, permitting access to unit record devices and members of GLOBAL MACLIBs or TXTLIBs. (See Advanced CMS I/O Facilities for a description of access to GLOBAL MACLIBs and TXTLIBs.) After each form, valid abbreviations are given. (None of these forms can be used with the xed style.)

Alternate Forms Abbreviations
TERMINAL TERM,*
READER RDR
PRINTER PRT
PUNCH PUN, PCH
%MACLIB (member member-name) %MACLIB (member-name)
TXTLIB (member member-name) %TXTLIB (member-name)

Also, an empty filename ( "" ) may be used to open a dummy file.

Note:    To open a CMS disk file whose filename is the same as one of the above device names, you must specify both the filename and the filetype.  [cautionend]

ddn-style filenames A filename in ddn style is a valid DDname, possibly preceded by white space. The filename can be in uppercase or lowercase letters, although it is translated to uppercase letters during processing. The DDname must be previously defined using the FILEDEF command (or the DLBL command for a VSAM file). The following alternate forms are also allowed, permitting access to members of MACLIBs, TXTLIBs, and OS/390 PDSs, and to the CMS terminal. (All forms have approximately the same meaning as under OS/390.) For more information, see Filename specification under OS/390.

*
*ddname
ddname*
ddname(member-name)

A ddn -style filename of * always references the user's CMS terminal.

A ddn -style filename of *ddname also references the terminal. (The DDname is never used because the terminal is always defined under CMS.)

A ddn -style filename of ddname* references the indicated DDname, if that DDname is defined. If the DDname is not defined, it references the CMS terminal. For example, the filename LOG* requests the use of the DDname LOG, if defined, and otherwise, the user's terminal.

A ddn -style filename of ddname(member-name) references a member of the MACLIB, TXTLIB, or OS/390 PDS identified by the DDname. For example, the filename SYSLIB(fcntl) requests the member FCNTL of the file whose DDname is SYSLIB. If the FILEDEF command also specifies a member name, the member name specified by the program overrides it.

tso-style filenames For compatibility with OS/390, the CMS version of the library accepts tso -style filenames where possible, by transforming them into equivalent cms -style filenames. See Filename specification under OS/390 for details on the format of such filenames.

A tso -style filename is transformed into a cms -style filename by removing single quotation marks, if present, and treating the resulting name as a compressed format fileid. (The result must be a valid CMS fileid or the open fails.) In addition, the specification tso: * is interpreted as cms: terminal . For instance, the following transformations from tso -style to cms -style names are performed:

tso: input.data                    cms: input data
tso: parser.source.c               cms: parser source c
tso: 'sys1.maclib(dcb)'            cms: sys1 maclib (member dcb
tso: *                             cms: terminal

sf-style filenames A sf -style filename references a file in the CMS shared file system. See Using the CMS Shared File System for detailed information on the syntax of sf -style filenames.

sfd-style filenames A sfd -style filename references a CMS shared file system directory or directory subset. See Using the CMS Shared File System for detailed information of the syntax of sfd -style filenames.

Open modes

The second argument to each open routine is an open mode, which defines how the file will be processed. This argument is specified differently, depending on whether you are using standard I/O or UNIX style I/O, but the basic capabilities are the same.

Standard I/O open modes When you open a file using standard I/O, the open mode is a character string consisting of one to three enquoted characters. The syntax for this string is as follows:

r|w|a[+][b|k]

The first character must be 'r' , 'w' , or 'a' . After the first character, a '+' may appear, and after the '+' (or after the first character, if '+' is omitted), 'b' or 'k' may appear. No blanks may appear in the string, and all characters must be lowercase letters.

The 'r|w|a' character specifies whether the file is to be opened for reading, writing, or appending. If a '+' appears, both reading and writing are permitted.

If a 'b' appears, the file is accessed as a binary stream. If a 'k' appears, the file is accessed as a keyed stream. If neither 'b' nor 'k' appears, the file is accessed as text. See Text access and binary access for detailed information on the differences between text and binary access. See Using VSAM Files for information on keyed access.

The effect of the 'r|w|a' specification and the '+' are closely linked and must be explained together.

Note:    For compatibility with some PC C libraries, certain variant forms of the open mode parameter are accepted. The order of the '+' and the 'b' may be reversed, and an 'a' may appear in place of the 'b' to request that the file be accessed as text.  [cautionend]

UNIX style I/O open modes When you open a file using UNIX style I/O, the open mode is an integer, with open mode options indicated by the presence or absence of particular bit settings. The open mode is normally specified by ORing symbolic constants that specify the options required. For instance, the specification O_RDONLY|O_BINARY is used for a read-only file to be accessed as a binary stream. The symbolic constants listed here are all defined in the header file <fcntl.h> .

The following open mode options are supported by UNIX style I/O:
O_RDONLY specifies that the file will be read but not written. If you do not specify O_WRONLY or O_RDWR , O_RDONLY is assumed.
O_WRONLY specifies that the file will be written but not read.
O_RDWR specifies that the file will be both read and written.
O_APPEND specifies that the file will be positioned to the end of file before each output operation.
O_CREAT specifies that if the file does not exist, it is to be created. (See File existence.) If O_CREAT is omitted, an attempt to open a file that does not exist fails.
O_TRUNC specifies that if the file exists, the file's current contents will be discarded when the file is opened.
O_EXCL is meaningful only if O_CREAT is also set. It excludes the use of an already existing file.
O_NONBLOCK specifies the use of non-blocking I/O. This option is meaningful only for USS HFS files.
O_NOCTTY specifies that the file is not to be treated as a "controlling terminal." This option is meaningful only for USS HFS files.
O_BINARY specifies that the file be accessed as a binary stream. If O_TEXT is not specified, O_BINARY is assumed. (The synonym O_RAW is supported for compatibility with other compilers.)
O_TEXT specifies that the file be accessed as a text stream.

Note:    UNIX I/O does not support keyed streams.  [cautionend]

Standard I/O and UNIX Style I/O Open Modes defines equivalent forms for standard I/O and UNIX style I/O open modes. Some UNIX style I/O open modes have no standard I/O equivalents.

Standard I/O and UNIX Style I/O Open Modes
Standard form UNIX style form
'r' O_RDONLY | O_TEXT
'rb' O_RDONLY
'r+' O_RDWR | O_TEXT
'r+b' O_RDWR
'w' O_WRONLY | O_CREAT | O_TRUNC | O_TEXT
'wb' O_WRONLY | O_CREAT | O_TRUNC
'w+' O_RDWR | O_CREAT | O_TRUNC | O_TEXT
'w+b' O_RDWR | O_CREAT | O_TRUNC
'a' O_WRONLY | O_APPEND | O_CREAT | O_TEXT
'ab' O_WRONLY | O_APPEND | O_CREAT
'a+' O_RDWR | O_APPEND | O_CREAT | O_TEXT
'a+b' O_RDWR | O_APPEND | O_CREAT


Library access method selection

When you use afopen or afreopen to open a file, you can specify the library access method to be used. If you use some other open routine, or specify the null string as the access method name, the library selects the most appropriate access method for you. If you specify an access method that is incompatible with the attributes of the file being opened, the open fails, and a diagnostic message is produced. Six possible access method specifications are available:

When no specific access method is requested by the program, the library selects an access method as follows:


Access method parameters

When you use afopen , afreopen , or aopen to open a file, you can optionally specify one or more access method parameters (or amparms). These are system-dependent options that supply information about how the file will be processed or allocated.

The amparms are specified as character strings containing one or more specifications of the form amparm=value , separated by commas (for example, "recfm=v,reclen=100" ). You can specify the amparms in any order and in uppercase or lowercase letters. (However, the case of the value for the eof and prompt amparms is significant.)

There are two sorts of amparms: those that describe how the file will be processed and those that specify how an OS/390 file will be created when the filename is specified in dsn or tso style. All amparms are accepted under both OS/390 and CMS, but their exact interpretation and their defaults differ from system to system, as described in the following section. Inapplicable amparms are ignored rather than rejected whenever reasonable.

The function descriptions for afopen , afreopen , and aopen provide examples of typical amparm usage.

File processing amparms The file processing amparms may be classified into the following four categories:

File Characteristics

recfm=f/v/u
operating system record format

reclen=nnn|x
operating system record length

blksize=nnn
operating system block size

keylen=nnn
VSAM key length requirement

keyoff=nnn
VSAM key offset requirement

org=value
file organization requirement

overjcl=no | yes
allows the program to override file attributes specified by JCL

File Usage

print=yes|no
file destined to be printed

page=nnn
maximum lines per page (with print=yes)

pad=no|null|blank
file padding permitted

trunc=yes|no
effect of output before end of file

grow=yes|no
controls whether new data can be added to a file

order=seq|random
specifies whether records for a file are normally processed in sequential or random order

commit=yes|no
specifies whether modifications to a file should be committed when the file is closed

dirsearch=value
used when opening a CMS Shared File System directory to specify the information to be retrieved from the directory

share=ispf|alloc|rls
specifies special sharing options

Terminal Options

eof=string
end-of-file string

prompt=string
terminal input prompt

VSAM Performance Options

bufnd=nnn
number of data I/O buffers VSAM is to use

bufni=nnn
number of index I/O buffers VSAM is to use

bufsp=nnn
maximum number of bytes of storage to be used by VSAM for file data and index I/O buffers

bufsize=nnn
size, in bytes, of a DIV window for a linear data set

bufmax=n
number of DIV windows for a linear data set

See Terminal I/O for a discussion of the eof and prompt amparms. See VSAM-related amparms for a discussion of the VSAM Performance amparms.

The default amparms vary greatly between OS/390 and CMS, so they are described separately for each system.

File characteristics amparms The recfm , reclen , blksize , keylen , keyoff , and org keywords specify the program's expectations for record format, maximum record length, block size, key length, key offset, and file organization. If the file is not compatible with the program's recfm , reclen , or blksize specifications, it is still opened, but a warning message is directed to the standard error file. If the file is not compatible with the program's keylen , keyoff , or org specifications, a diagnostic message is produced, and the open fails.

If the file is being opened for output and the previous file contents are discarded, the file will, if possible, be redefined to match the program's specifications, even if these are incompatible with the previous file attributes. This is not done if any of the file's contents are to be preserved, because changing the file characteristics may make this data unreadable. (One effect of this is that the characteristics of an OS/390 partitioned data set are never changed, because even if one member is completely rewritten, other members continue to exist.)

To resolve conflicts between the file characteristics, recfm, reclen, and blksize, when they are specified in the JCL and the program, you can use the amparm overjcl to indicate whether the file characteristics specified in the JCL or the program have precedence. overjcl is effective only for sequential files opened with an open mode of w, wb, w+, or w+b. For non-sequential files, such as partitioned data sets, or for input files, the JCL always has precedence. Values for overjcl contains the values for overjcl and their descriptions.

Values for overjcl
Value Description
No File characteristics specified in the JCL will have precedence. This is the default setting.
Yes File characteristics specified in the program will have precedence.

The effects of these amparms are sometimes different from similar specifications on a DD statement, a TSO ALLOCATE command, or a CMS FILEDEF. JCL or command specifications always override any previously established file characteristics, but amparms override only if the library can determine that this is safe.

Details of the file characteristics amparms include the following:

File usage amparms File usage amparms allow the program to specify how a file will be used. A specification that cannot be honored may cause the open to fail, generate a warning message, or cause a failure later in execution, depending on the circumstances. The exact treatment of these amparms is highly system-dependent.


Amparms - OS/390 details

This section discusses amparms under OS/390. It provides an explanation of and defaults for each amparm.

File characteristics For input files, or output files in which some or all of a file's previous contents are preserved, the file characteristics amparms serve as advice to the library regarding the file characteristics expected. If the actual file does not match the program's assumptions, a warning message is generated. In some cases, no warning is generated, even though the file characteristics are not exactly what the program specified. For instance, if a program specifies amparms of "recfm=v,reclen=80" and opens an input file with LRECL 40, no diagnostic is generated, because all records of the input file are consistent with the program's assumption of input records with a maximum length of 80 bytes.

To determine the characteristics of a file, the library merges information from the amparms, control language specifications, and the data set label. Unlike amparms information, control language specifications always override the physical file characteristics recorded in the label.

For each of these amparms, processing is as follows:
recfm If you specify recfm=f , the program expects records of equal length, and a warning is generated if the file does not have fixed-length records (blocked or unblocked).

If recfm=v or recfm=u is specified for a read-only file, no diagnostic is ever generated. For a write-only or update file, a warning is generated if the OS/390 RECFM does not match the amparm.

Note:    VSAM linear and RRDS files are always considered to have RECFM F, and other VSAM files and USS HFS files are considered to have RECFM V.  [cautionend]

reclen If you specify reclen=nnn for a read-only file, a warning is generated if the file's record size is larger, or if it is not equal and the record format is fixed. If reclen=x is specified for a read-only file, a diagnostic is never generated, except when the record format is fixed. Note that under OS/390, the program's reclen specification is compared to the LRECL-4 for a V-format file, not to the LRECL itself. (Additionally, for a file with carriage control characters, the control character is not counted.)

If you specify reclen=nnn for a write-only or update file, a warning is generated if the file's record size is not the same as the reclen specification. If you specify reclen=x , a warning is generated unless the file has RECFM=VBS or RECFM=VS and LRECL=X.

VSAM linear data sets are always considered to have reclen=4096 .

blksize If you specify blksize=nnn , a warning is generated only if the actual blksize is greater than that specified.

Note:    When a write-only or update file is opened and none of the file's previous contents are preserved, the file's characteristics are changed to correspond to the program's amparms specifications. The details of this process are outlined here:

  [cautionend]

When a file is opened and neither the file nor any amparms fully specify the file characteristics, the following rules apply:

OS/390 Default File Characteristics Amparms
Device Type recfm reclen blksize
Card Punch f 80 80
Printer/SYSOUT/DUMMY V 132 141
Other (print=yes) V 132 6144
Other (print=no) V 255 6144

File usage The amparm print has two uses: it specifies whether the corresponding file includes ANSI carriage control characters, and it specifies whether the C library should process the control characters '\r' and '\f' to effect page formatting. If print=no is specified, then '\r' and '\f' are simply treated as data characters, even if the file can support page formatting. If you specify print=yes , then the library attempts to perform page formatting. However, if the associated file does not have the correct attributes, '\r' and '\f' are treated as new lines, and a warning message is generated when the file is opened. If neither print=no nor print=yes is specified, the library chooses a default based on the attributes of the external file. However, print=yes is supported only for a file accessed as a text stream.

Under OS/390, a file is considered to be suitable for page formatting if it has the following characteristics:

For files with RECFM A, space for the control character is included in the LRECL, but not in any reclen specification made by the program.

The page amparm, which specifies the number of lines to be printed on each page of a print=yes file, does not have a default. That is, if page is not specified, page ejects will occur only when a '\f' is written to the file.

Note:    The print and page amparms are ignored when opening files in the USS hierarchical file system. Control characters are always written to an HFS file, regardless of whether you specified print .  [cautionend]

The pad amparm specifies whether padding of records will take place, and, if so, whether a blank or a null character ( '\0' ) will be used as the pad character. The default depends on the library access method and the open mode, as follows:

The trunc amparm indicates to the library whether the program requires that output before the end of file erase the following records or leave them unchanged. Under OS/390, whether existing data are preserved in this situation depends only on file type and access method. If trunc is omitted, the value corresponding to the file's actual behavior is used; if a conflicting trunc specification is made, the file fails to open. If a file is processed by the "rel" access method, is a VSAM file, or is in the USS HFS, only trunc=no is supported. For all other OS/390 file types, only trunc=yes is supported.

The grow amparm indicates to the library whether new data can be added to a file opened for "r" or "r+" . The specification grow=no informs the library that the program will only replace existing records of a file, rather than adding any data to the end. When you specify grow=no for a file processed with BSAM, the library can open it for UPDAT rather than OUTIN. This allows the library to support use of the fseek or fsetpos functions on a PDSE member. grow=no implies trunc=no .

The share=ispf amparm
The share=ispf amparm allows a program to write to an ISPF member without allocating the entire dataset as "OLD". Other programs can continue to read and write to other members while the program updates the designated member.

Here is an example:

int cardfd;
cardfd = aopen("dsn:sas.test.c(hello)", 
               O_RDWR, "share=ispf");

Note:    Opening a file with the share=ispf amparm allows a PDS to be shared by several programs or users but must be used carefully.

Using share=ispf allows a PDS to be allocated as SHR and used by cooperating programs, that is, by the ISPF editor and utilities, by other SAS/C programs which open specifying share=ispf , and by any other applications which observe the ISPF protocols.

Using share=ispf does not prevent access by applications that do not observe the ISPF protocols. Such access may cause file damage or loss of data.

While a SAS/C program has a PDS member open with share=ispf , an attempt by an ISPF user to save another member of the same PDS will wait until the SAS/C program closes the member. Similarly, when one SAS/C program has a PDS member open using share=ispf , any other SAS/C program which opens the same PDS with share=ispf will wait until the first program closes its member. For this reason, programs which use share=ispf should be designed to keep such files open for as small an interval of time as possible.  [cautionend]

The share=alloc amparm
The share=alloc amparm is used to open a new or existing file by DSN as shared. Here is an example:
int cardfd;
cardfd = 
  aopen("dsn:sas.test",
        O_CREAT | O_RDWR | O_APPEND,
       "recfm=f,reclen=80,share=alloc");

CAUTION:
When you open a file using the share=alloc amparm, the operating system and the C library offer little protection against file damage or loss of data if several programs write to the file at the same time. Some versions of OS/390 will ABEND a program which attempts to write to a PDS that another program has opened; however, no protection at all is available for sequential data sets. For this reason, the share=alloc amparms should be used only when there is no risk of multiple simultaneous access, when the program itself synchronizes access to the file ( for example, using the OS/390 ENQ macro, or where the risk of occassional loss of data or file damage is considered acceptable.  [cautionend]

Record Level Sharing (rls) amparms

shr=rls
specifies the use of VSAM Record Level Sharing protocols for processing the dataset.

shr=rlsread
specifies the use of VSAM Record Level Sharing protocols for processing the dataset and specifies their use for nonupdate file accesses.

Note:    Many of the sharing pitfalls can be prevented by used of VSAM Record Level Sharing (RLS) support available for OS/390 with DFSMS Version 1, Release 3 or higher. Also, with VSAM, RLS locking is done at the record level as opposed to the control interval.   [cautionend]

The share=rls or share=rlsread amparm specifies that VSAM record level sharing protocols are to be used for processing the dataset. The RLS protocols are available for OS/390 only with DF/SMS Version 1, Release 3 (or higher) installed or OS/390 Release 2, which includes it. In addition, using the RLS feature requires supporting hardware (SYSPLEX Coupling Facility (CF)) and proper site installation configuration of DF/SMS.

This option is meaningful only for VSAM files and is equivalent to coding MACRF=(RLS) for share=rls or MACRF=(RLS) , RLSREAD=CRI for share=rlsread on an ACB assembler macro used to open the VSAM file. RLS implies the use of cross-system record level locking as opposed to CI locking, uses CF for cross-system buffer consistency with a coordinated system wide local buffer cache. The amparm share=rlsread also specifies that consistent read integrity (record locking during all reads) be used for read requests, even if the dataset is opened for read only ( mode=r ) or K_noupdate flag is specified with kretrv . Specifying share=rlsread overrides any RLS JCL specification. VSAM RLS supports only cluster or path level access (that is, no individual data, index, or alternate index component access) and does not support linear datasets or datasets which are defined with an imbedded index or a keyrange. Recoverable datasets, that is, datasets defined with the IDCAMS LOG(UNDO) or LOG(ALL) attribute, can only be opened for read ( mode=r ) because of the CICS file control and logging requirements. Nonrecoverable datasets, that is, LOG(NONE), or the default can be opened in any mode with the LOG(NONE) attribute since no CICS file control or logging is required for these datasets.

share=rls or share=rlsread causes VSAM to ignore any bufnd/bufni/bufsp specification. However, in general, the VSAM RLS feature is transparent from a C library VSAM file function usage standpoint since the protocols are implemented at the operating system level and not in the library except for specifying the ACB options. However, a couple of pitfalls are worth mentioning. File opens with VSAM RLS will fail if there are nonRLS users of the dataset, and conversely nonRLS opens will fail if there are RLS users of the dataset. While OS/390 recovery is usually satisfactory, it is possible for system crashes and/or abends to leave the dataset locked out from nonRLS access, and locked records unavailable to RLS users, until the IDCAMS utility is called to fix the problem.

File creation amparms These amparms are used under OS/390 with filenames specified in dsn or tso style when the file does not exist and must be created. These amparms are accepted under CMS, and for ddn -style names or existing files under OS/390, but in these cases they are ignored.

Note:    VSAM files can be created directly by a C program only in OS/390, and only if the Storage Management Subsystem (SMS) is active. On CMS, or if SMS is not available, VSAM files must be created by the Access Method Services (AMS) utility before they can be accessed by a C program.  [cautionend]

The file creation amparms are as follows:

alcunit=block|trk|cyl|rec|krec|mrec
unit of space allocation

space=nnn
primary amount of space to allocate

extend=nnn
secondary amount of space to allocate

dir=nnn
number of PDS directory blocks

vol=volser
requested volume serial number

unit=name
requested unit name.

rlse=yes|no
release unused file space when file is closed

dataclas=name
data class for a new file

storclas=name
storage class for a new file

mgmtclas=name
management class for a new file

The meanings of these amparms and their defaults are discussed in the following list. Default values are site-specific and may have been changed at the time the SAS/C library was installed. Consult your SAS Software Representative for C compiler products to determine the defaults at your site.

See the IBM publication MVS/DFP Storage Administration Reference for further information on SMS concepts such as data class and storage class.

Amparms - CMS details

This section discusses amparms under CMS. It provides an explanation of each amparm, including default values.

File characteristics For input files, or output files in which some or all of a file's previous contents are preserved, the file characteristics amparms serve as advice to the library about the file characteristics expected. If the actual file does not match the program's assumptions, a warning message is generated. To determine the characteristics of a file, the library tries to merge information from the amparms, the FILEDEF options (for a ddn -style filename), and from an existing file with the same fileid.

The processing for each of the file characteristics amparms is described here. Unless otherwise specified, the description is for CMS disk files.
recfm If recfm=f , the program expects records of equal length. If the file is not a RECFM F file, a warning message is generated. Similarly, if recfm=v , the program expects records of varying length. If the file is not a RECFM V file and the file is opened for write-only or for update, a warning message is generated. recfm=u is treated as if it were recfm=v .
reclen For files with fixed length records, reclen specifies the length of the records. If the record length specified by reclen does not match the LRECL of the file, a warning message is generated. reclen=x cannot be used with fixed format files.

If the file has varying length records, reclen specifies the maximum length of the records. If the LRECL of the file exceeds that specified by reclen , a warning message is generated. reclen=x implies that the records may be of any length up to 65,535.

blksize The blksize amparm is used with the "rel" access method to specify the internal buffer size used by the library, which in turn specifies the number of records read or written by the library in each I/O operation. The blksize for the file should be a multiple of the file's logical record length. If it is not, it is rounded to the next higher multiple.

For files with fixed-length records opened for read only, blksize can also be used with the "seq" access method. In this case, blksize specifies the library's internal buffer size. If the buffer size is larger than the LRECL of the file, each input operation performed by the library reads as many records as can fit into the buffer. For example, if the file has 80-character records, specifying blksize=4000 causes the library to read 50 records at each input operation.

When an existing write-only or update file is opened, and none of the file's previous contents are preserved, the old file is erased and a new file is created. The characteristics of the new file are those specified by the amparms.
recfm recfm=f and recfm=v cause the file to be created as RECFM F or RECFM V, respectively. Again, recfm=u is treated as if it were recfm=v .
reclen specifies the maximum (and minimum, for recfm=f ) logical record length for the file. reclen=x indicates that the records may be of any length up to CMS's maximum of 65,535.
blksize specifies the buffer size used by the library when performing I/O operations on the file.

If the file characteristics are not completely described by the amparms, the FILEDEF options (when the ddn -style filename is used), or the file, the following defaults apply:

File usage The amparm print has two uses: it specifies whether the corresponding file includes ANSI carriage control characters, and it specifies whether the C library should process the control characters '\r' and '\f' to effect page formatting. If print=no is specified, then '\r' and '\f' are simply treated as data characters, even if the file can support page formatting. If you specify print=yes , then the library attempts to perform page formatting, but if the associated file does not have the correct attributes, '\r' and '\f' are treated as new lines. (A warning message is generated when the file is opened in this case.) If neither print=no nor print=yes is specified, the library chooses a default based on the attributes of the external file. However, print=yes is supported only for a file accessed as a text stream.

Under CMS, any disk file may be used with page formatting. If the filetype of the file is LISTING or the file is written to the virtual printer, the file is assumed to require ANSI control characters in the first byte of each record. (If a disk file has control characters in byte 1 and does not have a filetype of LISTING, the CMS command PRINT prints the file incorrectly unless the CC option is used.)

The page amparm, which specifies the number of lines to be printed on each page of a print=yes file, does not have a default. That is, if you do not specify page , page ejects will occur only when a '\f' is written to the file.

The pad amparm specifies whether padding of records will take place, and, if so, whether a blank or a null character ( '\0' ) will be used as the pad character. The default depends on the library access method and the open mode, as follows:

The trunc amparm indicates to the library whether the program requires that output before the end of file erase following data or leave them unchanged. CMS disk files support both trunc=yes and trunc=no . By default, trunc=no is assumed; that is, data are not erased following a modified record. Shared file system files support only trunc=no . All other types of files support only trunc=yes , except for VSAM, which supports only trunc=no . If a program's trunc specification is not supported for the file being opened, the open fails, and a diagnostic message is generated.


File Positioning

As described in Technical Background, the 370 operating systems provide a relatively inhospitable environment for the standard C file positioning functions. For this reason, you should read this section carefully if your application makes heavy or sophisticated use of file positioning. Some understanding of OS/390 or CMS I/O internals is helpful.

The details of file positioning depend heavily on the I/O package and library access method used, the stream type (text or binary), and the file organization and attributes. The following discussion is organized primarily by I/O package.

File positioning with UNIX style I/O

When UNIX style I/O is used to access a file as binary, file positioning is fully supported with the lseek function, which can be used to seek to an arbitrary location in the file. However, when UNIX style I/O is used to access a file as text, the seek address is interpreted in an implementation-specific way, as when the fseek function is used. This means that, for a text stream, you should use lseek only to seek to the beginning or end of a file, or to a position returned by a previous call to lseek .

The lseek function accepts three arguments: a file number, an offset value, and a symbolic value indicating the starting point for positioning (called the seek type). The seek type is interpreted as follows:

If the seek type is SEEK_CUR or SEEK_END , the offset value may be either positive or negative. lseek can be used with a seek type of SEEK_SET and an offset of 0 to position to the start of a file, and with a seek type of SEEK_END and an offset of 0 to position to the end of a file.

Positioning beyond the end of a binary file with lseek is fully supported. Note that positioning beyond the end of a file does not in itself change the length of the file; you must write one or more characters to do this. When you write data to a position beyond the end of file, any unwritten positions between the old end of file and current position are filled with null characters ( '\0' ).

The lseek function returns the current file position, expressed as the number of bytes from the start of the file. It is frequently convenient to call lseek with a seek type of SEEK_CUR and an offset of 0 to obtain the current file position without changing it.

Recall that, except for files suitable for "rel" access and USS HFS files, using UNIX style I/O is relatively inefficient. See Choosing I/O Techniques and File Organization for more information on the advantages and disadvantages of using UNIX style I/O.

File positioning with standard I/O (fgetpos and fsetpos)

Standard I/O supports the fgetpos and fsetpos functions to obtain or modify the current file position with either a text or a binary stream. Both fsetpos and fgetpos accept two arguments: a pointer to a FILE object and a pointer to an object of type fpos_t . For fsetpos , the fpos_t object specifies the new file position, and for fgetpos , the current file position is stored in this object. The exact definition of the fpos_t type is not specified by the ISO/ANSI C standard and, if you intend your program to be portable, you should make no assumptions about it. However, an understanding of its implementation by the SAS/C library can be useful for debugging or for writing nonportable applications.

The library defines fpos_t using the following typedef :

typedef struct {
   unsigned long _recaddr;    /* hardware "block" address */
   long _offset;              /* byte offset within block */
} fpos_t;

The first element of the structure ( _recaddr ) contains the address of the current block or record. The exact format of this value is system- and filetype-dependent. The second element of the structure ( _offset ) contains the offset of the current character from the start of the record or block addressed by _recaddr . In some cases, this offset may include space for control characters.

A more precise definition of these fields for commonly used file types follows.

fsetpos and fgetpos are implemented to be natural and efficient, and not to circumvent limitations or peculiarities of the operating systems. For this reason, you should be aware of the following:


File positioning with standard I/O (fseek and ftell)

In many cases, when you process a file with standard I/O, you can use the fseek and ftell functions to obtain or modify the current file position. Because fsetpos and fgetpos are relatively new additions to the standard C language, fseek and ftell are more portable. However, they are also more restricted in their use. Full fseek and ftell functionality is available only when you use the "rel" access method, when a file is accessed as a text stream, or when the file is in the USS hierarchical file system. For files processed with the "rel" access method and for HFS files, fseek and ftell function exactly like lseek does for UNIX style files.

The fseek function accepts three arguments: a pointer to a FILE object, an offset value, and a symbolic value indicating the starting point for positioning (called the seek type). The offset value is a long integer, whose meaning depends on whether the file is accessed as text or binary. For binary access, the offset value is a number of bytes. For text access, the offset value is an encoded file position whose interpretation is unspecified. The seek type is one of the values SEEK_SET , SEEK_CUR , or SEEK_END , indicating positioning relative to the start of file, the current position, or the end of file, respectively.

When you access a file as binary, the fseek offset value is simply interpreted as an offset from the point specified by the seek type. For instance, fseek(f, -50L, SEEK_CUR) requests positioning 50 characters before the current character.

Note:    Because the fseek offset value has type signed long , only files whose size is less than 2**31 bytes can be supported in a portable fashion. However, for files accessed using the "rel" access method, or stored in the USS HFS, the offset value is interpreted as unsigned long , thus allowing the use of files whose size is less than 2**32-1.  [cautionend]

When you access a file as text, only certain combinations of offset and seek type are meaningful. When the seek type is SEEK_CUR or SEEK_END , only an offset value of 0 is meaningful, requesting no change in positioning or positioning to the end of file, respectively. When the seek type is SEEK_SET , any valid file position previously returned by ftell is accepted as an offset. Additionally, you can use an offset of 0 with SEEK_SET to reposition to the start of the file.

The ftell function accepts one argument, a pointer to a FILE object, and returns a long integer defining the current file position. For a file accessed as binary, the returned file position is the number of bytes from the start of the file. For a file accessed as text, the returned file position is in an internal format.

Note:    For a text stream, ftell is the only safe mechanism for obtaining a file position for later use by fseek ; that is, you cannot construct meaningful file positions in another way.  [cautionend]

ftell computes the encoded file position for a text stream by first calling fgetpos to obtain the file position in fpos_t format. (Thus, if fgetpos is not usable with a file, neither is ftell .) Then, initial portions of the _recaddr and _offset fields of the result are combined in a manner that depends on the file organization. Because the size of an fpos_t value is 8 bytes and the size of a long integer is only 4 bytes, information is lost if either the _recaddr value or the _offset value is too large. The library detects this loss of information and returns an error indication, rather than an incorrect file position. For specific file types, the conditions under which a file position cannot be successfully returned are listed here:

Even for files that exceed one of these limits, ftell returns an error only when the actual file position is outside the limits.

When used with a binary stream, full fseek functionality is restricted to the "rel" access method, but note that fseek with a 0 offset value is usually supported. This means that you can use fseek with almost any file to rewind, position to end of file, or switch between reading and writing. The exceptions are noted in OS/390 Files with Restricted Positioning and CMS Files with Restricted Positioning.

OS/390 Files with Restricted Positioning
File Type Restrictions
terminal positioning not allowed
card reader positioning not allowed
USS pipe positioning not allowed
DD */DATA only fseek with a 0 offset supported
printer/card punch/SYSOUT rewind and seek to the end of file accepted but ignored
keyed VSAM (KSDS) only fseek with a 0 offset supported
PDS member seek to the end of file not supported; switch between reading and writing only supported at the start of file unless grow=no
PDSE member seek to the end-of-file not supported; seeking other than rewind only allowed if read-only or grow=no
RECFM=FBS (accessed as text) seek to end of file not supported
multivolume disk or tape file ("seq") access only rewind supported if not opened for append; only seek to end of file supported if opened for append
unlabeled tape with DISP=MOD only seek to the end of file supported
concatenated sequential files only fseek with a 0 offset supported

CMS Files with Restricted Positioning
File Type Restrictions
terminal positioning not allowed
reader/printer/punch rewind and seek to the end of file accepted but ignored
keyed VSAM (KSDS) only fseek with a 0 offset supported
OS/390 PDS member positioning not allowed with %MACLIB or %TXTLIB filename used

Note:    The warnings given in the previous section for use of fsetpos are equally applicable to fseek .  [cautionend]


Terminal I/O

Performing I/O on an interactive device such as a TSO or CMS terminal is quite different from performing I/O on a disk or tape file. Some programs need to use terminal and nonterminal files interchangeably, while others need to take advantage of the special properties of terminal files. Some specific differences between terminal I/O and other I/O follow.

Note:    The following considerations apply when you read from or write to the TSO or CMS terminal. They do not necessarily apply when you read or write from an USS terminal under the USS shell. The behavior of an USS terminal is defined by the POSIX standards. See the POSIX 1003.1 standard for further information.  [cautionend]


Buffering, flushing, and prompting

Most implementations of standard I/O perform buffered I/O; that is, characters are collected in a buffer and transmitted, one block at a time. This can cause problems for I/O to the terminal. For instance, if a terminal output file is buffered, it is possible for a terminal read to be issued before an output message asking the user to enter the input is transmitted. To write correct and portable interactive programs, it is important to understand the different ways that terminal I/O can be implemented. Some of the possible approaches are as follows:

Another situation that varies from one implementation to another, depending on the buffering strategy, is the effect of writing characters to several terminal files at the same time. In implementations that do not buffer terminal I/O, all the output characters are transmitted in the order that they are written. In an implementation that performs buffering, the output probably consists of complete lines of output, each line associated with a particular file. With the SAS/C library I/O functions, the buffer for one terminal output file is flushed when a character is written to another. This means that characters are transmitted in the same order as they are written and that characters written to two different files do not appear in the same output line.

Automatic prompting

When you use scanf or a similar function to read input from the terminal, it can be difficult to write prompts at all points where terminal input is required. For example, the following call reads two integers from the standard input stream (normally the terminal):

scanf("%d %d", &i, &j)

If only one integer is entered, scanf issues another read to obtain the second integer without returning to the program. This means that the program is unable to issue a prompt or message to tell you that more input is required.

When you open a terminal input file, the library allows you to specify a prompt that will be written by the library immediately before a read is issued to the terminal. This allows each input request to be automatically preceded by a prompt. You can also have more than one terminal input file, each with a different prompt, allowing you to easily distinguish one file from another. This feature is requested with an amparm when you use afopen or aopen to open the file.

Text access and binary access

One of the distinctions between text access and binary access to a nonterminal file is the treatment of control characters. Certain control characters, such as the new-line and form feed characters, may be transformed during text input and output. Binary access is required to transmit control characters without alteration.

The situation is similar for input and output to the terminal. Usually, the terminal is accessed as a text stream. In this mode, the new-line character separates lines, and a form feed issues several blank lines to simulate the effect of a form feed on a printer. Other control characters are removed from input and output. Both TSO and CMS are sensitive to the data sent to the terminal, and incorrect use of control characters can cause the user to be disconnected or logged off. Thus, the removal of control characters is a safety measure that prevents unpleasant consequences when uninitialized data are sent to the terminal.

However, some applications need the ability to send control characters to the terminal. This is supported by accessing the terminal as a binary stream. When a binary stream is used, output data are sent to the terminal without modification, and input data are only minimally edited. The new-line character still separates lines of output, because both TSO and CMS support this use of the new-line character. Note that even though the SAS/C library does not modify data transmitted to and from the terminal when binary access is used, the data can be modified by system software, including the VM control program, VTAM, and communication controller software. Also recall that incorrect data can cause disconnection or other errors, so use this technique with caution.

Terminal I/O under OS/390-batch

Programs that open the terminal for output or append can be run in OS/390-batch. In this case, the output is written to the file defined by the DDname SYSTERM. The SYSTERM data set must have record format VBA and LRECL 137, but any BLKSIZE is permissible.

You may have several terminal output files open simultaneously. In this case, lines written to the various files are interspersed, as they would be if the program were running interactively. Emulation of terminal input under OS/390-batch is not supported.

Terminal I/O amparms

When you use afopen or aopen to open a terminal file, you can specify the following amparms in addition to those discussed earlier in Opening Files:
eof=string specifies an end-of-file string.
prompt=string specifies a terminal input prompt.

The following amparms are ignored if the file to be opened is not the terminal:


Using the * filename under the shell

As an aid to porting existing programs to the USS shell, the SAS/C library allows the filename * in ddn , dsn , or tso style to access the USS terminal. When you use this filename, the eof and prompt amparms are permitted and honored. These amparms are ignored when you open the HFS file /dev/tty . Use of these amparms under the shell is not recommended for new programs, because they are not portable, but they can be useful when porting existing OS/390 programs.

Note:    When a program running under the shell opens the * filename, no distinction is made between text and binary access. The effect of control characters on the display is defined by USS.  [cautionend]


Using the USS Hierarchical File System

The USS Hierarchical File System (HFS) is an implementation of a UNIX file system under OS/390. In this file system, a directory is a special kind of file that contains names and other information about a group of files. The root directory is at the top of the hierarchy; thus, the root directory is not contained in any other directory. Files within the file system are identified by a pathname, which consists of the series of directories (beginning with the root directory) that lead to a file. Directory names are separated by slashes (/), and the filename itself comes last. For example, the pathname /u/marie/tools/wrench.c identifies the file wrench.c , contained in the directory tools , in turn contained in the directory marie , contained in the directory u , which is contained in the root (/) directory. This type of pathname, beginning with a slash connoting the root directory, is called an absolute pathname.

A program using a hierarchical file system always has a current directory defined, either by inheritance from a calling program, or from using the chdir function. A pathname without a leading slash is called a relative pathname. Such pathnames are interpreted relative to the current directory. For instance, if a program's current directory is /u/marie and it attempts to open the pathname tools/wrench.c , the file that is actually accessed is /u/marie/tools/wrench.c .

Note:    When you call the fopen or open function to access an HFS file, it may be necessary to prefix the pathname with the SAS/C style prefix hfs: . See File Naming Conventions for information on when this is required.  [cautionend]

Several different kinds of files exist in the hierarchical file system. Most files are so-called regular files, which are stored on disk (in a special OS/390 file system data set). The hierarchical file system also contains special files of various sorts, which may not have all the properties of regular files. For instance, some special files do not support seeking, or have different behavior when read or written. Some important examples of special files include


Low-level and Standard I/O

I/O to the hierarchical file system is implemented by USS OS/390 via a set of services that correspond to traditional UNIX unbuffered I/O calls, such as open , read , and write . For HFS files, UNIX style I/O functions interface directly to the operating system, bypassing most of the C library's I/O support. This ensures that access to the Hierarchical File System through SAS/C has the same characteristics as access when the operating system interfaces are used directly.

When an HFS file is opened using open , the operating system returns a small integer representing the file, called the file descriptor. All other I/O operations, such as reading and writing, are performed by specifying the file descriptor. File descriptors have two important properties not applicable to more traditional OS/390 files:

Of course, you can use standard I/O functions rather than the low-level functions like open , read , and write to access HFS files. However, program behavior may differ, depending on which set of routines you use. When you use fopen to open an HFS file, it calls the USS open interface, and then saves the resulting file descriptor in a control block accessed via the FILE pointer. Functions such as fread and fwrite read and write data from a buffer area allocated by the library (or by the user if the setvbuf function is used), and actually read from or write to the file descriptor only as necessary to empty or fill the buffer.

For most programs, the buffering performed by standard I/O results in a performance gain, because the program does not need to call the operating system as often. However, for some programs, this can result in unacceptable behavior. For example, programs that share files usually should not use standard I/O because output data may be buffered indefinitely; therefore, updates may not become visible to other programs using the file for an arbitrary amount of time. Similarly, if a program needs to receive an open file from a calling program, it must be aware that only the file descriptor is passed. That is, a FILE pointer is local to the program that creates it, and it cannot be inherited, except under special conditions.

For applications that might need to access a file using both low-level and standard I/O, the POSIX standards define two functions that cross the boundaries:

When a program is called by exec , the library automatically uses fdopen to associate the standard files stdin , stdout , and stderr with file descriptors 0, 1, and 2. Thus, these three files are partial exceptions to the rule stated earlier that FILE pointers cannot be inherited across exec .


USS I/O Considerations

USS support in SAS/C affects I/O in several ways. SAS/C now implements two different file-naming conventions. Also, DD statements can now be allocated to HFS files or directories. Finally, with USS support, you may find it useful to modify some programs to use PDS members and HFS directories interchangeably. These considerations are described in the next three sections.

File Naming Conventions

SAS/C implements two different file-naming conventions: one for use by traditional SAS/C programs, and one for POSIX-oriented programs. The choice of naming convention depends on whether any compilation in the main program load module specifies the posix compiler option. If so, then POSIX file-naming rules apply. If no compilation specifies the posix option, then traditional SAS/C naming conventions apply.

Using traditional SAS/C rules, a filename consists of a style prefix (one to four characters, followed by a colon), followed by the filename proper. The prefix determines how the rest of the filename is to be interpreted (for example, as a DDname or an HFS pathname). If there is no style prefix, then a default prefix is assumed. The default prefix may be defined by the program by initializing the _style external variable. If _style is not initialized, the default is system-dependent. A filename, with or without an explicit style prefix, may be further prefixed by the string // . If // precedes a style prefix, the // is simply ignored. If // is present, but there is no style prefix, then the style tso (in OS/390) or cms (in CMS) is assumed, independent of the _style definition. When these rules are in effect, you must do one of the following to access a file in the HFS:

The rules above are useful for OS/390-oriented programs, or for programs that must open diverse kinds of files. However, they are often not the most appropriate rules for portable applications. Notably, the POSIX.1 standard requires that any pathname not beginning with // be interpreted as a hierarchical file system pathname. For this reason, SAS/C implements alternate conventions to allow the porting and/or development of applications that conform to the POSIX.1 standard and are portable to UNIX operating systems.

These alternate rules apply whenever the main load module of a program contains at least one compilation using the posix compiler option. For such programs, the file-naming conventions are as follows:

Note:    For a program compiled with the posix option, the _style external variable is ignored.  [cautionend]

Note:    Because filenames beginning with // are interpreted in the same way for applications compiled with the posix option as for those compiled without the posix option, this form should be used by any functions that need to open files, and which can be used in programs compiled with or without the posix option. For example, to open the HFS file /u/marie/tools/wrench.c without knowing whether the program was compiled with the posix option, use the filename //hfs:/u/marie/tools.wrench.c . Any such functions must not be compiled with the posix option themselves, because then any program using such functions would automatically follow the naming conventions for programs compiled with the posix option.  [cautionend]

Accessing HFS files using DDnames

Enhancements to OS/390 JCL and dynamic allocation facilities for USS OS/390 allow DD statements to be allocated to HFS files or directories. Parameters on the DD statement correspond roughly to arguments to the open function: the PATH option corresponds to the pathname to be opened, the PATHOPTS option corresponds to the open flags, and the PATHMODE option corresponds to the file creation mode specification.

HFS files can be accessed using a ddn -style filename, as can any other OS/390 file. The following points should be noted:


Using HFS directories and PDS members interchangeably

Until the availability of USS, it was often convenient to replace the use of directories in UNIX applications with PDS's when porting them to OS/390. Consider porting a UNIX C compiler to the mainframe. In UNIX, a system header file like <stdio.h> is simply a file in a particular directory. In OS/390, such names are generally interpreted by treating the first part of the name as a member name, relative to a PDS defined by a DDname. (For example, SAS/C interprets <stdio.h> as ddn:syslib(stdio) ). With the availability of USS, it may be desirable to modify these programs to use a PDS or an HFS directory interchangeably, as convenient for the user. SAS/C provides the following extension to its ddn -style filename handling in support of this. Besides all previously accepted forms, a ddn -style filename may now have the following form:

ddname/filename

Here, ddname is a valid OS/390 filename, and filename is a valid POSIX filename (not containing any slashes). When ddn:ddname/filename is opened, the following occurs:

Note:    When the ddname/filename syntax is used and the DDname references an HFS directory, any PATHOPTS specified on the DD statement apply to the subfile as well. Thus, if DDname SYSLIB specifies PATHOPTS=OWRONLY, opening ddn:syslib/stdio.h using open mode 'r' will fail.  [cautionend]

Using Environment Variables in place of DDnames

When a new process is created by fork or exec , as when a program is called by the shell, a new address space is created with no DD statements allocated other than possibly a STEPLIB. For programs exclusively using UNIX oriented interfaces, this does not present a problem, but it can present difficulties for porting existing OS/390 applications to run under the shell. For this reason, the SAS/C library permits you to substitute environment variables for DDnames in programs invoked by the exec system call.

For a program invoked by exec , if an attempt is made to open a DDname (for example, using the filename //ddn:anyfile ), if no corresponding DD statement exists, the library checks for an environment variable named ddn_ANYFILE . Notice that the prefix ddn is always in lowercase letters, while the DDname proper is always in uppercase letters. The value of the environment variable, if it exists, must have one of two forms:

When a ddn -style filename is opened using an environment variable, the specified DDname is allocated by the library during processing. Thus, if the same program opens the DDname a second time, a DD statement will be found, and the environment variable will not be referenced again. Consequently, changing the environment variable after it has been used to open a file will be ineffective.

Note:    The ddn:ddname/filename pathname format described above can be used both with DDnames defined by an environment variable and with actual DD statements.  [cautionend]

File descriptor allocation

Whenever a file is opened using the open system call, the POSIX.1 standard requires that the call be assigned the lowest file descriptor number that is not in use by an open file. Under USS, the range of valid file descriptors is from 0 to a maximum defined by the site. The default maximum is 64, but it can be set by the site to be as low as 16 or as high as 65,536. The maximum number of open USS files can be determined using the sysconf function.

The limit on the number of open file descriptors is unrelated to the library's limit on the number of FILE pointers that may be opened using standard I/O. This limit is always 256, regardless of the USS limit. File descriptors in the valid USS range can be assigned to files other than USS files in two situations:

In both of these cases, confusion can occur. For example, if file descriptor 4 is assigned to a socket and you call open , USS could assign file descriptor 4 to the newly opened file, and then the library could not distinguish a request to write to file 4 from a request for socket 4.

The library solves this problem using shadow files. Whenever the library needs to assign a file descriptor for a file that is not an USS file, it first opens /dev/null to obtain a file descriptor, which is then assigned to the socket. The shadow file is closed only when the socket or standard file is closed. Because USS associates the file descriptor with /dev/null , it will not be possible for USS to associate the descriptor with any other file. This technique also ensures that socket numbers are assigned in accordance with USS rules.

You should note the following points about file descriptor allocation:


stdin, stdout, and stderr

The C language definition specifies that when program execution begins, three standard streams should be open and available for program use. These are stdin , the standard input stream, stdout , the standard output stream, and stderr , the standard error stream. A number of C library functions, such as puts and scanf , are defined to use stdin or stdout automatically, without requiring you to explicitly specify a FILE pointer. Note that the standard streams are always opened for text access.

stdin , stdout , and stderr are implemented as macros, not as true variables. For this reason, you cannot assign them new values. If you want to reopen one of the standard streams, you must use the freopen or afreopen function rather than fopen or afopen .

Whether the standard streams are actually used is determined by the program, with one exception. Library diagnostic messages are written to stderr , if it can be opened successfully and is suitable for output. If stderr is unavailable, library diagnostics are written to the terminal under CMS or TSO, and to the job log under OS/390-batch.

Under CMS, all three standard streams are, by default, directed to the terminal. Under OS/390, the default filenames for stdin , stdout , and stderr are ddn:sysin* , ddn:sysprint* , and * , respectively. stdin uses the DDname SYSIN, if it is defined, and the terminal, otherwise. Similarly, stdout uses SYSPRINT, if it is defined, and the terminal, otherwise. stderr is directed to the terminal or to the DDname SYSTERM, if running in batch.

For a program running under USS OS/390, by default stdin , stdout , and stderr are defined as file descriptors 0, 1, and 2, as passed by the calling program. If one or more of these file descriptors is not open in the calling program, any attempt to use the corresponding standard file in the called program will fail, unless it opens the appropriate file descriptors itself.

Under OS/390, it is possible for one or more of the standard streams to fail to open. For instance, in batch, stdin cannot be opened unless you define the DDname SYSIN, and stderr cannot be opened unless you define the DDname SYSTERM. To avoid generating an "open failure" error message for a file that is never used, the library delays issuing a system open for a standard stream until it is first used. Note that opening a file under OS/390 requires significant memory. For this reason, if you write to a standard file when your program runs out of memory (for instance, when malloc fails), you may want to force the file to be opened earlier, as by writing an initial new line at a time when enough memory is known to be available.

Changing standard filenames at execution time

Because the standard streams are initialized by the library before execution rather than by an explicit call to fopen , there is no direct way to change the filenames associated with them. For this reason, C implementations traditionally support command-line redirection. This permits the user of a program to specify on the command line (that invokes the program) the filenames to be associated with standard input and output streams. For example, the CMS command line "xyz <ddn:input > printer" invokes the program XYZ, requesting that ddn:input be used as the filename for stdin , and that printer be used as the filename for stdout . Redirection is described in detail in the SAS/C Compiler and Library User's Guide. Additionally, you should be aware of the following considerations:


Changing standard filenames and characteristics at compile time

Besides supporting command-line redirections, the library enables you to change the names of the standard files at compile time, or to specify amparms to be used when the files are opened. Thus, you can override some of the library defaults. If the program specifies a replacement filename and the command line includes a redirection for the same file, the filename specified on the command line is used.

To change the default name for a standard file, you must initialize an external char * variable with the filename to be used. The external variables are _stdinm , _stdonm , and _stdenm for stdin , stdout , and stderr , respectively. For example, the following declaration specifies that by default, _stdinm should read from the user's virtual reader:

char *_stdinm = "cms:reader";

The _stdinm , _stdonm , and _stdenm specifications are honored even for programs called with exec . Thus, using these variables, you can override the standard use of file descriptors 0, 1, and 2 for these files if you wish. If you do this, the standard file descriptors are not closed, and can still be accessed directly via the file descriptor number.

Similarly, you can assign an initial value to the external variables _stdiamp , _stdoamp , or _stdeamp to specify the amparms to be used when stdin , stdout , or stderr is opened. The library default amparms are shown in Default Amparms for the Standard Files:

Default Amparms for the Standard Files
File Amparms
stdin prompt=pgmname:\n
stdout print=yes
stderr print=yes, page=60

You may want to override these default amparms in the following situations:


Using the standard streams with UNIX style I/O

In UNIX operating systems and other similar systems, it is possible to access the standard streams using low-level I/O, specifying file numbers 0, 1, and 2 for stdin , stdout , and stderr , respectively. The library supports such access, provided that certain guidelines are followed. This usage is nonportable. The following restrictions apply:


I/O Error and Interrupt Handling

UNIX style I/O includes no specific error-handling functions or features. If a read , write , or lseek call fails, the only indication is the value returned by the function. Depending on the error, it may be possible to continue to use the file after the error occurs.

Error handling

As stated earlier, after a file has been opened, a pointer to a FILE object is used to identify the file. This pointer is passed to I/O routines such as fread and fwrite to indicate the file to be read or written. Associated with each FILE object is a flag called the error flag that indicates whether the most recent I/O request failed. When the error flag is set, it is not possible to use the file other than to close it or to call the clearerr function to clear the flag.

The error flag for a file is set whenever an error occurs trying to access a file. The flag is set for all types of errors, whether they are hardware errors (such as an unreadable tape block), errors detected by the operating system (such as a full CMS minidisk), or errors detected by the library (such as trying to read a write-only file). In addition to setting the error flag, the library also writes a diagnostic message to the stderr stream and sets the errno external variable to indicate the type of error that occurred.

The function ferror can be called to determine whether the error flag is set for a file. Using this function is sometimes necessary because some functions, such as fread , do not distinguish in their return values between error conditions and end of file.

If you want to continue processing a file after an error occurs, you must call the clearerr function to clear the error flag; that is, to cancel the effect of the previous error. Some errors (such as running out of disk space under OS/390) are so severe that it is impossible to continue to use the file afterwards without reopening it. In such cases, clearerr is unable to clear the error, and continued attempts to use the file cause new errors to be generated.

I/O and signal processing

In a program that handles asynchronous signals, it is possible for a library I/O routine to be interrupted by a signal. When a library I/O routine is interrupted, an interrupt flag is set for the file until the signal handler returns. Any attempt to use the file while the interrupt flag is set is treated as an error (and therefore sets the error flag) to avoid damage to the file or to library file control blocks. The situations in which the interrupt flag is most likely to be set are after using longjmp to exit from a signal handler, or when a signal handler performs I/O to a file in use at the time of the signal. When the interrupt flag is set, you can call clearerr to clear it along with the error flag and continue to use the file.

For terminal input under OS/390 and CMS (except with USS), the system calls do not allow signals to be detected while the program is waiting for terminal input, with one exception. The SIGINT signal, which is an attention interrupt under OS/390 or an IC immediate command under CMS, terminates the terminal read and causes any handler to be called immediately. If your SIGINT handler needs to read from the terminal, you should use a different FILE pointer from the one used by the rest of your program; otherwise, the error flag is set for the file, as described in the previous paragraph. If you must use the same FILE pointer in mainline code and in your handler, you need to call clearerr in the handler before reading from the terminal and call it again after exit (either by return or by longjmp ) from the handler.


Augmented Standard I/O

Some 370 I/O applications are beyond the scope of standard I/O because the record concept is absent from the C language. Consider, for example, a program to make an exact copy of any input file, including duplicating the input file's record structure. Such a program could not be written using binary file access because all information about the record structure of the input file would be lost. It also could not be written using text access, because if there were any new-line characters in the input file, they would be interpreted by the program as record breaks, and the output file would contain more records than the input file. The functions afread , afread0 , afreadh , afwrite , afwrite0 , and afwriteh have been defined to permit this sort of application to be written in C. These functions, together with afopen , afreopen , and afflush are known as augmented standard I/O.

afread and afwrite can only be used with binary streams. Because they are used with binary streams, they never translate or otherwise modify input or output data, even if the data include control characters. afread and afwrite are useful only when the "seq" access method is used, because a file processed with the "rel" access method is treated as a stream of characters without record boundaries. If you need to process files with fixed-length records using afread or afwrite , you should open the file with afopen , and request the use of the "seq" access method.

afread and afwrite

The afread and afwrite functions are very similar in form to the standard fread and fwrite functions: they accept as arguments a pointer to the input or output area, the size of the type of object to be read or written, the maximum number of objects, and the FILE pointer identifying the file. But, unlike fread and fwrite , whose purpose is simply to read or write the items specified without regard to record boundaries, the purpose of afread and afwrite is to read or write the items specified as a single record. Specifically, afread and afwrite read and write items as follows:

afread and afwrite do not support zero-length records. On input, a zero-length record is ignored, and similarly, an attempt to write a zero-length record is ignored. Two alternate functions, afread0 and afwrite0 , are provided. These functions can handle zero-length records, if the file being processed supports them. To support zero-length records, afread0 and afwrite0 use error-reporting conventions that are not compatible with the standard C fread and fwrite functions.

afread and afwrite do not require that the file be positioned to a record boundary when they are called. Also, you can freely mix calls to afread and afwrite with calls to other standard I/O routines, such as fscanf or fseek , if your application requires it. See the function descriptions for afread and afwrite for examples of their use.

afreadh and afwriteh

afreadh and afwriteh enable you to read or write a header portion of a record before calling afread or afwrite to process the remainder. This is useful for reading or writing files processed by another language (such as COBOL or Pascal) that supports variant records.

A variant record is a record composed of two parts, a fixed format part and a variable format part. The fixed format part contains information common to all records, and a field defining the length or structure of the remainder of the record. Depending on the situation, it may not be possible to read or write such records conveniently using afread and afwrite . (Defining the records to be processed as a C union is helpful only if all the different variants are the same size.) afreadh and afwriteh support processing such records in a straightforward way:

See the function descriptions for afreadh and afwriteh for examples of their use.


Advanced OS/390 I/O Facilities

This section discusses several advanced I/O tasks under OS/390, such as reading a PDS directory, recovering from ABENDs, PDSE access, and processing DIV objects.

Reading a partitioned data set directory

You can read a PDS directory sequentially by allocating the entire library to a DDname, and specifying the DDname without a member name, as the filename. For instance, you can use the following TSO code fragment to open the directory of SYS1.MACLIB for input:

system("tso:alloc file(sysmacs) da('sys1.maclib') shr");
direct = fopen("ddn:sysmacs", "rb");

You can also access the PDS directory by opening the PDS using a "dsn" - or "tso" -style name, and specifying the amparm "org=ps" , as in

direct = afopen("dsn:sys1.maclib", "rb", "seq", "org=ps");

The directory is treated by the library as a RECFM=F, LRECL=256 data set, regardless of the attributes of the members.

You cannot use C I/O to modify a PDS directory. Also, access to a PDS directory is supported using only ddn -style filenames, unless the org amparm is used. If you specify a PDS using a dsn - or tso -style filename without an org specification, and no member name is present, the member name TEMPNAME will be used.

Recovering from B37, D37, and E37 ABENDs

When an I/O operation requires additional space to be allocated to a file but space is unavailable, the program is normally terminated by the operating system with a B37, D37, or E37 ABEND. The SAS/C library intercepts these ABENDs and treats them as error conditions. It sets the error flag for the affected file and returns an error code from the failing I/O function. The ABEND is intercepted using a DCB ABEND exit, not a STAE or ESTAE, and functions correctly even if you use the nohtsig run-time option to suppress the library's ESTAE.

When the library recovers from one of these ABENDs, the file is automatically closed by the operating system. For this reason, the error flag is set permanently; that is, you cannot clear the flag with clearerr and continue to use the file. An exception is made by the "rel" access method, which reopens the file if you use clearerr to clear the error condition. This enables you to read or modify data you have already written, but you cannot add any more records to the file, because this simply will cause the ABEND to reoccur.

Although other kinds of I/O errors are quite rare, these out-of-space ABENDs occur frequently, even for production programs. Therefore, you should always check output operations for success to avoid loops when trying to write to a file that can no longer be accessed.

Using a PDSE

Recent releases of OS/390/ESA have introduced a new implementation of extended partitioned data sets, called a PDSE (Partitioned Data Set Extended). These files are compatible with ordinary PDS data sets, but have a number of advantages, including the following:

The SAS/C Library includes support for PDSEs. Most programs that presently access PDS members can access PDSE members without change.

Restrictions

Although PDSEs are compatible in most ways with standard PDSs, they do not support either BSAM INOUT or OUTIN processing, which enable a member to be read and written at the same time. When the fseek or fsetpos functions are used on a PDS member, the library depends on this processing, except for a read-only file. For this reason, the use of fseek or fsetpos on a PDSE member is not supported unless the member is read-only, or unless you specify grow=no . One exception is that fseek(f, 0L, SEEK_SET) can always be used to reposition a PDSE member to the start of file.

Note:    When a PDSE member is accessed through UNIX style I/O in binary mode, this restriction does not apply. In this case, full use of the lseek function for repositioning is supported.  [cautionend]

Access via the grow= amparm

The SAS/C Library defines the amparm grow , which can be specified when a file is opened for 'r+' or 'r+b' . You specify grow=no to inform the library that the program will only replace existing records of a file, rather than adding any data to the end. When you specify grow=no for a PDSE member, the library can open the member for UPDAT rather than OUTIN and can then support use of either the fseek or fsetpos function.

The grow amparm is also supported for standard PDS members, and it should be used where possible, because it performs an update-in-place action, and avoids wasting the space in the PDS occupied by the previous member.

Allocating PDSEs

When a new partitioned data set is created, the decision to create it as a regular PDS or as a PDSE is normally determined by your site, possibly based on data set name or other data set characteristics. In some cases, you may want to force a particular choice. The org amparm supports this. org has more uses than just PDS allocation. See Opening Files for more information.

When you use the afopen function to create a new PDS, you can specify one of three values for org
po specifies that the file is a PDS and that normal site criteria should be used to select between a regular PDS and a PDSE.
pds specifies that the file should be created as a regular PDS.
pdse specifies that the file should be created as a PDSE.

Note:    A site may choose to ignore a program's request for a particular type of PDS, although this is fairly unusual. For this reason, it cannot be guaranteed that org=pds or org=pdse will be honored in all cases. If your operating-system level does not support PDSEs, the org values pds and pdse will be treated like the value po .  [cautionend]

Using VSAM linear data sets (DIV objects)

A DIV object is different from other OS/390-type data sets. Essentially, it is a single stream of data with no record or block boundaries. The operating system processes the file in 4096-byte units with paging I/O, mapping the data in the file to virtual storage referred to by the program (all of which is transparent to the program). For more information on DIV objects, see the IBM manual MVS/ESA Application Development Guide.

You can access DIV objects using the ordinary C library I/O functions and the "rel" access method. Two amparms are available for use with VSAM linear data sets. These amparms are not required, but they allow the program to direct the internal buffering algorithm used by the library:
bufsize=nnn specifies the size, in bytes, of a DIV window.
bufmax=n specifies the number of DIV windows.

The value specified for bufsize is rounded up to a multiple of 4096. The default value for bufsize is bufsize=262144 (256K). The default value for bufmax is bufmax=4 . These default values can be modified by your site; see your SAS software representative for C compiler products for more information about the default values for bufsize and bufmax . This discussion assumes the default values have not been modified.

DIV windows The library allocates one window when the object is opened. This window is mapped to the beginning of the object. When a reference is made to a location that is outside the bounds of the window, the library allocates a new window that maps the location.

New windows can be allocated, until the number specified by bufmax is reached. Then, if a reference is made to a location that is not mapped by any window, the library remaps the least-used window to the new location. The least-used window is the window that has the fewest references made to locations that it maps.

If the limit specified by bufmax has not been reached, but there is insufficient storage available to allocate a new window, the library issues a warning and begins remapping existing windows.

How the amparms are used As with other amparms, the linear data set amparms may be specified in the amparms argument to afopen or aopen . If one of the amparms is omitted, then the library uses its default value.

If a linear data set is opened with fopen or open , or neither amparm is used, then bufsize is calculated from the object size divided by 4, rounded to a multiple of 4096 as necessary. If the data set has size 0 (that is, the data set is empty), the default values are used. If there is insufficient storage available to allocate the first window, the library issues a warning and uses whatever storage is available.


Advanced CMS I/O Facilities

This section discusses several advanced I/O tasks under CMS, such as use of xed style files, extending global MACLIB/TXTLIB processing, and using the CMS shared file system.

The xed filename style

The CMS version of the library supports access to files being processed by XEDIT with the xed filename style. xed style filenames have the same format as cms -style filenames, for example, xed:payroll data a . The filename must identify a CMS disk file. That is, you cannot specify device names such as PRINTER. Also, you cannot use the MEMBER keyword (or its abbreviated format equivalent).

You can use the xed style even when XEDIT is not active. In this case, or when the file requested is not in the XEDIT ring, the file is read from disk.

See the system function description and Chapter 2, "CMS Low-Level I/O Functions," in SAS/C Library Reference, Volume 2 for information on other facilities that may be useful for programs that use XEDIT files.

Extensions to global MACLIB/TXTLIB processing

As described previously, you can use the cms -style filenames %MACLIB (MEMBER name ) and %TXTLIB (MEMBER name ) to access members of global MACLIBs or TXTLIBs. Global MACLIBs and TXTLIBs are established using the CMS GLOBAL command. Here is an example:

GLOBAL TXTLIB LC370 MYLIB1 MYLIB2

When %TXTLIB(name) is opened, the libraries LC370 TXTLIB, MYLIB1 TXTLIB, and MYLIB2 TXTLIB are searched, in that order, for the member name . Also, the library implements several extensions to standard CMS GLOBAL processing to support larger numbers of global libraries than allowed directly by the CMS GLOBAL command. These extensions also support the use of OS partitioned data sets as global libraries.

One extension to GLOBAL processing enables you to issue a FILEDEF using the DDname CMSLIB and then include CMSLIB in the list of files for the GLOBAL command. This causes the files associated with the CMSLIB DDname to be treated as global. For example, if you issue the following commands, the same set of libraries as in the previous example is defined, and the effects of opening %TXTLIB(name) are the same:

FILEDEF CMSLIB DISK MYLIB1 TXTLIB A
FILEDEF CMSLIB DISK MYLIB2 TXTLIB A (CONCAT
GLOBAL TXTLIB LC370 CMSLIB

One advantage of using the FILEDEF approach is that the FILEDEF may be concatenated, enabling you to bypass the limit of eight global libraries imposed by early versions of CMS. Another is that you can put OS partitioned data sets into the global list (in a non-XA system), as described in the following section. Note that when CMSLIB is concatenated, the global search order is the same as if CMSLIB were replaced in the global list by the files that compose it, in the order in which they were concatenated.

The special processing of the CMSLIB DDname is a feature of the SAS/C library; the DDname CMSLIB has no special significance to CMS.

Using an OS PDS as a global MACLIB/TXTLIB If your site permits CMS access to OS disks, the CMSLIB FILEDEF for use in a global list may reference an OS partitioned data set. The FILEDEF must have the following form:

FILEDEF CMSLIB DISK filename MACLIB fm DSN OS-data-set-name TXTLIB

The filemode (fm) cannot be an asterisk (*) and must refer to an OS disk. The PDS referenced must have fixed-length blocked records with an LRECL of 80. The PDS must reside on a 3330, 3350, or 3380 disk device.

Note:    You cannot use an OS PDS as a global MACLIB/TXTLIB in a XA-mode virtual machine.  [cautionend]

Using the CMS Shared File System

VM/SP Release 6 introduced a new file system into CMS called the Shared File System (SFS). This file system provides new file management and sharing capabilities. SFS files are stored in a file pool (a collection of CMS minidisks) where users are given space to organize files into directories. Directories enable users to group related files together. By granting read or write authority to files or directories, users can allow other users to share their files. This feature enables several users to have access to the same file at the same time, although only one user can update a shared file at a time.

When a shared file is open for update, the file system provides update access to a copy of the file. Changes to the file do not take effect until the changes are committed. Alternately, after updating a file, the user can roll back the changes, which leaves the file unmodified. If a user opens a shared file for reading while another user is updating it, the reading user accesses a temporary copy of the data and can read only the data in the file at the time it was opened, even after the writing user commits changes.

Shared files can be accessed as if they were normal CMS disk files by using the CMS ACCESS command, which can assign a filemode letter to an SFS directory. Currently, use of unique SFS functionality, such as access to subdirectories and the ability to roll back changes, is not available with the CMS ACCESS command. These features are only available when the Shared File System is used directly.

The SAS/C Library allows access to the Shared File System directly and via the ACCESS command. If you use the ACCESS command to assign a file-mode letter to an SFS directory, files in the directory can be accessed using standard CMS pathnames. Alternately, a shared file can be processed directly by using an sf -style filename. For example, opening the following file accesses the file SUMMARY DATA in the directory of userid ACCTING named YR90.JUNE:

sf:summary data accting.yr90.june

When a shared file is processed directly, it can be committed automatically as it is closed, or the file can be committed explicitly using the afflush function.

You can also process an SFS directory as if it were a file (for input only) by using an sfd -style filename. This enables you to retrieve various sorts of information about the files or subdirectories stored in the directory. The way in which information is returned is controlled by the dirsearch amparm.

SFS files can be processed with either the "seq" or "rel" access method, if the file attributes are appropriate. Except for trunc=yes (which is not allowed), all amparms that can be used with cms -style files can be used with an sf -style file. SFS directories can be opened only for input, and are always processed by the "seq" access method.

For more general information about using the Shared File System, see the IBM publication VM/ESA CMS User's Guide and other CMS documentation.

Naming shared files

The format of the name of a shared file, as specified to fopen , is

sf:fileid dirid [filemode-number]

(You can omit the sf: prefix if the external variable _style has been set to define sf as the default style.)

Here fileid represents either a standard filename and filetype, or a namedef, which is an indirect reference to a filename and filetype created by the CMS CREATE NAMEDEF command.

Similarly, a dirid represents the following:

[filepool]:[userid].[subdir1.[subdir2]...] namedef

The filepool argument identifies a file pool; userid identifies a user; subdir1, subdir2, and so on name subdirectories of the user's top directory; and namedef is an indirect reference to a directory created by the CMS CREATE NAMEDEF command. Note that every dirid that is not a namedef contains at least one period. The simplest dirid is "." (which represents the current user's top directory in the default file pool).

Here are some examples of sf filenames and their interpretation:

sf: profile exec .
specifies the file PROFILE EXEC in the current user's top directory.

sf: updates amy.
specifies the file identified by the namedef updates in the top directory of user AMY.

sf: test data qadir 3
specifies the file named TEST DATA in the directory identified by the namedef qadir . The file has file mode 3; that is, it will be erased after it is read.

sf: graphix data altpool:.model.test
specifies the file named GRAPHIX DATA in the user's subdirectory MODEL.TEST in the file pool named ALTPOOL.

Note:    There is no compressed (blankless) form for sf filenames.  [cautionend]

Committing changes

When you open an sf -style file for update, you control when changes are committed. Two methods are provided to control when changes are committed: the afflush function and the commit amparm.

The afflush function is called to flush output buffers to disk with high reliability. For SFS files, a call to afflush causes a commit to take place, so that all changes to the file up to that point are permanently saved.

The commit amparm is used with sf -style files to specify whether changes will be committed when the file is closed. The default, commit=yes , specifies that when the file is closed, changes are committed. The alternative, commit=no , specifies that changes are not committed when the file is closed. When you open a file with commit=no , you must call afflush before closing the file if you want changes saved. On the other hand, if you want to roll back your changes, close the file without calling afflush , and no changes will be saved. You can call afflush as often as you want, with either commit=yes or commit=no ; when you close a commit=no file, all changes since the last call to afflush are rolled back. See the afflush function description for an example of the use of commit=no .

Reading shared-file directories

To process a CMS Shared File System directory, you open an sfd -style pathname for input. The pathname specifies the directory to be read, and possibly a subset of the directory entries to be read. The format of the information read from the file, as well as which entries (files and subdirectories) are processed, is determined by the value of the dirsearch amparm when the file is opened.

The following values are accepted for dirsearch :
file specifies that information is to be read for files in the directory. This option corresponds to the FILE option of the CMS DMSOPDIR routine.
all specifies that information is to be read for files in the directory or its subdirectories. This option corresponds to the SEARCHALL option of the CMS DMSOPDIR routine.
allauth specifies that information is to be read for files in the directory or its subdirectories to which the user is authorized. This option corresponds to the SEARCHAUTH option of the CMS DMSOPDIR routine.
subdir specifies that information is to be read for subdirectories of the directory. This option corresponds to the DIR option of the CMS DMSOPDIR routine.

If you specify no value for dirsearch when you open a shared-file directory, dirsearch=allauth is assumed.

When you open a directory with dirsearch=file , dirsearch=all , or dirsearch=allauth , the pathname specifies both the directory that is to be read and a filename and filetype, possibly including wild-card characters, indicating from which directory entries are to be read.

The form of an sfd -style filename for these dirsearch values is

sfd: fileid dirid [filemode-number]

If fileid has the form filename filetype, the filename, filetype, or both can be specified as *, indicating that the filename and/or filetype is not to be considered while reading the directory. If filemode-number is specified, only entries for files with the specified mode number are read.

Here are a few examples of sfd -style filenames for use with dirsearch=file , dirsearch=all , or dirsearch=allauth :

sfd: * * .
specifies that entries for all files in the user's top directory are to be read.

sfd: * exec devel
specifies that entries for all files with the filetype EXEC in the directory identified by the namedef devel are to be read.

sfd: * * mike.backup 2
specifies that entries for all files with filemode number 2 in the subdirectory BACKUP, belonging to the user MIKE, are to be read.

When you open a directory with dirsearch=subdir , the pathname specifies only the directory that is to be read. This format of sfd: -style filename is also used when you call remove , rename , access , cmsstat , or sfsstat for a Shared File directory.

Here are a few examples of sfd -style filenames to use with dirsearch=subdir :

sfd: .
specifies that entries for all subdirectories of the user's top directory are to be read.

sfd: master
specifies that entries for all subdirectories of the directory associated with the namedef master are to be read.

After you open a Shared File System directory, you read it as any other file. The data you read consist of a number of records, one for each matching file if dirsearch=subdir is not specified, and one for each subdirectory if

dirsearch=subdir is specified. Mappings for the formats of these records are provided in the header file <cmsstat.h> . The following are more exact specifications:

dirsearch=file
specifies that the records are mapped by a portion of struct MAPALL . Only the first L_file bytes of this record are actually read.

dirsearch=all or dirsearch=allauth
specifies that the records are mapped by struct MAPALL . The number of bytes read is L_all .

dirsearch=subdir
specifies that the records are mapped by struct MAPDIR . The number of bytes read is L_dir .

These structures generally contain binary data. Therefore, if new-line characters appear in the data, you should open sfd files for binary rather than text access to avoid possible confusion. Also, none of the seeking functions fseek , ftell , fsetpos , or fgetpos are supported for Shared File directories.

Here is a simple example that opens a directory with dirsearch=file to print out the names of the files in the directory:

#include <lcio.h>
#include <cmsstat.h>

struct MAPALL dir_data;

main() {
   FILE *dir;
   int count = 0;
      /* Open top directory for input. */
   dir = afopen("sfd: * * .", "rb", "", "dirsearch=file");

   if (!dir) abort();
   for(;;) {
      fread(&dir_data, L_file, 1, dir);  /* Read one entry. */
      if (ferror(dir)) abort();
      if(feof(dir)) break;
      if (count == 0)     /* Write title before first line. */
         puts("Files in directory .:");
      ++count;
      printf("%8.8s %8.8s\n", dir_data.file.name,
      dir_data.file.type);
   }

   printf("\n%d files found.\n", count);
   fclose(dir);
   exit(0);
 }


Using VSAM Files

VSAM (Virtual Storage Access Method) is a file organization and access method oriented toward large collections of related data. Ordinary OS/390 and CMS files are organized as a sequential collection of characters, possibly grouped into records. To locate a particular record, it is necessary to read the entire file, until the appropriate record is found. VSAM files are organized according to keys that serve as record identifiers. This makes VSAM especially useful and efficient for many large-scale data processing applications.

For example, suppose you have a file containing a record for each employee in a company. If the data are stored in a normal OS/390 or CMS file, to update a single record you must read the entire file, until the correct record is found. Alternatively, the data can be stored in a VSAM file, using employee name or employee number as a key. With this organization, a program can immediately read and update any record, given the key value, without having to read the rest of the file.

VSAM files are described in more detail in the IBM publication MVS/DFP Using Data Sets. Consult this publication for a more complete description of VSAM files and services.

Kinds of VSAM files

Four kinds of VSAM files exist: KSDS, ESDS, RRDS, and LDS files. Following are the characteristics of these files:

KSDS (Key-Sequenced Data Set)
is a VSAM file in which each record has a character key stored at some offset in each record. Each record must have a unique key. Records are stored and retrieved in key sequence. They can be added to or deleted from the data set in any order; that is, you can freely add new records between existing records. When records are modified, the length of the record can change, but the value of the key cannot be changed. Most VSAM files are KSDS files.

KSDS files can have alternate indices, which are auxiliary VSAM files that provide access to records by a key field other than the primary key. Access to records by an alternate index is accomplished by a path, which is an artificial data set name assigned to the combination of the base KSDS and the alternate index. An alternate index can be defined for a KSDS using a nonunique key field. For example, an employee file cannot have last name as its primary key because more than one employee may have the same last name. But a path to the employee file can use last name as its key, allowing quick access, for instance, to the records for all employees named Brown.

ESDS (Entry-Sequenced Data Set)
is a VSAM file in which each record is identified by its offset from the start of the file. This offset is called a relative byte address (RBA). When a record is added to the file, it is added to the end, and the RBA of the new record is returned to the program. The RBA thus serves as a logical key for the record. ESDS data sets can have records of different lengths, but when a record is replaced, the length must not be changed. Also, records cannot be deleted.

Like KSDS files, ESDS files can have alternate indices that provide keyed access to the records of the ESDS. For instance, you can choose to make an employee file an ESDS file, with the records arranged by the order in which they were added to the file. You can still access records in this file using last names by building an alternate index with last name as the key over the ESDS. The same rules apply when you use a path to access an ESDS as when you access the ESDS directly; that is, you cannot change the length of a record or delete a record. Except for these restrictions, a path over an ESDS is treated as a KSDS because records accessed through the path are always arranged in the order of the alternate keys.

RRDS (Relative-Record Data Set)
is a VSAM file in which each record is identified by record number. Records can be added or deleted in any order, and the file can have holes; that is, it is not necessary that all possible record numbers be defined. Records in an RRDS file are normally all the same length; SAS/C does not support RRDS files with variable-length records. Alternate indices over an RRDS are not supported.

LDS (Linear-Data Set)
is a VSAM file consisting solely of a sequence of characters divided into 4096-byte pages. Unlike other VSAM files, LDS files are not keyed, and normally are accessed via the supervisor DIV (Data In Virtual) service, as described in the IBM publication MVS/ESA Application Development Guide. LDS files are supported only under OS/390.


Access to VSAM files using standard I/O

You can access all kinds of VSAM files using the standard C I/O functions defined by <stdio.h> . Because of the special characteristics of VSAM files, there are restrictions for some types of VSAM files:


Keyed access to VSAM files

Because standard C I/O assumes that files are simply sequences of characters stored at unchanging offsets in the file, standard C is not suited to exploiting VSAM capabilities. For this reason, the SAS/C library provides a keyed-access mode for VSAM files designed to take advantage of their unique properties. Keyed access is an alternative to text or binary access, specified by the open mode argument to the fopen or afopen function, as shown in the following statements:

ftext = fopen("ddn:ESDS", "r+");   /* Open ESDS for text access.   */
fbin  = fopen("ddn:ESDS", "r+b");  /* Open ESDS for binary access. */
fkey  = fopen("ddn:ESDS", "r+k");  /* Open ESDS for keyed access.  */

Only a subset of the standard I/O functions (shown in the following list) are available for files opened for keyed access; that is, this list shows the functions that make sense for such files.
afflush flush output buffers, synchronize file access
clearerr reset previous error condition
clrerr reset previous error condition
fattr return file attributes
fclose close file
feof test for end of file
ferror test for error
ffixed test for fixed-length records
fnm return filename
fterm test whether file is the terminal
setbuf change buffering (null operation)
setvbuf change buffering (null operation).

The following keyed-access functions are supported only for VSAM files. These functions are described in more detail later in this section:
kdelete delete the last record retrieved
kgetpos return position of current record
kinsert add a new record
kreplace replace the last record retrieved
kretrv retrieve a record
ksearch locate a record
kseek reposition keyed file
ktell return RBA of current record.

Keyed access is not supported for VSAM LDS files because these files are not divided into records and have no keys.

Records and keys

Keyed-access functions for VSAM process one record at a time, rather than one character at a time. Most functions have arguments that are pointers to records or pointers to keys. Because C is not, in general, a record-oriented language, you need to be careful when defining data structures for use with VSAM.

Many VSAM files have fixed-length records, where all records have the same format. These files are easy to process in C, because the record can be represented simply as a structure, as shown in this simple example:

struct {
   char name[20];
   char address[50];
   short age;
}  employee;

kretrv(&employee, NULL, 0, f);

This example reads records of a single type from a VSAM file. More complicated files may have records with different lengths or types; C unions can be helpful in processing such files:

struct personal {
   char name[20];
   char rectype;     /* = P for personal */
   char address[50];
   short age;
} ;
struct job {
   char name[20];
   char rectype;          /* = J for job */
   long salary;
   short year_hired;
}
union {
   struct personal p_rec;
   struct job j_rec;
}  employee;

kretrv(&employee, NULL, 0, f);

The call to the kretrv function can read a record of either type; then the rectype field can be tested to determine which type of record was read. Here is an example showing the replacement of a record with several types:

if(employee.p_rec.rectype == 'P')
   recsize = sizeof(struct personal);
else recsize = sizeof(struct job);
kreplace(&employee, recsize, 0, f);

If the length were specified as sizeof(employee) and the record were a job record, more data would be written than defined in the record, and file space is wasted.

Any characters can occur in a record. Neither new-line characters ('\n') nor null characters ('\0') have any significance.

For a KSDS file, a record key is always a fixed-length field of the record. Any characters can appear in a key, including the new-line character ('\n') and the null character ('\0'). Also, the key is not restricted to being a character type; for some files, the key might be a long , a double , or even a structure type.

When you have character keys, you should be sure to specify all characters of the key. For instance, consider the following call to the ksearch function, intended to retrieve the record whose key is Brown in the employee file described previously:

ksearch("Brown", 0, K_exact, f);

This search will probably fail, because the key length for this file is 20 characters. The ksearch function looks for a record whose key is the characters "Brown" , followed by a null character, followed by 14 random characters (whatever comes after the string "Brown" in memory), and probably will not find one.

Usually, strings in VSAM files are padded with blanks, so the following example shows the correct usage:

char key[20];
memcpyp(key, "Brown", 20, 5, ' ');  /* Copy key and blank pad. */
ksearch(key, 0, K_exact, f);

ESDS and RRDS files do not have physically recorded keys. However, the RBA (for an ESDS) and the record number (for an RRDS) serve as logical keys for these files. The structures representing these records in a C program must include an unsigned int or unsigned long field at the start of the record to hold the key value. This key is not actually recorded in the file. In this example, record 46 is inserted into an RRDS:

struct {
   unsigned long recno;
   char name[20];
   char address[60];
}  rrds_rec;

rrds_rec.recno = 46;
kinsert(&rrds_rec, sizeof(rrds_rec), NULL, rrds);

The key (46) is specified in the first 4 bytes of the record. Note that the key is not actually stored in the file. The size of the C record is 84 characters, but the length of the record in the VSAM file is 80 characters because the key is not physically recorded.

Rules for keyed access

The keyed-access functions are record-oriented rather than character-oriented. When a keyed file is used, it can be in one of the following states:

Unlike some other kinds of files, your program can open the same VSAM file more than once. The same file can be opened any number of times, either using the same filename, or using different names. A file can also be opened via several paths. The open modes do not need to be the same in this case. For example, one stream can access the file for input only, and another can access to append. However, each opening of the file must specify keyed access; that is, standard I/O and keyed I/O to the same file cannot be mixed.

When several streams access the same VSAM file, only one of them can hold a particular record for update. If one stream attempts to retrieve a record held by another stream and K_noupdate is not specified, retrieval will fail. Because of the way that VSAM holds records for update, it is also possible for two streams accessing the same file to interfere with each other's processing of different records. See VSAM pitfalls for more information.

Using a KSDS

The following are considerations unique to processing a KSDS:


Using an ESDS

The following are considerations unique to processing an ESDS:


Using an RRDS

The following considerations are unique to processing an RRDS:


Using an alternate path

The following are considerations unique to processing an alternate path. An alternate path to a KSDS or an ESDS is treated, in general, as a KSDS, except that some operations are forbidden for a path whose base is an ESDS:


VSAM pitfalls

The VSAM access method is highly optimized for performance when processing large files. Sometimes this optimization can produce incorrect results for programs that process the same VSAM data set using more than one FILE or more than one path. This section describes some of these situations and suggests how to circumvent them. These pitfalls apply only to programs that access the same file in several ways. Simple VSAM programs that open each VSAM file only once are not affected.

Note:    Sharing of VSAM files between users through SHAREOPTIONS(3) or the SHAREOPTIONS(4) Access Method Services (AMS) option is not supported by the SAS/C library. If you ignore this restriction, lost records, duplicate records, or file damage may occur.  [cautionend]

When the same file is accessed through several paths, a problem can occur when VSAM attempts to avoid reading a record from disk because a copy exists in memory. If a request is made to read a record and the record is not to be updated, VSAM may return an obsolete copy of the record from memory to the program, rather than reading a current copy from disk. If this is a problem for your application, you can always retrieve records for update by not specifying K_noupdate , regardless of whether you intend to update them. This ensures that you get the most recent version of a record at the cost of additional disk I/O. But note that you cannot retrieve a record for update if you open the file with open mode 'rk' . Alternately, you can use the afflush function to flush all buffers in memory. After using afflush , retrievals access the most recent data from disk, because there is no copy in memory.

Many programs cannot be affected by this problem. For example, if your program processes records in key order, it probably does not ever attempt to retrieve a record after it has been updated. For such a program, there is no need to avoid the use of K_noupdate on retrieval.

Another potential problem has to do with the way that VSAM stores records. Records in VSAM files are organized into control intervals of a fixed size. Each disk access consists of the reading or writing of an entire control interval. When VSAM is said to be holding a record, the control interval is actually what is held. This means that an attempt to read a record for update using one stream may fail, because another record in the same control interval is held by another stream. In general, when this may occur cannot be predicted, although records whose keys are close to each other are more prone to this condition.

This condition can be recognized by the setting of the value EINUSE for the errno variable. Resolving the condition is more difficult. It is possible to release a hold on a record without updating the record by the call kseek(fp, SEEK_CUR) . This call does not change the file position, which means that kretrv can be used to retrieve the next record, as if kseek had never been called. Also, sometimes you may be able to organize your program so that it normally retrieves records using K_noupdate and, if the program decides that the record should be modified, retrieves the record a second time for update immediately before replacing or deleting it.

VSAM-related amparms

For some VSAM files, processing performance can be improved by allocating storage for additional I/O buffers. SAS/C allows you to specify buffer allocation for VSAM files using the following amparms. (Note that the order amparm can also have an effect on performance.

A valid bufsp specification generally overrides any bufnd or bufni specification. However, the VSAM rules for doing this are fairly complex, and you should consult the appropriate IBM VSAM Macro Reference manual for your system for more information on the ACB macro BUFSP option.

VSAM I/O Example

This example consists of three pieces of code. The first piece is the SAS/C VSAM example program; the second piece is the JCL used to create, load, and update the VSAM file; and the third piece is the JCL used to compile and link the VSAM example.

The SAS/C VSAM example program, KSDS , demonstrates how to load, update, search, retrieve, and delete records from a KSDS VSAM file. Two VSAM files are used:
ddn:ITEM the VSAM file being used
ddn:DATA where records for initially loading the VSAM file are stored

Two data files are used:
ddn:UPDATE contains the records for loading and updating
ddn:DELETE contains the keys for the records being deleted

#include <stdio.h>
#include <lcio.h>
#include <fcntl.h>

#define BUFSIZE 80
#define VBUFSIZE 50
#define KEYSIZE 19

void loadit(void);           /* Load a VSAM file.                  */
void update(void);           /* Update a VSAM file.                */
void printfil(void);         /* Print a VSAM file.                 */
void add_rep(void);          /* Add or update specific records.    */
void del_rec(void);          /* Delete specific records.           */

FILE *vfptr1,                /* ptr to the VSAM file               */
     *vfptr2,                /* another ptr to the VSAM file       */
     *fptr;                  /* ptr to the DATA file               */

char buffer[BUFSIZE+1];      /* buffer for reading input file data */
char vbuffer[VBUFSIZE+1];    /* VSAM record buffer                 */
char key[KEYSIZE+1];         /* key field for VSAM record          */
main()
{
   unsigned long offset;
      /* If VSAM file has been loaded, update.  Otherwise LOAD.    */
   puts("Has the VSAM file been loaded?");
   quiet(1);
      /* Attempt to open the file r+k.  If that works, then it     */
      /*  is loaded.  The open could fail for reasons other than   */
      /*  that the file has not yet been loaded.  In this case,    */
      /*  loadit will also fail, and a library diagnostic will     */
      /*  be printed.                                              */
   vfptr1 = afopen("ddn:ITEM", "r+k", "", "keylen=19, keyoff=0");
   quiet(0);

   if (!vfptr1){
      puts("File has not been loaded. Load it.");
      loadit();
      }
   else{
      puts("File has been loaded. Update it.");
      update();
      }

      /* Show the current state of the VSAM file. */
   printfil();

      /* Est. 2nd ptr to VSAM file.  */
      /*Search and print specific records. */
   if ((vfptr2 = afopen("ddn:ITEM", "r+k", "", "keylen=19, keyoff=0"))
      == NULL){
      puts("Could not open VSAM file a 2nd time");
      exit(99);
      }
      /* Search for some specific records by key. */
   puts("\n\nDo some searching");
   memcpy(key, "CHEMICAL FUEL      ", KEYSIZE);
   key[KEYSIZE]='\0';       /* Terminate the key. */
   printf("Search for %s\n", key);
   ksearch(key, 0, K_noupdate | K_exact, vfptr1);
   memcpy(key, "HOUSEHOLD PAN      ", KEYSIZE);
   key[KEYSIZE]='\0';       /* Terminate the key. */
   printf("Now Search for %s\n", key);
   ksearch(key, 0, K_noupdate | K_exact, vfptr2);

      /* Retrieve the records found. */
   puts("\n\nOK, now retrieve the records that we found");
   kretrv(vbuffer, NULL, K_noupdate, vfptr1);
   vbuffer[VBUFSIZE]='\0';
   puts(vbuffer);
   kretrv(vbuffer, NULL, K_noupdate, vfptr2);
   vbuffer[VBUFSIZE]='\0';
   puts(vbuffer);

   fclose(vfptr2);

      /* Find the first and last records in the file and their RBA. */
   kseek(vfptr1, SEEK_SET);
   kretrv(vbuffer, NULL, K_noupdate, vfptr1);
   vbuffer[VBUFSIZE]='\0';
   printf("\nThe first record in the file is:\n%s\n", vbuffer);
   offset = ktell(vfptr1);
   printf("Its RBA is: %lu\n", offset);
   kseek(vfptr1, SEEK_END);
   kretrv(vbuffer, NULL, K_backwards | K_noupdate, vfptr1);
   vbuffer[VBUFSIZE]='\0';
   printf("\nThe last record in the file is:\n%s\n", vbuffer);
   offset = ktell(vfptr1);
   printf("Its RBA is: %lu\n", offset);
}

   /* This is the loadit function, which does the initial */
   /*  loading of the VSAM file.                          */
void loadit()
{

   puts("Loading the VSAM file");
   if ((fptr = fopen("ddn:DATA", "rb")) == NULL){
      puts("Input file could not be opened");
      return;
   }

      /* We must attempt to open the file again.  Since we are here, */
      /*  we know that the first attempt failed, probably because    */
      /*  the file was empty.                                        */

   if ((vfptr1 = afopen("ddn:ITEM", "a+k", "", "keylen=19, keyoff=0"))
      == NULL){
      puts("VSAM file could not be opened");
      return;
   }

   while (afread(buffer, 1, BUFSIZE, fptr)){
      kinsert(buffer, VBUFSIZE, NULL, vfptr1);
   }
}


/* The following function updates the VSAM file by calling functions */
    to add, replace, and delete records.                             */

void update()
{
   puts("Updating the VSAM file");
   printfil();
   add_rep();
   del_rec();
}
   /* The add_rep function updates the VSAM file by adding or  */
   /*  replacing records specified in DDN:UPDATE.              */

void add_rep()
{

   puts("\nUpdating specified VSAM records");
   if ((fptr = fopen("ddn:UPDATE", "rb")) == NULL){
      puts("Update file could not be opened");
      return;
   }
   puts("\n");

      /* Search VSAM file for records whose keys match those in */
      /*  UPDATE file. If a match is found, update record.      */
      /*  Otherwise, add record.                                */

   kseek(vfptr1, SEEK_SET);
   while (afread(buffer, 1, BUFSIZE, fptr)){
      memcpy(key, buffer, KEYSIZE);
      if ((ksearch(key, 0, K_exact, vfptr1)) > 0){
        kretrv(vbuffer, NULL, 0, vfptr1);
        vbuffer[VBUFSIZE]='\0';
        printf("Replace the record:\n%s\n", vbuffer);
        memcpy(vbuffer, buffer, VBUFSIZE);
        vbuffer[VBUFSIZE]='\0';
        printf("With:\n%s\n", vbuffer);
        kreplace(vbuffer, VBUFSIZE, vfptr1);
      }
      else{
        memcpy(vbuffer, buffer, VBUFSIZE);
        vbuffer[VBUFSIZE]='\0';
        printf("Can't find this record, so we'll add it:\n%s\n",vbuffer);
        kinsert(vbuffer, VBUFSIZE, NULL, vfptr1);
      }
   }
   fclose(fptr);
}

   /* The del_rec function deletes VSAM records               */
   /* with specified keys.                                    */                                                   |

void del_rec()
{
   puts("\nDeleting specified VSAM records");
   if ((fptr = fopen("ddn:DELETE", "rb")) == NULL){
      puts("Delete file could not be opened");
      return;
   }

      /* Search VSAM file for records whose keys match those in */
      /*  DELETE file. If a match is found, delete record.      */
      /*  Otherwise, issue a message that no match was found.   */                             |
   kseek(vfptr1, SEEK_SET);
   while (afread(buffer, 1, BUFSIZE, fptr)){
      memcpy(key, buffer, KEYSIZE);
      key[KEYSIZE]='\0';
      if ((ksearch(key, 0, K_exact, vfptr1)) > 0){
        kretrv(vbuffer, NULL, 0, vfptr1);
        vbuffer[VBUFSIZE]='\0';
        printf("Delete the record:\n%s\n", vbuffer);
        kdelete(NULL, vfptr1);
      }
      else
        printf("Couldn't find a record with the key: %s\n", key);
   }
   fclose(fptr);
}


   /* The function printfil prints the contents of the VSAM file. */
                              |
void printfil()
{
   int i=0;

   puts("\n\nHere is the current state of the VSAM file");
   puts("   ITEM             QTY SZ BIN DESC    COMMENTS  ");
   kseek(vfptr1, SEEK_SET);
   while ((kretrv(buffer, NULL, K_noupdate, vfptr1)!=-1) &&
         !feof(vfptr1)){
      buffer(|VBUFSIZE|) = '\0';
      printf("%d %s\n", i, buffer);
      i++;
   }
}

The JCL file KSDSGO creates a KSDS VSAM file, loads it using the above program ( KSDS ), and then updates it using KSDS .

Note:    The DEFINEIT step will fail the first time if there is no VSAM file to delete. You must either comment out the DELETE step the first time or create a dummy VSAM file that can be deleted before this job is run:  [cautionend]

//*------------------------------------------------------------------
//* DEFINE THE VSAM KSDS
//*------------------------------------------------------------------
//DEFINEIT EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=A
//SYSIN DD *
 DELETE (yourid.ksds.vsamfile) PURGE CLUSTER
 DEFINE CLUSTER (NAME(yourid.ksds.vsamfile) INDEXED VOLUMES(YOURVOL) -
              TRACKS(2 2) KEYS(19 0) RECORDSIZE(50 100)             -
              FREESPACE(0 0) CISZ(512) )                            -
       DATA  (NAME(yourid.ksds.vsamfile.DATA))                      -
       INDEX (NAME(yourid.ksds.vsamfile.INDEX))
 LISTCAT ENTRIES(yourid.ksds.vsamfile) ALL
/*
//*------------------------------------------------------------------
//* LOAD THE VSAM KSDS USING SAS/C
//*------------------------------------------------------------------
//LOADIT EXEC PGM=KSDS
//STEPLIB DD DSN=your.load.dataset,DISP=SHR
//        DD DSN=sasc.transient.library,DISP=SHR
//SYSPRINT DD SYSOUT=A
//SYSTERM  DD SYSOUT=A
//SYSUDUMP DD SYSOUT=A
//ITEM     DD DSN=yourid.ksds.vsamfile,DISP=SHR
//DATA     DD  *
AUTO STARTER       99 02 92  WARRANTY  MODERATE
CHEMICAL ACID      05 04 00  PH10      MODERATE
CHEMICAL EAAAAA    55 75 50  ALCOHOL   CHEAP
CHEMICAL FUEL      45 77 80  DIESEL    EXPENSIVE
CHEMICAL GAS       10 30 50  LEADED    CHEAP
HOUSEHOLD PAN      03 10 33  METAL     CHEAP
/*
//*
//*------------------------------------------------------------------
//* OBTAIN AN IDCAMS LISTCAT
//*------------------------------------------------------------------
//IDCAMS3  EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=A
//SYSIN DD *
  LISTCAT ENTRIES(yourid.ksds.vsamfile) ALL
/*
//*
//*------------------------------------------------------------------
//* ADD/UPDATE/DELETE RECORDS TO THE VSAM KSDS
//*------------------------------------------------------------------
//UPDATE EXEC PGM=KSDS
//STEPLIB DD DSN=your.load.dataset,DISP=SHR
//        DD DSN=sasc.transient.library,DISP=SHR
//SYSPRINT DD SYSOUT=A
//SYSTERM  DD SYSOUT=A
//SYSUDUMP DD SYSOUT=A
//ITEM     DD DSN=yourid.ksds.vsamfile,DISP=SHR
//UPDATE   DD  *           LIST OF RECORDS TO BE ADDED OR UPDATED
CHEMICAL FUEL      45 77 80  GASOLINE  CHEAP
CHEMICAL FUELS     55 67 81  PROPANE   CHEAP
/*
//DELETE   DD  *           LIST OF KEYS OF RECORDS TO BE DELETED
AUTO STARTER
BOGUS ID
/*
//


SAS/C I/O Frequently Asked Questions

The following are frequently asked questions about SAS/C I/O:

Flushing output to disk

Q. My program runs for days at a time. I want to be sure that all data I write to my files are actually stored on disk in case the system fails while the program is running. fflush does not seem to guarantee this. What can I do?

A. It is true that using fflush on a file does not guarantee that output data are immediately written to disk. For a file accessed as a binary stream, fflush passes the current output buffer to the system I/O routines, but there is no guarantee that the data are immediately transmitted. (For instance, under OS/390 the data are not written until a complete block of data has been accumulated.) This situation is not limited to OS/390 and CMS. For instance, fflush under most versions of UNIX simply transfers the data to a system data buffer, and there is no guarantee that the data are immediately written to disk.

Even after output data are physically written to disk, they may be inaccessible after a system failure. This is due to the details of the way that disk space is managed under OS/390 and CMS. For instance, under CMS, the master file directory for a minidisk maps the blocks of data associated with each file. If a program adds new data blocks to a file, but the system fails before the master file directory is updated, the new blocks are inaccessible. CMS only updates the master file directory when a command terminates, or when the last open file on the disk is closed by CMS. A similar problem occurs under OS/390 with the end-of-file pointer in the data set label, which is updated only when the file is closed.

The recommended solution to this problem is the nonportable SAS/C function afflush . This function is similar to fflush but guarantees that all data has been written to disk and that the master file directory or VTOC has been updated to contain current information. For programs using UNIX style I/O, the function fsync can be used in the same way.

Comparing C standard I/O to other languages' I/O

Q. I have compared a small C program with a small COBOL program. Both programs simply copy records from one file to another. Why is C standard I/O so much slower than COBOL, and is there anything I can do about it?

A. A simple COBOL file copy and a simple C file copy are not comparable. COBOL I/O can be implemented much more efficiently on the 370 than C standard I/O because the COBOL I/O model is a record I/O model, corresponding closely to the I/O model implemented by the operating system. COBOL clauses such as "RECORD CONTAINS 80 CHARACTERS" and "BLOCK CONTAINS 40 RECORDS" allow the compiler to generate code that invokes the operating system's access methods directly.

C standard I/O, on the other hand, is stream-oriented. The C library cannot call the access method directly because of the need to support text file translation and complicated interfaces like ungetc , fflush , and fseek . Even if the program does not use these capabilities, the library must still support them. In addition, because of the requirement that compiled code be system independent, and because the meanings of attributes like reclen and blksize differ from OS/390 to CMS, C I/O cannot be optimized based on knowledge of these attributes, as COBOL can.

The SAS/C OS and CMS low-level I/O functions enable you to access files at the same low level used by COBOL. When you use low-level I/O in C, you will find performance similar to that of COBOL. If you do not want to use low-level I/O, then refer to the next question in this section.

Note:    The discussion here applies when you compare C I/O to other languages such as PL/I. However, the difference is not as great because these languages also compare unfavorably to COBOL, and for the same reason: their I/O models are not as close to the 370 model as COBOL's, although they are closer than the C model.  [cautionend]

Efficient I/O

Q. What can I do to improve the performance of I/O?

A. Here are some recommendations for improving the performance of I/O. Each recommendation should be evaluated individually because many are not relevant to every application:


Processing character control characters as data

Q. How can I process the carriage control characters as data in a file defined as RECFM=xxA (where xx is F, FB, V, or VB)?

A. Process the file using binary I/O. When you process a record format A file with text I/O, the library manages the carriage control for you, so it can correctly handle C control characters like '\f' and '\r' . But when you use binary I/O, the library leaves the data alone, so you can process the file as you see fit.

Processing SMF records

Q. How can I best process SMF records using C I/O?

A. The library functions afreadh and afread are well-suited to reading SMF records. Simple SMF records consist of a header portion containing data items common to all records, including a record type, followed by data whose format is record-type dependent. Complex SMF records may contain subrecords that occur a varying number of times. For instance, a type 30 record contains an I/O subrecord (or section) for each DD statement opened by a job. To process a simple SMF record in C, use afreadh to read the common record header, and then use afread to read the remainder. The length specified for the call to afread is the largest length possible for the record type. ( afread returns the amount of data actually read.)

To process a complex SMF record in C, use afreadh to read each section of the record, using information from previous sections to allow you to map the record. For instance, if the record header indicates you are reading a type 30 record, then you would call afreadh again to read the common header for a type 30 record. This header may indicate you have three sections of type A and two of type B. You can then call afreadh three times to read the A sections, and two more times to read the B sections.

Note:    Using afread to read any of the nonheader information is not necessary.  [cautionend]

Compatibility between OS/390 and CMS

Q. What can I do to make my I/O portable between OS/390 and CMS?

A. You hardly need to do anything special at all. The main source of I/O incompatibility between OS/390 and CMS is filename syntax. By default, filenames are interpreted as DDnames under OS/390, but as CMS disk filenames under CMS. Furthermore, CMS and OS/390 naming conventions are different. Here are three possible strategies for solving this particular problem (there may be more):

After you have solved the filename problem, you will find that your I/O applications move effortlessly between CMS and OS/390.

File creation

Q. I call the creat function, followed by close , to create a file without putting any data in it. But when I open it read-only later, I get a message that the file doesn't exist. What is wrong?

A. You attempted to create an empty file; that is, one containing no characters. CMS does not permit such files to exist. Additionally, for reasons explained in detail in Technical Background, under OS/390, empty files with sequential organization are treated by the library as not existing. The ISO/ANSI C Standard permits this interpretation because of the existence of systems like CMS.

You can avoid this restriction in two ways:


Diagnostic Messages

Q. I do not want the library to issue diagnostic messages when I/O errors occur, because my application has complete error-checking code. How do I suppress the library messages?

A. You can use the quiet function to suppress all library diagnostics or to suppress diagnostics at particular points in execution. If you use quiet , you may occasionally run into errors whose cause cannot be immediately determined. When this happens, you can use the =warning run-time option to override quiet and obtain a library diagnostic without having to recompile the program.

Converting an Assembler VSAM Application

Q. I'm converting an assembler VSAM application to SAS/C, and I need to know the return code set by VSAM when a function like kretrv or kinsert fails. How can I find this information?

A. When the SAS/C library invokes an operating system routine, such as a VSAM macro, and the macro fails, information about the failure is saved in a system macro information structure. You can access the name of the macro that most recently failed via the library macro __sysmi_macname and its return code via __sysmi_rc . For more information on this facility, see System Macro Information.

Sharing an Output PDS

Q. When I open a PDS member for output, the fopen call fails if another user has the PDS allocated, even if it is allocated as SHR. How can I write to the PDS if it shared with another user?

A. If more than one user writes to the same PDS at the same time, the results are unpredictable. Generally, both members will be damaged. For this reason, when a PDS member (or any other OS/390 data set) is opened for output, the library allocates the data set to OLD to make sure that no one else writes to it at the same time. In some cases, this may be overprotective, but it prevents file damage from unintended simultaneous access.

In your application, if you are certain that only one user can open the file for output at a time, you should access the file through a DDname rather than through a data set name. You can define the DDname using JCL or a TSO ALLOCATE command as SHR, and the library will not alter this allocation when the DDname is opened. In TSO, you can use the system function to allocate a data set to a specific DDname. Also, in any environment, you can use the osdynalloc function to dynamically allocate the data set.

Note:    With a PDSE, it is possible to simultaneously write to distinct members. Even with a PDSE, the effects are unpredictable if the same member is opened by more than one user for output at the same time.  [cautionend]


Chapter Contents

Previous

Next

Top of Page

Copyright © 2001 by SAS Institute Inc., Cary, NC, USA. All rights reserved.