Supporting user-defined file types
ReportWriter (and Repository) explicitly support three file types: ASCII, DBL ISAM, and relative. They also support the user-defined file type, which enables you to provide your own support for any additional file types using I/O subroutines that you write yourself.
You can overload these I/O subroutines in ReportWriter. Whenever ReportWriter performs an I/O function where the file type is user-defined, it calls one of the four routines listed below. RPS_OPEN_METHOD opens a channel, RPS_CLOSE_METHOD closes a channel, RPS_READ_METHOD reads a record, and RPS_READS_METHOD reads a record sequentially.
The versions of these routines linked with your original ReportWriter distribution are “dummy” routines; they simply return. You can overload these routines with your own versions that provide support for file types not supported by ReportWriter or Repository.
RPS_OPEN_METHOD
subroutine RPS_OPEN_METHOD
a_channel ,n ;I/O channel returned (d3)
a_mode ,a ;I/O mode in which to open the channel
; (for example, U:I) (a3)
a_filename ,a ;Name of file to open (Not a file
; definition name.) (a64)
a_user_area ,a ;User–defined data area (a60)
a_record ,n ;Record number, if file is relative (d5)
a_error ,n ;Returned with an error code (d2)
RPS_CLOSE_METHOD
subroutine RPS_CLOSE_METHOD
a_channel ,n ;I/O channel (d3)
a_user_area ,a ;User–defined data area (a60)
a_record ,n ;Record number, if file is relative (d5)
a_error ,n ;Returned with an error code (d2)
RPS_READ_METHOD
subroutine RPS_READ_METHOD
a_channel ,d3 ;I/O channel to use (d3)
a_record ,a ;Record returned
a_key_val ,a ;Key value that identifies the record
a_key_ref ,n ;Explicit key of reference; if no keys defined
; for the structure, this value is -1 (d1)
a_user_area ,a ;User–defined data area (a60)
a_record ,n ;Record number, if file is relative (d5)
a_error ,n ;Returned with an error code (d2)
RPS_READS_METHOD
subroutine RPS_READS_METHOD
a_channel ,n ;I/O channel to use (d3)
a_record ,a ;Record returned
a_user_area ,a ;User–defined data area (a60)
a_record ,n ;Record number, if file is relative (d5)
a_error ,n ;Returned with an error code (d2)
Error codes to return from I/O subroutines
These are the valid error codes to return from the four I/O subroutines listed above:
DD_IO_ERROR ,-1 ;Operation error DD_IO_NORMAL ,0 ;Normal operation DD_IO_NOFIND ,1 ;"Record not found" error DD_IO_UNKNOWN ,2 ;Unknown error DD_IO_INVFIL ,3 ;Invalid file type DD_IO_EOF ,4 ;End of file DD_IO_CANCEL ,8 ;Interrupt signal entered
Sample user-defined file type I/O subroutines
This example illustrates the use of the user-defined file type for supporting an MCBA-like file structure. Also shown is the definition file this subroutine uses. The files containing these routines and their record definitions are included in your distribution.
;------------------------------------------------------------------
;
; Source: USRDCTIO.DEF
;
; Description: User-defined file type I/O control error
; codes and example user control area records
;
;-----------------------------------------------------------------
;
; Define error codes
;
.define DD_IO_ERROR , -1 ;Operation error
.define DD_IO_NORMAL , 0 ;Normal operation
.define DD_IO_NOFIND , 1 ;Record not found
.define DD_IO_UNKNOW , 2 ;Unknown error
.define DD_IO_INVFIL , 3 ;Invalid file type
.define DD_IO_EOF , 4 ;End of file
.define DD_IO_CANCEL , 8 ;Interrupt signal entered
; Example user file type definition area
record usr_type ;User area 1-15
usrtyp ,a15 ;User file type
usr_ftyp ,a1 @usrtyp+1 ;Specific file type
; Example user file structure for MCBA master type
record usr_mcba ;User area 16-41
idx_chn ,d2 ;Index file channel
keyref ,d1 ;Key reference used
orgcnt ,d5 ;Organized record count
reccnt ,d5 ;Record count
maxrec ,d5 ;Maximum record count
delcnt ,d3 ;Delete record count
recnum ,d5 ;Record number (pointer)
.ifndef SHOW_DEF_LIST
.START NOPAGE, LIST
.endc
;------------------------------------------------------------------
;
; Source: USRMCBA.DBL
;
; Description: Sample user-defined file type I/O access
; routines for an MCBA-like file
;
; Routines: RPS_OPEN_METHOD, RPS_READ_METHOD,
; RPS_READS_METHOD, RPS_CLOSE_METHOD
;
;------------------------------------------------------------------
subroutine rps_open_method
;
; Description: This is a sample user-defined file type open routine.
;
; Arguments:
;
a_chn ,n ;Returned open channel
a_mod ,a ;Returned file open mode
; in this example, not used
a_filnam ,a ;Open filename
a_userarea ,a ;Returned user control area
a_ddarea ,n ;Returned record number
a_sts ,n ;Returned status
; Special Notes:
; This example doesn't use "RPS_FILNAM_METHOD" to process the open
; filename. Instead, this routine processes the filename tag character
; to determine the necessary operation.
;
; User file types are specified at the end of the open filename.
;
; User file types supported:
; "@M" type file: MCBA-like standard master file with index (example
; file specification DAT:CUSMAS, IDX:CUSIDX@M)
;
; User data area structure:
;
; usrtyp ,a15 ;User file type
; idx_ch ,d2 ;Index file channel
; keyref ,d1 ;Key reference used
; orgcnt ,d5 ;Organized record count
; reccnt ,d5 ;Record count
; maxrec ,d5 ;Maximum record count
; delcnt ,d3 ;Delete record count
; recnum ,d5 ;Record number (pointer)
.define SHOW_DEF_LIST
.include "usrdctio.def"
record
len ,d2
mstlen ,d2
record rec_buf
buffer ,a200 ;Temporary buffer to read the MCBA
; control record
proc
clear usr_mcba
len = %trim(a_filnam) ;Get actual size of the filename
if (a_filnam(len-1:2).eq."@M") then
begin
a_mod = "i"
usrtyp = "@M" ;Load user type
len = len - 2
call mcba_master
xreturn ;We are done here!
end
else ;Invalid file type
begin
a_sts = DD_IO_INVFIL
xreturn
end
xreturn
mcba_master,
;
; Do MCBA master file type open
;
mstlen = %instr(1, a_filnam(1,len), ',') ;Find delimiter
if (.not.mstlen) ;Invalid file specification
begin ;Index file required for this type
a_sts = DD_IO_INVFIL
xreturn
end
;Open the index file and store channel
; in the user data area
xcall u_open(idx_chn, "i", a_filnam(mstlen+1,len),,, a_sts)
if (a_sts)
xreturn
len = mstlen - 1 ;Get the master filename length
xcall u_open(a_chn, a_mod, a_filnam(1,len),,, a_sts)
if (a_sts)
xreturn
;We don't care about actual record size
reads(a_chn, rec_buf) [eof=nofind, err=errexit]
reccnt = rec_buf(1,5) ;Record count
recnum = 2 ;And set next record to read
a_userarea(1,15) = usrtyp ;Save user file type
a_userarea(16,41) = usr_mcba ;Save control in user area
return
errexit,
a_sts = DD_IO_ERROR
return
nofind,
a_sts = DD_IO_NOFIND
return
end
subroutine rps_read_method
;
; Description: Sample user-defined file type random read routine.
;
; Arguments:
;
a_chn ,n ;File open channel
a_recbuf ,a ;Returned record buffer
a_keyval ,a ;Search key value
a_keyref ,n ;Search key reference ID
a_userarea ,a ;User control data area
a_ddarea ,n ;Returned record number
a_sts ,n ;Returned status
.include "usrdctio.def"
record
len ,d5
rtn ,d5
proc
usrtyp = a_userarea(1,15) ;Get user type
len = size(a_recbuf) ;Get size of record buffer
if (usrtyp.eq."@M") then ;Do random read on user case
begin
usr_mcba = a_userarea(16,41) ;Get local user area
xcall mcba_search(idx_chn, reccnt, a_keyval, a_keyref,
& recnum, a_sts)
if (a_sts)
goto nofind
get(a_chn, a_recbuf(1,len), recnum) [err=errexit, eof=nofind]
keyref = a_keyref ;Save for sequential read by key
a_userarea(16,41) = usr_mcba ;Save update
end
else ;DBL ISAM
goto errexit
xreturn
errexit,
a_sts = DD_IO_ERROR
xreturn
nofind,
a_sts = DD_IO_NOFIND
xreturn
end
subroutine rps_reads_method
;
; Description: Sample user-defined file type sequential read routine.
;
; Arguments:
;
a_chn ,n ;File open channel
a_recbuf ,a ;Returned record buffer
a_userarea ,a ;User control data area
a_ddarea ,n ;Returned record number
a_sts ,n ;Returned status
.include "usrdctio.def"
record
len ,d5
rtn ,d3
arecsiz ,a4
proc
usrtyp = a_userarea(1,15) ;Get the user type
len = %size(a_recbuf) ;Get size of record buffer
if (usrtyp.eq."@M") then ;Do sequential read on user case
begin
usr_mcba = a_userarea(16,41) ;Get local user area
xcall mcba_search(idx_chn, reccnt,, keyref, recnum, a_sts)
if (a_sts)
xreturn
get(a_chn,a_recbuf(1,len), recnum)
& [eof=nofind,key=nofind,err=errexit]
a_userarea(16,41) = usr_mcba ;Save update
end
else
goto errexit
xreturn
errexit,
a_sts = DD_IO_ERROR
xreturn
nofind,
a_sts = DD_IO_NOFIND
xreturn
end
subroutine rps_close_method
;
; Description: Sample user-defined file type close routine.
;
; Arguments:
;
a_chn ,n ;File open channel
a_userarea ,a ;User control data area
a_ddarea ,n ;Record number
a_sts ,n ;Returned status
.include "usrdctio.def"
proc
usrtyp = a_userarea(1,15)
xcall u_close(a_chn)
if (usrtyp.eq."@M")
begin
usr_mcba = a_userarea(16,41)
xcall u_close(idx_chn)
end
clear a_userarea
xreturn
end
subroutine mcba_search
;
; Description: Search MCBA master index and return record number
;
; Arguments:
;
a_chn ,n ;Index file open channel
a_reccnt ,n ;Organized record count
a_keyval ,a ;Index key value
a_keyref ,n ;Index key reference ID - 0 based
a_recnum ,n ;Returned record number
a_sts ,n ;Returned status
;
; Special Notes:
; This routine assumes the index file contains a single keyed index with
; a record pointer (number), and the index file is sorted by the index.
;
.include "usrdctio.def"
record idx_rec1 ;Sample index record structure
index_key ,a50 ; Index part
rec_num ,d5 ; Pointer part
,a1 ; Record Terminator
record idx_rec, X ;Sample index record structure
idxrec ,a55 ; Data only
record ;For binary search
first ,d5
last ,d5
saved ,d5
proc
; Do necessary search operation on the a_keyval and a_keyref
if (.not.%passed(a_keyval)) then ;Sequential read on keyref
call do_seq
else if (a_keyval.eq.' ') then ;Initial read on keyref
call do_seq
else
call do_random
a_recnum = rec_num + 1 ;Increment by one for control record
xreturn
do_seq,
do
reads(a_chn, idx_rec) [eof=nofind, err=errexit]
until (idx_rec.ne.']' .and. index_key.ne.' ')
return
do_random,
; Do sequential search or binary search
if (.not.%passed(a_reccnt)) then ;Sequential search
do forever
begin
reads(a_chn, idx_rec) [eof=nofind, err=errexit]
if (index_key.eq.a_keyval)
return
end
else ;Do binary search
begin
recnum = a_reccnt / 2 ;Initialize the indexes
first = 1
last = a_reccnt
do forever
begin
saved = recnum ;Save last middle index
get(a_chn, idx_rec1, recnum) [eof=nofind, err=errexit]
if (index_key.eq.a_keyval) then ;Found
return
else if (recnum.eq.first .or. recnum.eq.last) then ;Not found
goto nofind
else if (index_key.gt.a_keyval) then ;Try left half
begin
if ((last-recnum).eq.1) then ;No middle item set to first
decr recnum
else ;Set the next middle index
recnum = recnum - (last-recnum) / 2
last = saved
end
else ;Try right half
begin
if ((last-recnum).eq.1) then ;No middle item set to last
incr recnum
else ;Set the next middle index
recnum = recnum + (last-recnum) / 2
first = saved
end
end
end
return
nofind,
a_sts = DD_IO_NOFIND
xreturn
errexit,
a_sts = DD_IO_ERROR
xreturn
end
