1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
|
librms is work-in-progress at the moment but the aim is to provide
good (whatever that means) and easy access (whatever *that* means) to RMS
files on VMS machines from Linux.
The API is a hybrid of the RAB/FAB SYS$OPEN/SYS$GET interface of RMS itself
and the open/read/write interface of Unix. In all cases the FAB or RAB
arguments to the functions below are optional for straightforward sequential
access but some basic use of RABs(at least) will be necessary to read/update
indexed files.
The rms_* functions are accessed by an opaque structure RMSHANDLE returned by
rms_open. All the functions, apart from rms_open, take this structure as their
first argument. You should not assume anything about the value of this
structure or its content as I may well change it in future.
rabdef.h and fabdef.h have been provided unmodified from the GNU C distribution
for VMS and are installed into /usr/include.
You will need to compile your programs with the -fdollars-in-identifiers switch
(otherwise you will get loads of compilation errors!) and also link with
libdap. You may also need to link with the C++ libraries because libdap is
written in C++. However, your applications can be in 'just' C.
The "text based" API allows more user-friendly access to RMS files with a
VAXC/PASCAL like interface - see near the bottom of this file for more
information.
There are example.c and example_t.c files provided which do nothing very
useful but may serve as a guide to using the functions.
I recommend that RABs and FABs be cleared using memset (or allocated using
calloc) before use because, although the whole structure is not currently
used, I may add functionality in the future that uses fields that are
currently unused.
For full information on the values in RABs and FABs see the VMS manual
"Record Management Services Reference".
Please let me know what you think of this library and any other functions
you think you might need. It's not (very) difficult for me to add features
because all the hard work has already been done in libdap.
THE API
-------
>>> RMSHANDLE rms_open(char *name, int mode, struct FAB *);
Open an RMS file on a VMS system. The name should include the remote node
name and any necessary access control in the usual format. mode is the
usual Unix open mode but may be overridden or augmented by the contents of
the FAB structure if it is not NULL. The return value is an opaque handle that
should be passed to the other API functions. If rms_open() fails it returns
NULL and you can call rms_openerror to get the text explaining the failure.
FAB fields honoured:
fab$b_fac
fab$b_shr
fab$b_rfm
fab$b_rat
The following fields will be filled in on return:
fab$b_org
fab$l_alq
fab$w_deq
fab$b_rat
fab$b_rfm
fab$l_jnl
fab$w_mrs
fab$l_mrn
fab$b_fsz
>>> char *rms_openerror(void);
Returns the text of the error caused by the last rms_open failure. If no
failure has occurred then NULL will be returned.
>>> int rms_close(RMSHANDLE h);
Close the file referenced by the handle.
>>> int rms_read(RMSHANDLE h, char *buf, int maxlen, struct RAB*);
Read the next record in the file. If fields are set in the RAB then
the system may lookup a record on the file by key, record number or RFA and
return that.
RAB fields honoured
rab->rab$b_rac
rab->rab$l_rop
rab->rab$l_kbf Key value
rab->rab$b_ksz If this is 0 then strlen will be called on the key value
rab->rab$b_krf Key number
Returns -1 if the function failed for any reason, or a positive number
indicating the number of bytes read. 0 indicates end-of file only if
rms_lasterr() contains "EOF". if not then you have just read an empty record!
The best way to check for an empty record is (res == 0 && !rms_lasterr()). If
you get a return of 0 and rms_lasterr() is non-NULL then you have reached the
end of the file.
If the record just read is larger than 'maxlen' then the return value will be
the negative value of the actual size of the record and buf will not
be altered. You should then call rms_read() again with a buffer big enough to
hold this record.
>>> int rms_find(RMSHANDLE h, struct RAB *);
Same as rms_read() but no data is read. Used for positioning within the file.
>>> int rms_write(RMSHANDLE h, char *buf, int maxlen, struct RAB*);
Add a record to the file (rms $PUT).
RAB fields as for rms_read()
>>> int rms_update(RMSHANDLE h, char *buf, int maxlen, struct RAB*);
Update a record to the file (rms $UPDATE).
RAB fields as for rms_read()
>>> int rms_delete(RMSHANDLE h, struct RAB *);
Delete a record to the file (rms $DELETE).
RAB fields as for rms_read()
>>> int rms_rewind(RMSHANDLE h, struct RAB *);
Rewind the file back to the beginning.
>>> int rms_truncate(RMSHANDLE h, struct RAB *);
Truncate a sequential file here.
>>> char *rms_lasterror(RMSHANDLE);
Returns the text of the last error that occurred on this handle.
>>> int rms_lasterrorcode(RMSHANDLE);
Returns the DAP status code of the last error that occurred on this handle.
Be aware that RMS warnings or status messages can cause rms_write to return
-1 so after any write operation it is wise to call rms_lasterrorcode() and
check for errors that are not really errors. One example of such an "error"
is adding a record start creates a duplicate secondary key. The write
operation has completed successfully but returns -1.
librms should check for these conditions but there are rather a lot of them
so currently it doesn't. Sorry.
THE TEXT_BASED API
------------------
This is an attempt at a more friendly access to RMS for those more familiar
with VAXC or PASCAL. It replaces all that nasty, messy RAB/FAB business with
a text string containing the options you want. You pay a processing overhead
for this convenience, of course, but its probably minimal compared to the
line time. The functions are the same as those above except that they start
rms_t_ rather than rms_ and take a char* rather than a RAB or a FAB. You
can freely mix and match the text and RAB/FAB based functions as you like
because they all use the same RMSHANDLE structure internally.
The options are case-blind and comma separated. If the option value
contains a quote or a comma then enclose the value in double quotes. A quote
in the value itself should be doubled - or you can use single quotes:
eg
rms_t_find(handle, "krf=2,key=PATRICK,rop=\"rlk,kge\"");
or
rms_t_find(handle, "krf=2,key=PATRICK,rop='rlk,kge'");
You can also specify the values to keys as variables in a printf-type way eg:
rms_t_find(handle, "krf=%d,key=%s,rop='rlk,kge'", keynum, keybuf);
is functionally identical to the line above (if keynum==2 and keybuf=="PATRICK").
To specify a binary key you need to do the following incantation:
rms_t_find(handle, "ksz=%d,key=%*s,kop=kge", keylen, keylen, key);
ksz= *MUST* be specified before key= and the length must be passed in twice:
once to ksz= and once to replace the * in %*s so that librms knows how much data
to copy into its internal buffer. This form of substitution only works for values, not
keys so parameters of the form "%s=%d" are not valid.
For more information on the particular function see the description of
the non _t functions above.
>>> RMSHANDLE rms_t_open(char *name, int mode, char *options);
Options can be:
fac=put,del,get,upd,del
shr=put,del,get,upd,del
rfm=udf,fix,var,vfc,stm,stmcr,stmlf
rat=ftn,cr,prn,blk
You can't retrieve file information from this call as the FAB fields
filled in by rms_opn() are lost.
>>> int rms_t_read(RMSHANDLE h, char *buf, int maxlen, char *options);
rac=seq,key,rfa
rop=eof,fdl,uif,hsh,loa,ulk,tpt,rah,wbh,kge,kgt,nlk,rlk,bio,nxr,wat,rrl,rea,kle,kgt
kbf Key value
krf Key number
key is an alias for kbf (to aid readability)
kop is an alias for rop (to aid readability when you just need key options)
If you use key= or kbf= then rac=key is implied
Similarly for the others, with RAB replaced by the options string.
DEBUGGING
---------
Because librms uses the same DAP functions as dncopy,fal & friends it has
the same debug/logging code available. Simply set the LIBRMS_VERBOSE
environment variable to some number (1 is the default) and you will
get message logging to stderr.
|