4.5 Getting Started



Dlopen(C-3) opens a logical RMSfile for further activity. The logical RMSfile must already exist for dlopen to succeed. The file pointer returned must be saved and used for calls to other RMS functions. A sample dlopen call to open the subscription file might be:

sfp = dlopen ("script", "r");

This opens the logical file script for reading. The pointer returned into sfp must be used on other calls that refer to the subscription file. If any errors occur during the call to dlopen, the file is not opened and a NULL pointer is returned.

Legal values for access mode are:

"r" - open the file for reading only

(all find functions, dreadc, dread)

"w" - open the file for writing only zero out all records before returning

(dinsert, dupdate, ddelete, dwrite)

"a" - (append) open the file for writing only

(dinsert, dupdate, ddelete, dwrite)

"u" - open the file for both reading and writing

(all functions)

The function names listed in parentheses are the only function calls allowed for the given mode. Attempting to read from a file that is opened for just writing returns an error. Likewise, writing to a file that is opened for just reading returns an error.

If a logical file is opened for some kind of writing ("w", "a", "u") and changes to the file are to be logged, the log file is opened at this time also.

Dclose(C-3) closes an RMSfile to further activity. Each RMSfile opened with dlopen(C-3) or dopen(C-3) must be closed with dclose before your program exits. This is especially important for programs that are updating RMSfiles.

Closing an RMSfile ensures that any output that might have been deferred is written to the file. It also makes an entry in the database log file indicating when the RMSfile is closed. The database restore program, drestore(C-1), can not process RMS definition file changes (like those done by convertlf(C-1), expanddf(C-1), etc.) if it thinks RMSfiles are still open.

When an RMSfile is closed, the memory space for buffers and field lists is released (see free(3)). An application that requires a lot of program memory might recognize a savings by closing an RMSfile as soon as all processing in that file is complete. A sample dclose call might be:

dclose (sfp);

This closes the logical file pointed to by sfp which, in the examples, is the subscription file.

Before continuing, we need to explain the concept of a user record and the actual file record. The actual file record is the record as it appears in the RMS data file. This includes the user data fields plus some information RMS needs to maintain the file. The user record is something logically created by the program. The buffer space for the user record must be provided by the program, and the actual file records are invisible to the program. In essence, each program always works with its view of how the RMSfile records are formatted; which may or may not match the format of the actual RMS data file record.

Dblist(C-3) declares which fields are to be accessed and in what order they are to appear in the user record. A program may elect to access all the fields in an RMSfile or perhaps only a subset of the fields. The order of fields in the user record does not have to match the physical order of fields in the actual RMS data file record. RMS ensures that each field is placed in the proper location when information is transferred between the user record and the RMS data file record. A sequence to call dblist might be:

#include <stdio.h>

#include <cbase/dirio.h>

#include <cbase/dtypes.h>

/* in global definitions area */

char *scriptlist[] = {

"subscriber", "magazine",

"started", "issues",

0 };

struct scriptrec {

STRING subscriber[16],

magazine[16];

DATE started;

INT issues;

} scriptbuf;

DFILE *sfp;

/*

:

:

*/

init() /* part of initialization code */

{

if ((sfp = dlopen ("script", "r")) == NULL) {

puts (derrmsg());

printf ("can't open subscription file\n");

exit (1);

}

if (dblist (scriptlist, sfp) < 0L) {

puts (derrmsg());

printf ("bad field list for subscription file\n");

dclose (sfp);

exit (1);

}

}

In this example, the user record contains the fields subscriber, magazine, started, and issues. The buffer space for the user record is scriptbuf.

Dblist returns the size of the user record. If this size is less than zero, there was an error in the list of field names. If an error condition is returned, the program should not make further access to the RMSfile; RMS has rejected the field list and any further activity will reference the entire record (not the subset specified in the dblist call).

There may be several calls to dblist for one RMSfile. Each time dblist is called, the list of fields to be delivered to the user record is set to the new list specified.

If dblist is not used for an RMSfile, the contents of the record as stored in the RMS data file are delivered to the user record. This requires that the program have a buffer that exactly matches the format of the data record as stored in the RMS data file. Any changes to the data file would require changes to the program. With dblist, fields not in the specified list can be changed with no ill effects on the program.

While dblist is simple to use, it has the drawback that the program is still sensitive to changes made to the fields used by the program. Drlist(C-3) is designed to further insulate programs from file changes. The basic concept of drlist is identical to dblist: your program is declaring the user record. Drlist differs in that your program can also describe field data types and lengths in the user record:

#include <stdio.h>

#include <cbase/dirio.h>

#include <cbase/dtypes.h>

/* in global definitions area */

#define SZ(s,m) (sizeof(((s *)0)->m))

typedef struct scriptrec {

STRING subscriber[16],

magazine[16];

DATE started;

INT issues;

} ScriptRec;

ScriptRec scriptbuf;

DR scriptlist[] = {

{"subscriber", STRING_TYPE, SZ(ScriptRec,subscriber), 1, DRCONVPRE}, {"magazine", STRING_TYPE, SZ(ScriptRec,magazine), 1, DRCONVPRE}, {"started", DATE_TYPE, SZ(ScriptRec,started), 1, DRCONVPRE}, {"issues", INT_TYPE, SZ(ScriptRec,issues), 1, DRCONVPRE},

{0}

};

DFILE *sfp;

/*

:

:

*/

init() /* part of initialization code */

{

if ((sfp = dlopen ("script", "r")) == NULL) {

puts (derrmsg());

printf ("can't open subscription file\n");

exit (1);

}

if (drlist (scriptlist, sfp) < 0L) {

puts (derrmsg());

printf ("bad field list for subscription file\n");

dclose (sfp);

exit (1);

}

}

In this example, the structure for the user record is identical to the one used by dblist. The list of fields is where drlist differs. Where dblist used an array of char pointers, drlist uses an array of DR structures. Each DR structure describes one field in the user record. The first element of this structure names the field in the RMSfile. The second element gives the data type of the user record field . The third element gives the size of the user record field. The fourth element states how many elements there are in the user record field . The last element of the DR structure states what data conversions are permitted between the user record and the actual file record. The acceptable values are given in the table below:

Value Conversion

DRNOCONV No conversion allowed. Data types, sizes, and number of elements must match exactly.

DRCONVPRE Element sizes may differ on STRING_TYPE or CHAR_TYPE fields, but values must be preserved.

 

DRCONVTRUNC Element sizes may differ on STRING_TYPE or CHAR_TYPE fields, values may be truncated to fit.

Number of elements may differ between user record and RMSfile. Data may be lost.

When you specify a conversion of DRCONVPRE in a field list, RMS calls that pass data into and out of a user record field may fail due to lengths of string fields. This can occur only when the sizes of the user record field and the RMSfile field are different.

Using drlist makes application programs more robust. For this reason, drlist is used throughout the remainder of the examples in this chapter.