tldm-universe/Ardent/UV/BP/DATALOADER.B
2024-09-09 17:51:08 -04:00

1862 lines
69 KiB
Plaintext
Executable File

******************************************************************************
*
* Dataloader Facility
*
* Module %M% Version %I% Date %H%
*
* (c) Copyright 1998 Ardent Software Inc. - All Rights Reserved
* This is unpublished proprietary source code of Ardent Software Inc.
* The copyright notice above does not evidence any actual or intended
* publication of such source code.
******************************************************************************
*
* Maintenence log - insert most recent change descriptions at top
*
* Date.... GTAR# WHO Description.........................................
* 10/14/98 23801 SAP Change copyrights.
* 06/28/96 18440 SHJ Program rewrite
* 18440 TMC New file
*
*******************************************************************************
DEFFUN UVREADMSG(num,args) CALLING '*UVREADMSG'
* There are two main loops, the first reads the input file, the second
* does the actual loading
errargs = ""
thiscol = ""
could.be.end = 0
conf.eof = 0
item.count = 0 ;* Counter for items processed
newfile = 0
recnum = 0
destfile.fnd = 0
itype.flag = 0
quote.locs = ""
alt.quote.locs = ""
processed = UVREADMSG(85322,"") ;* processed message
GOSUB read.configuration
* GOSUB print.configuration
GOSUB do.loading
CALL *UVPRINTMSG(074300, "")
GOTO program.end
error.exit:
CALL *UVPRINTMSG(errmessage,errargs)
CALL *UVPRINTMSG(074301, "")
@SYSTEM.RETURN.CODE = -1
GOTO program.end
***************************************************************************
* *
* Read in the configuration file line by line and parse the data into *
* something we understand and can use when it comes time to load the *
* data. *
* *
***************************************************************************
read.configuration:
* get the first argument and open the file
sentence = @SENTENCE
* Convert any Tab characters to whitespace
CONVERT CHAR(9) TO ' ' IN sentence
* take last token
check.token = FIELD(sentence," ",DCOUNT(sentence, " "))
OPENSEQ check.token TO fv.config
LOCKED
errmessage = 074302
errargs<1> = check.token
GOTO error.exit
END
ELSE
errmessage = 074303
errargs<1> = check.token
GOTO error.exit
END
* file was able to be opened, parse it
* Read it a line at a time, saving pertinent information while processing
**************************************************************************
* The Format of the configuration file is:
*FILE NUMBER:
* LOCATION:
* ROW SEPARATOR CHARACTER(S):
* COLUMN SEPARATOR CHARACTER(S):
* QUOTE CHARACTER:
* ALTERNATE QUOTE CHARACTER:
* ESCAPE CHARACTER:
* COLUMN NUMBER:
* SOURCE FORMAT:
* WIDTH:
* NEXT SEPARATOR CHARACTER(S):
* BYTE ORDER:
*DESTINATION FILE:
* LOCATION:
* CREATE FILE: YES/no True/False
* CREATE TABLE: YES/no True/False
* PARAMETER(S):
* AUTOSIZE: YES/no True/False
* AUTOSIZE ROWS:
* KEY COLUMN(S):
* COLUMN NUMBER:
* COLUMN NAME:
* SOURCE FILE NUMBER(S):
* SOURCE COLUMN NUMBER(S):
* CONVERSION TYPE: (I/O/B)
* CONVERSION CODE:
**************************************************************************
EQU maxinfiles TO 100 ; * allow 100 input files for now
EQU maxdestfiles TO 1 ; * only 1 destination file allowed
EQU no.in.params TO 15 ; * 11 parameter fields for input file
EQU no.out.params TO 18 ; * 18 parameter fields for output file
EQU readblksiz TO 1024
DIM infile(maxinfiles, no.in.params)
MAT infile = ""
DIM destfile(maxdestfiles,no.out.params)
MAT destfile = ""
* input file equates
* if additional fields are added, adjust no.in.params above
EQU FILE.NUMBER TO 1
EQU LOCATION TO 2
EQU ROW.SEP.CHAR TO 3
EQU COL.SEP.CHAR TO 4
EQU QUOTE.CHAR TO 5
EQU ALT.QUOTE.CHAR TO 6
EQU ESCAPE.CHAR TO 7
EQU COL.NUMBER TO 8
EQU SRC.FORMAT TO 9
EQU WIDTH TO 10
EQU NEXT.SEP.CHAR TO 11
EQU BYTE.ORDER TO 12
EQU QUOTE.SEP TO 13
* dest file equates
* if additional fields are added, adjust no.out.params above
* FILE.NUMBER and LOCATION are defined above as 1 and 2
* we'll reuse them for the destination file
EQU CREATE.FILE TO 3
EQU CREATE.TABLE TO 4
EQU PARAMS TO 5
EQU AUTOSIZE TO 6
EQU AUTOSIZE.ROWS TO 7
EQU KEY.COLUMNS TO 8
EQU DCOL.NUMBER TO 9
EQU DCOL.NAME TO 10
EQU SRC.FILE.NO TO 11
EQU SRC.COL.NO TO 12
EQU CONV.TYPE TO 13
EQU CONV.CODE TO 14
* other equates
EQU PARAM.LINE LIT "TRIM(FIELD(confline,':',2,9999))"
* yes/no array
yes.no.check = "N":@FM:"NO":@FM:"F":@FM:"FALSE":@FM
yes.no.check := "Y":@FM:"YES":@FM:"T":@FM:"TRUE"
* array of valid formats
informats = "INTEGER":@FM:"SMALLINT":@FM:"FLOAT":@FM:"DOUBLE"
informats := "BYTEINT":@FM:"DECIMAL":@FM:"CURRENCY":@FM:"RAW"
* We can use READSEQ to read data until the file is consumed since data
* must be on a line by line basis
numfiles = 0
getconflineerr = 0
linenum = 0
filenum = -1
GOSUB getconfline
IF conf.eof THEN GOTO confreaderror
GOSUB conflinecheck
* first line read must begin a FILE paragraph
if not(newfile) then
errmessage = 074346
GOTO error.exit
end
* reset newfile and destfile flags
newfile = 0
destfile.fnd = 0
* loop for each file
begin.new.file:
LOOP
numcols = 0
GOSUB getconfline
GOSUB conflinecheck
IF conf.eof THEN EXIT
IF newfile AND oldfilenum # -1 ;* check oldfilenum to be safe
THEN
* we're starting a new FILE NUMBER entry. Verify that the previous
* file's information was complete first.
*
* if the number of cols found = 0 then there is a problem
IF oldnumcols = 0
THEN
errmessage = 074328
errargs<1> = infile(oldfilenum, FILE.NUMBER)
GOTO error.exit
END
* set defaults
infile(filenum,ROW.SEP.CHAR) = CHAR(10)
infile(filenum,COL.SEP.CHAR) = " "
infile(filenum,QUOTE.CHAR) = ""
infile(filenum,ALT.QUOTE.CHAR) = ""
infile(filenum,ESCAPE.CHAR) = ""
infile(filenum,BYTE.ORDER) = 0
infile(filenum,QUOTE.SEP) = 1
infile(filenum, COL.NUMBER) = ""
infile(filenum, SRC.FORMAT) = ""
infile(filenum, WIDTH) = ""
infile(filenum, NEXT.SEP.CHAR) = ""
* reset newfile again or else we'll set the defaults every time we read
newfile = 0
END
REPEAT
* if the number of files found = 0 then there is a problem
IF numfiles = 0
THEN
errmessage = 074312
GOTO error.exit
END
* if no destination file was specifed, there's a problem
IF NOT(destfile.fnd)
THEN
errmessage = 074351
GOTO error.exit
END
* check to see if ONE of AUTOSIZE, CREATE FILE, or CREATE TABLE is true
onecheck = SUM(destfile(dfilenum, CREATE.FILE):@FM:destfile(dfilenum, CREATE.TABLE):@FM:destfile(dfilenum, AUTOSIZE))
IF NOT(onecheck = 1 OR onecheck = 0)
THEN
errmessage = 074315
GOTO error.exit
END
* check to see if the destination file already exists if AUTOSIZE, CREATE
* FILE or CREATE TABLE was specified
IF onecheck
THEN
OPEN destfile(dfilenum,LOCATION) TO fv.outfile
THEN
errmessage = 074352
errargs<1> = destfile(dfilenum,LOCATION)
GOTO error.exit
END
END
******************************************
* close configuration file
CLOSE fv.config
RETURN
******************************************
* retrieve a line of data from the configuration file
******************************************
getconfline:
READSEQ confline FROM fv.config
THEN
linenum += 1
END ELSE
IF could.be.end AND STATUS() = 1
THEN
conf.eof = 1
RETURN
END
ELSE GOTO confreaderror
END
* ignore blank lines
IF confline = "" THEN GOTO getconfline
confline = TRIM(confline)
lastconfline = confline
RETURN
******************************************
* figure out what data was given on this line
******************************************
conflinecheck:
parm = UPCASE(FIELD(confline,":",1))
BEGIN CASE
CASE parm = "FILE NUMBER"
newfile = 1
IF destfile.fnd
THEN
* cannot specify additional input files after the destination file in
* configuration file.
errmessage = 074348
GOTO error.exit
END
oldfilenum = filenum
oldnumcols = numcols
filenum = PARAM.LINE
numfiles += 1
IF NOT(NUM(filenum)) OR (filenum < 1)
THEN
errmessage = 074305
errargs<1> = parm
errargs<2> = linenum
GOTO error.exit
END
IF (numfiles > maxinfiles)
THEN
errmessage = 074347
GOTO error.exit
END
CASE parm = "LOCATION"
* confirm location
location = PARAM.LINE
IF destfile.fnd THEN
* this is the location specification for the destination file
destfile(dfilenum,LOCATION) = location
END
ELSE
* this is the location specification for an input file
infile(filenum, LOCATION) = location
OPENSEQ location TO infile(filenum, FILE.NUMBER)
ELSE
errmessage = 074306
errargs<1> = location
GOTO error.exit
END
END
CASE parm = "ROW SEPARATOR CHARACTER(S)"
rowsep = PARAM.LINE
* if rowsep is numeric, then convert it to characters
* and concatenate into one string
IF NUM(FIELD(rowsep," ",1))
THEN
rowsep = CHARS(CONVERT(" ",@FM,rowsep))
rowsep = CONVERT(@FM,"",rowsep)
END
infile(filenum, ROW.SEP.CHAR) = rowsep
CASE parm = "COLUMN SEPARATOR CHARACTER(S)"
colsep = PARAM.LINE
* if colsep is numeric, then convert it to characters
* and concatenate into one string
IF NUM(FIELD(colsep," ",1))
THEN
colsep = CHARS(CONVERT(" ",@FM,colsep))
colsep = CONVERT(@FM,"",colsep)
END
infile(filenum, COL.SEP.CHAR) = colsep
CASE parm = "QUOTE CHARACTER"
quote.char = PARAM.LINE
IF NUM(quote.char)
THEN
* have numbers, convert them to characters and save them
infile(filenum, QUOTE.CHAR) = CHAR(quote.char)
END
ELSE
infile(filenum, QUOTE.CHAR) = quote.char
IF LEN(quote.char) # 1
THEN
errmessage = 074307
errargs<1> = quote.char
GOTO error.exit
END
END
IF infile(filenum,QUOTE.CHAR) = infile(filenum,ROW.SEP.CHAR) OR infile(filenum,QUOTE.CHAR) = infile(filenum,COL.SEP.CHAR)
THEN
* quote character cannot be the same as row or column sep char
errmessage = 074358
GOTO error.exit
END
CASE parm = "ALTERNATE QUOTE CHARACTER"
alt.quote.char = PARAM.LINE
IF NUM(alt.quote.char)
THEN
* have numbers, convert them to characters and save them
infile(filenum, ALT.QUOTE.CHAR) = CHAR(alt.quote.char)
END
ELSE
infile(filenum, ALT.QUOTE.CHAR) = alt.quote.char
IF LEN(alt.quote.char) # 1
THEN
errmessage = 074307
errargs<1> = alt.quote.char
GOTO error.exit
END
END
IF infile(filenum,ALT.QUOTE.CHAR) = infile(filenum,ROW.SEP.CHAR) OR infile(filenum,ALT.QUOTE.CHAR) = infile(filenum,COL.SEP.CHAR)
THEN
* quote character cannot be the same as row or column sep char
errmessage = 074359
GOTO error.exit
END
CASE parm = "ESCAPE CHARACTER"
esc.char = PARAM.LINE
IF NUM(esc.char)
THEN
* have numbers, convert them to characters and save them
infile(filenum, ESCAPE.CHAR) = CHAR(esc.char)
END
ELSE
infile(filenum, ESCAPE.CHAR) = esc.char
IF LEN(esc.char) # 1
THEN
errmessage = 074308
errargs<1> = esc.char
GOTO error.exit
END
END
CASE parm = "COLUMN NUMBER"
IF destfile.fnd
THEN
* I'm processing a destination file, but I found a stray column number
* which should have been processed under KEY COLUMN
errmessage = 074353
errargs<1> = parm
errargs<2> = linenum
GOTO error.exit
END
* beginning column information
newcol.found:
numcols += 1
thiscolnum = PARAM.LINE
IF NOT(NUM(thiscolnum)) OR (thiscolnum < 1)
THEN
errmessage = 074305
errargs<1> = thiscolnum
errargs<2> = linenum
GOTO error.exit
END
* set column number and initialize everything else for this column
infile(filenum,COL.NUMBER)<numcols> = thiscolnum
infile(filenum,SRC.FORMAT)<numcols> = "RAW"
infile(filenum,WIDTH)<numcols> = 0
infile(filenum,NEXT.SEP.CHAR)<numcols> = ""
LOOP
* get next line of information
GOSUB getconfline
colparm = UPCASE(FIELD(confline,":",1))
BEGIN CASE
* case of possibilities for column information
CASE colparm = "SOURCE FORMAT"
thisformat = UPCASE(PARAM.LINE)
* set default
IF thisformat # ""
THEN
* make sure user specified a valid format
LOCATE thisformat IN informats SETTING junk
ELSE
errmessage = 074310
errargs<1> = thisformat
errargs<2> = thiscolnum
errargs<3> = infile(filenum, FILE.NUMBER)
GOTO error.exit
END
infile(filenum,SRC.FORMAT)<numcols> = thisformat
END
CASE colparm = "WIDTH"
width = PARAM.LINE
IF NOT(NUM(width)) OR (width < 0)
THEN
errmessage = 074305
errargs<1> = colparm
errargs<2> = linenum
GOTO error.exit
END
infile(filenum,WIDTH)<numcols> = width
CASE colparm = "NEXT SEPARATOR CHARACTER(S)"
nextsep = PARAM.LINE
* if nextsep is numeric, then convert it to characters
* and concatenate into one string
IF NUM(FIELD(nextsep," ",1))
THEN
nextsep = CHARS(CONVERT(" ",@FM,nextsep))
nextsep = CONVERT(@FM,"",nextsep)
END
infile(filenum, NEXT.SEP.CHAR)<numcols> = nextsep
CASE colparm = "COLUMN NUMBER"
* we found the start of the next column's information
parm = colparm ; * for sanity's sake
colparm = ""
GOTO newcol.found
CASE 1
* we're not looking at column information anymore
* go to beginning of conflinecheck and start fresh
GOTO conflinecheck
END CASE
REPEAT
CASE parm = "SOURCE FORMAT"
* found a stray instruction
errmessage = 074350
errargs<1> = parm
errargs<2> = linenum
GOTO error.exit
CASE parm = "WIDTH"
* found a stray instruction
errmessage = 074350
errargs<1> = parm
errargs<2> = linenum
GOTO error.exit
CASE parm = "NEXT SEPARATOR CHARACTER(S)"
* found a stray instruction
errmessage = 074350
errargs<1> = parm
errargs<2> = linenum
GOTO error.exit
CASE parm = "BYTE ORDER"
* check to see it is 0 or 1
byte.order = PARAM.LINE
IF (byte.order # 0) AND (byte.order # 1)
THEN
errmessage = 074340
errargs<1> = byte.order
errargs<2> = infile(filenum, LOCATION)
GOTO error.exit
END
ELSE
infile(filenum, BYTE.ORDER) = byte.order
END
CASE parm = "DESTINATION FILE"
IF destfile.fnd
THEN
* we already had a destination file specified -- you only get one
errmessage = 074349
GOTO error.exit
END
destfile.fnd = 1
* dfilenum is hardcoded to 1 for now since we only support 1
* destination file
dfilenum = 1
* set up destination file defaults
destfile(dfilenum,CREATE.FILE) = 0
destfile(dfilenum,CREATE.TABLE) = 0
destfile(dfilenum,PARAMS) = ""
destfile(dfilenum,AUTOSIZE) = 0
destfile(dfilenum,AUTOSIZE.ROWS) = 10
CASE parm = "CREATE FILE"
checkval = PARAM.LINE
GOSUB check.true.false
IF checkval
THEN destfile(dfilenum,CREATE.FILE) = 1
ELSE destfile(dfilenum,CREATE.FILE) = 0
CASE parm = "CREATE TABLE"
checkval = PARAM.LINE
GOSUB check.true.false
IF checkval
THEN destfile(dfilenum,CREATE.TABLE) = 1
ELSE destfile(dfilenum,CREATE.TABLE) = 0
CASE parm = "PARAMETER(S)"
* only valid for CREATE.FILE
IF PARAM.LINE # ""
THEN
IF NOT(destfile(dfilenum, CREATE.FILE) OR destfile(dfilenum,CREATE.TABLE))
THEN
errmessage = 074332
GOTO error.exit
END
END
destfile(dfilenum, PARAMS) = PARAM.LINE
CASE parm = "AUTOSIZE"
checkval = PARAM.LINE
GOSUB check.true.false
IF checkval
THEN destfile(dfilenum,AUTOSIZE) = 1
ELSE destfile(dfilenum,AUTOSIZE) = 0
CASE parm = "AUTOSIZE ROWS"
autosize.rows = PARAM.LINE
* If user did not specify AUTOSIZE, but gave AUTOSIZE.ROWS, error
IF autosize.rows # "" AND NOT(destfile(dfilenum,AUTOSIZE))
THEN
errmessage = 074334
GOTO error.exit
END
IF destfile(dfilenum,AUTOSIZE)
THEN
* If user specified AUTOSIZE, but didn't specify AUTOSIZE.ROWS, then set default
IF autosize.rows = "" THEN autosize.rows = 10 ; * default
IF NOT(NUM(autosize.rows)) OR (autosize.rows < 0)
THEN
errmessage = 074305
errargs<1> = parm
errargs<2> = linenum
GOTO error.exit
END
END
destfile(dfilenum, AUTOSIZE.ROWS) = autosize.rows
CASE parm = "KEY COLUMN(S)"
IF NOT(destfile.fnd)
THEN
errmessage = 074354
errargs<1> = linenum
GOTO error.exit
END
key.cols = PARAM.LINE
destfile(dfilenum, KEY.COLUMNS) = CONVERT(" ", @FM, key.cols)
* initialize fields
destfile(dfilenum, DCOL.NUMBER) = ""
destfile(dfilenum, DCOL.NAME) = ""
destfile(dfilenum, SRC.FILE.NO) = ""
destfile(dfilenum, SRC.COL.NO) = ""
destfile(dfilenum, CONV.TYPE) = ""
destfile(dfilenum, CONV.CODE) = ""
thiscolnum = 0
numoutcols = 0
* Begin process column information. Next line read should be a COLUMN spec.
GOSUB getconfline
parm = FIELD(confline,":",1)
IF parm # "COLUMN NUMBER"
THEN
errmessage = 074355
GOTO error.exit
END
process.dest.cols:
numoutcols += 1
thiscolnum = PARAM.LINE
IF thiscolnum = "KEY" OR thiscolnum = 0
THEN
* a column number of 0 indicates this is the information
* for the key column, and not a field in the data record
thiscolnum = 0
END ELSE
if NOT(NUM(thiscolnum)) OR thiscolnum < 1
THEN
errmessage = 074305
errargs<1> = parm
errargs<2> = linenum
GOTO error.exit
END
END
destfile(dfilenum,DCOL.NUMBER)<numoutcols> = thiscolnum
LOOP
* process column parameters
GOSUB getconfline
colparm = FIELD(confline,":",1)
BEGIN CASE
CASE colparm = "COLUMN NAME"
colname = PARAM.LINE
* check to see it's unique
LOCATE colname IN destfile(dfilenum,DCOL.NAME) SETTING junk
THEN
errmessage = 074323
errargs<1> = colname
GOTO error.exit
END
* cannot allow a null column name
IF colname = ""
THEN
errmessage = 074356
errargs<1> = thiscolnum
GOTO error.exit
END
destfile(dfilenum,DCOL.NAME)<numoutcols> = colname
CASE colparm = "SOURCE FILE NUMBER(S)"
srcfilenum = PARAM.LINE
srcfilenum = CONVERT(" ", @FM, srcfilenum)
* every parameter given must be a number
IF SUM(NUMS(srcfilenum)) # DCOUNT(srcfilenum,@FM)
THEN
errmessage = 074317
errargs<1> = colparm
GOTO error.exit
END
destfile(dfilenum, SRC.FILE.NO)<numoutcols> = CONVERT(@FM, @VM, srcfilenum)
CASE colparm = "SOURCE COLUMN NUMBER(S)"
srccolnum = PARAM.LINE
srccolnum = CONVERT(" ", @FM, srccolnum)
IF SUM(NUMS(srccolnum)) # DCOUNT(srccolnum,@FM)
THEN
errmessage = 074317
errargs<1> = colparm
GOTO error.exit
END
IF destfile(dfilenum,SRC.FILE.NO)<numoutcols> = ""
THEN
errmessage = 074320
errargs<1> = destfile(dfilenum,DCOL.NUMBER)<numoutcols>
errargs<2> = destfile(dfilenum,DCOL.NAME)<numoutcols>
GOTO error.exit
END
destfile(dfilenum, SRC.COL.NO)<numoutcols> = CONVERT(@FM, @VM, srccolnum)
* It's possible this is the end of the file, if I have one column's worth
* of information collected.
could.be.end = 1
CASE colparm = "CONVERSION TYPE"
conv.type = UPCASE(PARAM.LINE)
IF conv.type # "B" AND conv.type # "I" AND conv.type # "O"
THEN
errmessage = 074324
errargs<1> = conv.type
errargs<2> = linenum
GOTO error.exit
END
destfile(dfilenum, CONV.TYPE)<numoutcols> = conv.type
CASE colparm = "CONVERSION CODE"
destfile(dfilenum, CONV.CODE)<numoutcols> = PARAM.LINE
CASE colparm = "COLUMN NUMBER"
* Found the start of the next column's information
* Check info for last read column
GOSUB last.column.check
* Go to top of loop.
parm = colparm ;* for sanity's sake
colparm = ""
GOTO process.dest.cols
END CASE
UNTIL conf.eof
REPEAT
GOSUB last.column.check
* check if key columns exist
numkeys = DCOUNT(destfile(dfilenum,KEY.COLUMNS), @FM)
IF numkeys < 1
THEN
errmessage = 074336
GOTO error.exit
END
FOR i = 1 TO numkeys
LOCATE destfile(dfilenum, KEY.COLUMNS)<i> IN destfile(dfilenum,DCOL.NAME) SETTING thiscolnum
ELSE
errmessage = 074318
errargs<1> = destfile(dfilenum, KEY.COLUMNS)<i>
GOTO error.exit
END
NEXT i
* must have a column name defined for every column, no skipping
FOR thiscolnum = 1 TO numoutcols
IF destfile(dfilenum, DCOL.NAME)<thiscolnum> = ""
THEN
errmessage = 074329
errargs<1> = thiscolnum
GOTO error.exit
END
NEXT thiscolnum
CASE parm = "COLUMN NUMBER"
* found a stray instruction
errmessage = 074353
errargs<1> = parm
errargs<2> = linenum
GOTO error.exit
CASE parm = "COLUMN NAME"
* found a stray instruction
errmessage = 074353
errargs<1> = parm
errargs<2> = linenum
GOTO error.exit
CASE parm = "SOURCE FILE NUMBER(S)"
* found a stray instruction
errmessage = 074353
errargs<1> = parm
errargs<2> = linenum
GOTO error.exit
CASE parm = "SOURCE COLUMN NUMBER(S)"
* found a stray instruction
errmessage = 074353
errargs<1> = parm
errargs<2> = linenum
GOTO error.exit
CASE parm = "CONVERSION TYPE"
* found a stray instruction
errmessage = 074353
errargs<1> = parm
errargs<2> = linenum
GOTO error.exit
CASE parm = "CONVERSION CODE"
* found a stray instruction
errmessage = 074353
errargs<1> = parm
errargs<2> = linenum
GOTO error.exit
CASE 1
if not(numfiles) then
errmessage = 074346
GOTO error.exit
end
* invalid parameter
errmessage = 074345
errargs<1> = parm
GOTO error.exit
END CASE
RETURN
check.true.false:
checkval = UPCASE(checkval)
LOCATE checkval IN yes.no.check SETTING truth.value
ELSE
errmessage = 074314
errargs<1> = confline
GOTO error.exit
END
* false is 1,2,3,4 true is 5,6,7,8
checkval = (truth.value > 4)
RETURN
************************************
* tell user an error was encountering while reading
* the configuration file.
************************************
confreaderror:
errmessage = 074316
errargs<1> = infile(filenum, LOCATION)
errargs<2> = lastconfline
GOTO error.exit
************************************
* Do some error checking on previous column spec.
* Number of files should equal number of columns
************************************
last.column.check:
num.of.srcfiles = DCOUNT(destfile(dfilenum, SRC.FILE.NO)<numoutcols>, @VM)
num.of.srccols = DCOUNT(destfile(dfilenum,SRC.COL.NO)<numoutcols>,@VM)
IF num.of.srcfiles # num.of.srccols
THEN
errmessage = 074319
errargs<1> = destfile(dfilenum,DCOL.NAME)<numoutcols>
GOTO error.exit
END
* check to see if these columns exist
FOR i = 1 TO num.of.srcfiles
checkfile = destfile(dfilenum,SRC.FILE.NO)<numoutcols, i>
* checkfile is the FILE.NUMBER. Check infile array to see if this slot was
* processed, and that the input file was successfully opened.
IF NOT(ASSIGNED(infile(checkfile,FILE.NUMBER)))
THEN
errmessage = 074321
errargs<1> = checkfile
errargs<2> = destfile(dfilenum,DCOL.NAME)<numoutcols>
GOTO error.exit
END
checkcol = destfile(dfilenum,SRC.COL.NO)<numoutcols, i>
LOCATE checkcol in infile(checkfile,COL.NUMBER) SETTING fnd
ELSE
errmessage = 074322
errargs<1> = checkcol
errargs<2> = checkfile
errargs<3> = thiscolnum
errargs<4> = destfile(dfilenum,DCOL.NAME)<numoutcols>
GOTO error.exit
END
NEXT i
RETURN
**********************************************
* *
* The section will actually do the loading *
* *
**********************************************
do.loading:
* sdata(x, 1) = data left
* sdata(x, 2) = this record
* sdata(x, 3) = eof?
* sdata(x, 4) = filesize
* sdata(x, 5) = current position
* sdata(x, 6) = dynamic array of quotes found
EQU REMAINING.DATA TO 1
EQU THIS.RECORD TO 2
EQU EOF TO 3
EQU FILESIZE TO 4
EQU CURRENT.POS TO 5
EQU QUOTE.POS TO 6
DIM sdata(numfiles, 6)
* get the sizes of all the source files and initialize sdata
dfilenum = 1 ;* only one destination file for now
maxincols = 0
FOR filenum = 1 TO numfiles
STATUS fvstat from infile(filenum, FILE.NUMBER)
ELSE
errmessage = 074325
errargs<1> = filenum
errargs<2> = infile(filenum, LOCATION)
GOTO error.exit
END
sdata(filenum, REMAINING.DATA) = ""
sdata(filenum, THIS.RECORD) = ""
sdata(filenum, EOF) = 0
sdata(filenum, FILESIZE) = fvstat<6>
sdata(filenum, CURRENT.POS) = 0
sdata(filenum, QUOTE.POS) = ""
IF maxincols < DCOUNT(infile(filenum,COL.NUMBER),@FM)
THEN maxincols = DCOUNT(infile(filenum,COL.NUMBER),@FM)
NEXT filenum
* figure out how many columns in this file
DIM coldata(numfiles, maxincols)
* if autosize, set up output row data
IF destfile(dfilenum,AUTOSIZE)
THEN DIM saverows(destfile(dfilenum,AUTOSIZE.ROWS), 2)
* set up key array -- numkeys is the number of KEY COLUMNS
DIM outkeys(numkeys)
*************************************************************
* set up destination file ( if CREATE.FILE or CREATE TABLE) *
*************************************************************
IF destfile(dfilenum, CREATE.FILE) OR destfile(dfilenum,CREATE.TABLE)
THEN
IF destfile(dfilenum,CREATE.FILE)
THEN exeline = "CREATE.FILE "
ELSE exeline = "CREATE TABLE "
exeline := destfile(dfilenum, LOCATION):" ":destfile(dfilenum, PARAMS)
errargs<1> = exeline
CALL *UVPRINTMSG(074335, exeline)
EXECUTE exeline
END
* open the file if not autosize
IF NOT(destfile(dfilenum,AUTOSIZE))
THEN
OPEN destfile(dfilenum, LOCATION) TO fv.outfile
ELSE
errmessage = 074339
errargs<1> = destfile(dfilenum,LOCATION)
GOTO error.exit
END
END
******************
* do the loading *
******************
quote.check.at = 0
totsize = 0
row.num = ""
FOR filenum = 1 TO numfiles
row.num<filenum>=0
NEXT filenum
LOOP
havedata = 0
FOR filenum = 1 TO numfiles
* Do we have quote characters to look for?
check.quotes = (infile(filenum, QUOTE.CHAR)#"") OR (infile(filenum, ALT.QUOTE.CHAR)#"")
LOOP
*
* This loop will find the next complete row, and put it in
* sdata(filenum,THIS.RECORD). Everything else will go into
* sdata(filenum,REMAINING.DATA).
*
********The first time through this loop, we haven't read any data yet
end.of.row = 0
curdata = sdata(filenum, REMAINING.DATA)
* find next row seperator
rowsep = infile(filenum, ROW.SEP.CHAR)
rowsep.len = LEN(rowsep)
escchar = infile(filenum,ESCAPE.CHAR)
end.of.row = INDEX(curdata, rowsep, 1)
* make sure it was not escaped
IF (end.of.row > 1) AND (escchar # "")
THEN
next.instance = 1
LOOP
* check the second to last character to see if it's
* an escape character
WHILE curdata[end.of.row-1, 1] = escchar
old.eor = end.of.row
next.instance += 1
end.of.row = INDEX(curdata, rowsep, next.instance)
UNTIL NOT(end.of.row)
REPEAT
END
IF end.of.row # 0
THEN
* this record is everything before the end.of.row char
sdata(filenum, THIS.RECORD) = LEFT(curdata, end.of.row - 1)
newstart = end.of.row + rowsep.len
newlen = LEN(curdata) - newstart + 1
sdata(filenum, REMAINING.DATA) = curdata[newstart, newlen]
END
ELSE
* we didn't find the end of a row
IF sdata(filenum, EOF)
THEN
* but we got to the end of file, so all of the
* current data is considered one record.
sdata(filenum, THIS.RECORD) = curdata
sdata(filenum, REMAINING.DATA) = ""
EXIT ; * quit loop when file is exhausted
END
ELSE
read.more:
chars.left = sdata(filenum, FILESIZE) - sdata(filenum, CURRENT.POS)
IF chars.left <= 0
THEN
* there aren't any characters left. eof reached.
sdata(filenum, EOF) = 1
END
ELSE
IF chars.left < readblksiz
THEN chars.to.read = chars.left
ELSE chars.to.read = readblksiz
READBLK thisblk FROM infile(filenum, FILE.NUMBER), chars.to.read
THEN
sdata(filenum, CURRENT.POS) += chars.to.read
sdata(filenum, REMAINING.DATA) := thisblk
END
ELSE
IF STATUS() = 1
THEN
*
* end of file was encountered by READBLK
*
newlen = (sdata(filenum,CURRENT.POS) + chars.to.read)
IF (newlen # sdata(filenum,FILESIZE))
THEN
errmessage = 074326
errargs<1> = filenum
errargs<2> = infile(filenum, LOCATION)
GOTO error.exit
END
ELSE sdata(filenum, EOF) = 1
END
ELSE
Errmessage = 074327
errargs<1> = filenum
errargs<2> = infile(filenum, LOCATION)
GOTO error.exit
END
END
END
END
END
UNTIL end.of.row
REPEAT
*
* Done finding the next row.
*
row.num<filenum> += 1
* check the row for quote characters
IF check.quotes
THEN
curdata = sdata(filenum,THIS.RECORD)
* process quote character first
IF infile(filenum,QUOTE.CHAR)
THEN
quote.locs = ""
instance = 0
true.instance = 0
LOOP
next.quote = INDEX(curdata,infile(filenum,QUOTE.CHAR),true.instance+1)
UNTIL next.quote = 0
* increment true.instance anytime we find a quote char
true.instance += 1
* it doesn't count as a quote if it's preceded
* by an escape character
IF next.quote # 1 AND curdata[next.quote-1,1] # escchar
THEN
* don't want to increment instance until we know
* we actually found the next instance of the quote char
instance += 1
quote.locs<instance>=next.quote
END
REPEAT
END
* process alt quote character
IF infile(filenum,ALT.QUOTE.CHAR)
THEN
alt.quote.locs = ""
alt.instance = 0
true.instance = 0
LOOP
next.quote = INDEX(curdata,infile(filenum,ALT.QUOTE.CHAR),true.instance+1)
UNTIL next.quote = 0
* increment true.instance anytime we find a quote char
true.instance += 1
* it doesn't count as a quote if it's preceded
* by an escape character
IF next.quote # 1 AND curdata[next.quote-1,1] # escchar
THEN
* don't want to increment alt.instance until we know
* we actually found the next instance of the quote char
alt.instance += 1
alt.quote.locs<alt.instance>=next.quote
END
REPEAT
IF alt.instance
THEN
* check for uneven number of alt.quote.chars (mismatched)
IF MOD(alt.instance,2)
THEN
* mismatched alternate quote characters. Ignore last
* one and warn user.
errmessage = 074331
errargs<1> = infile(filenum,ALT.QUOTE.CHAR)
errargs<2> = filenum
errargs<3> = row.num<filenum>
CALL *UVPRINTMSG(errmessage,errargs)
END
* now look for quote.chars within alt.quote.chars, and
* if any are found, don't count them as quote.chars, since
* they're intended to be literal.
alt.quote.len = LEN(infile(filenum,ALT.QUOTE.CHAR))
FOR i = 2 TO alt.instance STEP 2
* subtract length of alt quote char as well to get length
* of the string only
IF alt.quote.locs<i> > 0
THEN
substr.len = alt.quote.locs<i> - alt.quote.locs<i-1> - alt.quote.len
substr = curdata[alt.quote.locs<i-1>+alt.quote.len,substr.len]
j=1
LOOP
quote.char.fnd = INDEX(substr,infile(filenum,QUOTE.CHAR),j)
UNTIL NOT(quote.char.fnd)
* offset should be the actual location of the quote.char
* within curdata, not the substring.
offset = alt.quote.locs<i-1> + quote.char.fnd
* now pull offset value from quote.locs because it's
* intended to be a literal character, not a quote
LOCATE offset IN quote.locs SETTING pos
THEN
quote.locs = DELETE(quote.locs,pos)
instance -= 1
END
j += 1
REPEAT
END
NEXT i
END
END
IF infile(filenum,QUOTE.CHAR) # ""
THEN
* now that we've weeded out the literal quote characters,
* check for uneven number of quote.chars (mismatched)
IF MOD(DCOUNT(quote.locs,@FM),2)
THEN
* mismatched quote characters. Ignore last
* one and warn user.
errmessage = 074331
errargs<1> = infile(filenum,QUOTE.CHAR)
errargs<2> = filenum
errargs<3> = row.num<filenum>
CALL *UVPRINTMSG(errmessage,errargs)
END
* if I don't have at least one pair of quote chars, then
* I don't have to check quotes after all
IF instance < 2 THEN check.quotes = 0
END
END ; * check.quotes
FOR thiscolnum = 1 TO DCOUNT(infile(filenum,COL.NUMBER),@FM)
* process all the columns for this file
curdata = sdata(filenum, THIS.RECORD)
IF curdata = ""
THEN
thiscol = ""
leftdata = ""
GOTO start.next.col
END
byteorder = destfile(dfilenum, BYTE.ORDER)
thisformat = infile(filenum, SRC.FORMAT)<thiscolnum>
BEGIN CASE
CASE thisformat = "DECIMAL" OR thisformat = "CURRENCY" OR thisformat = "RAW"
* if length defined, use length
IF infile(filenum, WIDTH)<thiscolnum>
THEN
end.of.col = infile(filenum, WIDTH)<thiscolnum>
thiscol = LEFT(curdata, end.of.col)
leftdata = RIGHT(curdata, LEN(curdata)-end.of.col)
END
ELSE
* look for next column seperator
IF infile(filenum, NEXT.SEP.CHAR)<thiscolnum>
THEN
thissep = infile(filenum, NEXT.SEP.CHAR)<thiscolnum>
END ELSE thissep = infile(filenum, COL.SEP.CHAR)
end.of.col = INDEX(curdata, thissep, 1)
* make sure it was not escaped
next.instance = 1
escape.check:
IF (end.of.col > 1) AND (escchar # "")
THEN
LOOP
WHILE curdata[end.of.col-1, 1] = escchar
next.instance += 1
end.of.col = INDEX(curdata, thissep, next.instance)
UNTIL NOT(end.of.col)
REPEAT
END
* make sure it isn't between quote characters
within.quotes = 0
IF check.quotes AND end.of.col
THEN
LOOP
LOCATE end.of.col IN quote.locs BY "AR" SETTING pos
THEN
* ran into trouble. end.of.col character shouldn't
* fall on a quote character's location
errmessage = 074357
errargs<1> = row.num<filenum>
GOTO error.exit
END
* if pos is an even number, we're between quotes
WHILE NOT(MOD(pos,2)) AND pos <= DCOUNT(quote.locs,@FM)
next.instance += 1
within.quotes = 1
end.of.col = INDEX(curdata, thissep, next.instance)
WHILE end.of.col
REPEAT
END
* if we found a new end.of.col because the original was
* within quotes, we have to go back up and check for
* escape character again.
IF within.quotes AND end.of.col THEN GOTO escape.check
IF NOT(end.of.col)
THEN
* never found the end of column. Everything
* will be considered one single column.
thiscol = curdata
leftdata = ""
END
ELSE
thiscol = LEFT(curdata, end.of.col-1)
leftdata.len = LEN(curdata)-end.of.col-LEN(thissep)+1
leftdata = RIGHT(curdata, leftdata.len)
END
column.length = LEN(thiscol)
* remove escape characters
* convert escaped escape to specchar -- there shouldn't
* be any row separator characters left in this column,
* so we're safe using that as specchar (two of them
* concatenated in case there was a single escaped one
* that got left in this column.
IF escchar
THEN
specchar = rowsep:rowsep
thiscol = CHANGE(thiscol,escchar:escchar,specchar)
no.of.escchars = DCOUNT(thiscol,escchar) - 1
* remove remaining escapes
thiscol = CHANGE(thiscol,escchar,"")
* convert specchar to escape
thiscol = CHANGE(thiscol,specchar,escchar)
no.of.escchars = no.of.escchars + DCOUNT(thiscol,escchar) - 1
END ELSE no.of.escchars = 0
* remove quote characters
* use the quote.locs array so we don't remove the
* literal quote characters
quotes.removed = 0
IF DCOUNT(quote.locs,@FM)
THEN
* have to use remove.array so that REMOVE pointer
* is reset each time we come into this loop
remove.array = quote.locs
LOOP
REMOVE quote.pos FROM remove.array SETTING more
IF quote.pos < 0 AND quote.pos # ""
THEN CONTINUE
WHILE quote.pos # "" AND column.length >= quote.pos
* adjust by whatever escchars we already removed
quote.pos = quote.pos - quotes.removed - no.of.escchars
thiscol[quote.pos,1] = rowsep:rowsep
CONVERT rowsep:rowsep TO "" IN thiscol
quotes.removed += 1
UNTIL NOT(more)
REPEAT
END
* remove all alternate quote characters
* use the alt.quote.locs array so we don't remove the
* literal alternate quote characters (escaped)
IF DCOUNT(alt.quote.locs,@FM)
THEN
counter = 0
* have to use remove.array so that REMOVE pointer
* is reset each time we come into this loop
remove.array = alt.quote.locs
LOOP
REMOVE quote.pos FROM remove.array SETTING more
IF quote.pos < 0 AND quote.pos # ""
THEN CONTINUE
WHILE quote.pos # "" AND column.length >= quote.pos
* adjust by whatever escchars and quote chars
* we already removed
quote.pos = quote.pos - counter - no.of.escchars - quotes.removed
thiscol[quote.pos,1] = rowsep:rowsep
CONVERT rowsep:rowsep TO "" IN thiscol
counter += 1
UNTIL NOT(more)
REPEAT
END
END
* handle packed decimal
IF thisformat = "DECIMAL"
THEN
decval = 0
IF byteorder = 1
THEN
FOR i = LEN(thiscol) TO 1 STEP -2
decval = decval * 10
decval += 10*(MOD(MOD(SEQ(thiscol[i,1]), 16),10))
decval += MOD(INT(SEQ(thiscol[i,1])/16), 10)
NEXT i
END
ELSE
FOR i = 1 TO LEN(thiscol) STEP 2
decval = decval * 10
decval += 10*(MOD(MOD(SEQ(thiscol[i,1]), 16),10))
decval += MOD(INT(SEQ(thiscol[i,1])/16), 10)
NEXT i
END
thiscol = decval
END
ELSE IF thisformat = "CURRENCY"
THEN
* handle currency
* remove "," and $, should be a number
CONVERT "$" TO "" IN thiscol
CONVERT "," TO "" IN thiscol
* remove all but last "."
LOOP
WHILE COUNT(thiscol, ".") > 1
thiscol = CHANGE(thiscol, ".", "", 1)
REPEAT
END
CASE thisformat = "BYTEINT"
* byteint is 1 char
end.of.col = 1
thiscol = SEQ(curdata[1,1])
leftdata = RIGHT(curdata, LEN(curdata)-end.of.col)
CASE thisformat = "INTEGER"
* integer is 4 characters
thiscol = 0
multiplier = 1
IF byteorder = 1
THEN
FOR i = 4 TO 1 STEP -1
thiscol += SEQ(curdata[i,1]) * multiplier
multiplier = multiplier * 256
NEXT i
END
ELSE
FOR i = 1 TO 4
thiscol += SEQ(curdata[i,1]) * multiplier
multiplier = multiplier * 256
NEXT i
END
leftdata = RIGHT(curdata, LEN(curdata)-4)
CASE thisformat = "SMALLINT"
* smallint is 2 characters
IF byteorder = 1
THEN
thiscol = SEQ(curdata[1,1]) + SEQ(curdata[2,1])*256
END
ELSE
thiscol = SEQ(curdata[2,1]) + SEQ(curdata[1,1])*256
END
leftdata = RIGHT(curdata, LEN(curdata)-2)
CASE thisformat = "FLOAT"
* float is 8 characters
* Interpret an IEEE floating point value from the number stored in buff.
* IEEE specifies a 64 bit representation for floating point which
* consists of:
*
* 1 sign bit (bit 63) 0 = +, 1 = -
* 11 exponent bits (bit 62 - 52) 2^(exponent - 1023)
* 52 fraction bits (bit 51 - 0) With implied leading 1
*
* The 8 bytes of the floating point result are stored in REVERSE order
* (low-to-high as addresses ascend) This is not to be confused with
* the 80386 byte swapping. Floating point values are not swapped, but
* reversed.
* punt and use system call to convert it, possibly after reversing the bytes
IF byteorder = 1
THEN
thiscol = ''
FOR i = 8 TO 1 STEP -1
thiscol := curdata[i, 1]
NEXT i
END
ELSE
thiscol = LEFT(curdata, 8)
END
thiscol = SYSTEM(1210, thiscol)
leftdata = RIGHT(curdata, LEN(curdata)-end.of.col)
CASE thisformat = "DOUBLE"
* double is 8 characters
thiscol = 0
multiplier = 1
IF byteorder = 1
THEN
FOR i = 8 TO 1 STEP -1
thiscol += SEQ(curdata[i,1]) * multiplier
multiplier = multiplier * 256
NEXT i
END
ELSE
FOR i = 1 TO 8
thiscol += SEQ(curdata[i,1]) * multiplier
multiplier = multiplier * 256
NEXT i
END
leftdata = RIGHT(curdata, LEN(curdata)-8)
END CASE
start.next.col:
coldata(filenum, thiscolnum) = thiscol
sdata(filenum, THIS.RECORD) = leftdata
IF DCOUNT(quote.locs,@FM)
THEN
* figure out the adjusted quote locations for the next
* column so we know where the quotes are
IF (infile(filenum,WIDTH)<thiscolnum> > 0)
THEN adjustment = infile(filenum,WIDTH)<thiscolnum>
ELSE adjustment = column.length + LEN(thissep)
quote.locs = SUBS(quote.locs,REUSE(adjustment))
END
IF DCOUNT(alt.quote.locs,@FM)
THEN
* figure out the adjusted alt.quote locations for the next
* column so we know where the alt.quotes are
IF (infile(filenum,WIDTH)<thiscolnum> > 0)
THEN adjustment = infile(filenum,WIDTH)<thiscolnum>
ELSE adjustment = column.length + LEN(thissep)
alt.quote.locs = SUBS(alt.quote.locs,REUSE(adjustment))
END
IF thiscol # "" THEN havedata = 1
NEXT thiscolnum
NEXT filenum
*
* Done breaking all of the columns apart into coldata for this row
*
all.eof = 1
filenum = 0
*
* Make sure we actually found some data somewhere
LOOP
filenum += 1
IF NOT(sdata(filenum, EOF)) THEN all.eof = 0
UNTIL NOT(all.eof) OR NOT(havedata) OR (filenum >= numfiles)
REPEAT
IF NOT(all.eof) OR havedata
THEN
* assemble the destination record
outrec = ""
*
num.of.cols = DCOUNT(destfile(dfilenum,DCOL.NUMBER),@FM)
FOR destcolnum = 1 TO num.of.cols
thiscol = ""
keyoffset =0
FOR numsource = 1 TO DCOUNT(destfile(dfilenum, SRC.FILE.NO)<destcolnum>, @VM)
fromfile = destfile(dfilenum,SRC.FILE.NO)<destcolnum,numsource>
fromcol = destfile(dfilenum,SRC.COL.NO)<destcolnum,numsource>
thiscol := coldata(fromfile, fromcol)
NEXT numsource
* run conversion if given
conv.type = destfile(dfilenum,CONV.TYPE)<destcolnum>
BEGIN CASE
CASE conv.type = "I"
thiscol = ICONV(thiscol, destfile(dfilenum, CONV.CODE)<destcolnum>)
CASE conv.type = "O"
thiscol = OCONV(thiscol, destfile(dfilenum, CONV.CODE)<destcolnum>)
CASE conv.type = "B"
* For a conversion type B, we can write all the itypes now.
* We'll set a flag so it only gets done once.
*
* If it's AUTOSIZE then file hasn't been created yet so we have to
* use the dictionary of the VOC to compile the I-types. We will
* keep track of the items we create and write them over to the
* dictionary of the destination file after it's created.
IF NOT(itype.flag)
THEN
IF (destfile(dfilenum,AUTOSIZE))
THEN
file.to.use = "VOC"
items.written = ""
END ELSE file.to.use = destfile(dfilenum,LOCATION)
itype.flag = 1
dfilenum = 1 ; * only one destination file for now
no.of.dcols = DCOUNT(destfile(dfilenum,DCOL.NUMBER),@FM)
* open dictionary file
OPEN "DICT",file.to.use TO fv.dictfile
ELSE
* cannot open dictionary.
CALL *UVPRINTMSG(074361, file.to.use)
GOTO skip.itype
END
dictrec.itype = "I":@FM:@FM:@FM:@FM:"10R":@FM:"S"
items.written = ""
FOR dcolnum = 1 to no.of.dcols
* If we run into trouble with any of these B types, print
* a warning and unset the conversion type so we don't do
* the conversion
IF destfile(dfilenum,CONV.TYPE)<dcolnum> = "B"
THEN
* write the conversion code onto COLUMN.B in dict
dictrec.id = destfile(dfilenum,DCOL.NAME)<dcolnum>:".B"
dictrec.itype<2> = destfile(dfilenum,CONV.CODE)<dcolnum>
WRITE dictrec.itype TO fv.dictfile,dictrec.id
LOCKED
CALL *UVPRINTMSG(074360,destfile(dfilenum,DCOL.NAME)<dcolnum>)
errargs<1> = dictrec.id
errargs<2> = destfile(dfilenum,LOCATION)
CALL *UVPRINTMSG(074362, errargs)
destfile(dfilenum,CONV.TYPE)<dcolnum> = ""
END
THEN
items.written := dictrec.id:" "
END
ELSE
CALL *UVPRINTMSG(074360,destfile(dfilenum,DCOL.NAME)<dcolnum>)
errargs<1> = dictrec.id
errargs<2> = destfile(dfilenum,LOCATION)
CALL *UVPRINTMSG(074363, errargs)
destfile(dfilenum,CONV.TYPE)<dcolnum> = ""
END
END
NEXT dcolnum
* compile dictionary
CALL *UVPRINTMSG(074365,destfile(dfilenum,LOCATION))
EXECUTE "CD ":file.to.use:" ":items.written
END
* read in compiled itype
* itype.flag indicates that the dictionary has been
* compiled.
IF itype.flag
THEN
dictrec.id = destfile(dfilenum,DCOL.NAME)<destcolnum>:".B"
READ col.itype FROM fv.dictfile,dictrec.id
ELSE
CALL *UVPRINTMSG(074360,destfile(dfilenum,DCOL.NAME)<destcolnum>)
errargs<1> = dictrec.id
errargs<2> = destfile(dfilenum,LOCATION)
CALL *UVPRINTMSG(074364,errargs)
destfile(dfilenum,CONV.TYPE)<destcolnum> = ""
GOTO skip.itype
END
* now run itype conversion
@RECORD = thiscol
thiscol = ITYPE(col.itype)
END
skip.itype:
END CASE
* check to see if this column name is one of the key columns
LOCATE destfile(dfilenum,DCOL.NAME)<destcolnum> IN destfile(dfilenum,KEY.COLUMNS) SETTING dkey.loc
THEN
outkeys(dkey.loc) = thiscol
END
thiscolnum = destfile(dfilenum,DCOL.NUMBER)<destcolnum>
IF thiscolnum # 0
THEN
* A column number of 0 indicates key only. Since this
* isn't 0, it can be included in the data record
outrec<thiscolnum> = thiscol
END
*
NEXT destcolnum
* Done building record
recnum +=1
* assemble key
* multipart keys are text mark delimited. They really only make
* sense if we're writing to a TABLE, but we'll trust the user to
* understand that. Note that if the user did not define the CREATE
* TABLE parameters with the primary key corresponding to these key
* columns, the WRITEs will fail in this program. Again, the user
* should understand this.
destkey = ""
FOR i = 1 TO numkeys
IF i = numkeys
THEN destkey := outkeys(i)
ELSE destkey := outkeys(i):@TM
NEXT i
* write the destination record
IF destfile(dfilenum,AUTOSIZE)
THEN
IF recnum > destfile(dfilenum,AUTOSIZE.ROWS)
THEN
* write row after autosize complete
GOTO writeit
END
ELSE
* save record
saverows(recnum, 1) = outrec
saverows(recnum, 2) = destkey
totsize += len(outrec)
autosize.check:
IF recnum = destfile(dfilenum,AUTOSIZE.ROWS)
THEN
* autosize
* compute average row size
avrecsiz = totsize/destfile(dfilenum,AUTOSIZE.ROWS)
* average record size
CALL *UVPRINTMSG(074341, avrecsiz)
* size of input
totinsize = 0
FOR filenum = 1 TO numfiles
totinsize += sdata(filenum, FILESIZE)
NEXT filenum
CALL *UVPRINTMSG(074342, totinsize)
CALL *UVPRINTMSG(074343, totinsize/avrecsiz)
min.mod = INT(totinsize/2048)
* there's a minimum for the minimum modulus!
IF min.mod < 1 THEN min.mod = 1
CALL *UVPRINTMSG(074344, min.mod)
* create the file
exeline = "CREATE.FILE "
exeline := destfile(dfilenum, LOCATION):" DYNAMIC MINIMUM.MODULUS "
exeline := min.mod
errargs<1> = exeline
CALL *UVPRINTMSG(074335, errargs)
EXECUTE exeline
* open the file
OPEN destfile(dfilenum, LOCATION) TO fv.outfile
ELSE
errmessage = 074339
errargs<1> = destfile(dfilenum,LOCATION)
GOTO error.exit
END
* write all the saved records
FOR i = 1 TO destfile(dfilenum,AUTOSIZE.ROWS)
WRITE saverows(i, 1) TO fv.outfile, saverows(i, 2)
LOCKED
errmessage = 074337
errargs<1> = saverows(i,2)
errargs<2> = i
errargs<3> = destfile(dfilenum, LOCATION)
CALL *UVPRINTMSG(errmessage,errargs)
END
ELSE
errmessage = 074338
errargs<1> = saverows(i,2)
errargs<2> = i
errargs<3> = destfile(dfilenum, LOCATION)
GOTO error.exit
END
NEXT i
* if we had any B conversion types, copy the
* dict items from the VOC into the dest file
IF itype.flag
THEN
items.written = TRIM(items.written)
no.to.copy = DCOUNT(items.written," ")
CALL *UVPRINTMSG(074366,errargs)
FOR i = 1 to no.to.copy
this.item = FIELD(items.written," ",i)
cmd = "COPYI FROM DICT VOC TO DICT ":destfile(dfilenum,LOCATION):" ":this.item:" DELETING"
EXECUTE cmd CAPTURING bequiet
NEXT i
* now close the dict VOC and reopen the dict of
* the destination file.
CLOSE fv.dictfile
OPEN "DICT",destfile(dfilenum,LOCATION) TO fv.dictfile
ELSE
* cannot open dictionary.
CALL *UVPRINTMSG(074361, destfile(dfilenum,LOCATION))
GOTO skip.itype
END
END
END
END
END
ELSE
* normal write
writeit:
WRITE outrec TO fv.outfile, destkey
LOCKED
errmessage = 074337
errargs<1> = destkey
errargs<2> = destfile(dfilenum, LOCATION)
CALL *UVPRINTMSG(errmessage,errargs)
END
ELSE
errmessage = 074338
errargs<1> = destkey
errargs<2> = destfile(dfilenum, LOCATION)
GOTO error.exit
END
END
END
* Display some type of movement
IF recnum = 1 THEN PRINT
IF NOT(MOD(recnum,25)) THEN
PRINT '*':
IF NOT(MOD(recnum,1000)) THEN
** " processed."
PRINT FMT(recnum,"R#15"):processed
END
END
UNTIL all.eof AND NOT(havedata)
* before we allow ourselves to drop out of the loop altogether,
* make sure that the total number of records wasn't less than
* AUTOSIZE.ROWS, because if it was, we didn't write anything
* yet and we need to go back and do that.
IF destfile(dfilenum,AUTOSIZE) AND recnum < destfile(dfilenum,AUTOSIZE.ROWS)
THEN
* readjust AUTOSIZE.ROWS and go back up to create file
destfile(dfilenum,AUTOSIZE.ROWS) = recnum
GOTO autosize.check
END
REPEAT
** " total processed."
PRINT FMT(recnum,"R#15"):UVREADMSG(85323,"")
* close all input files
FOR filenum = 1 TO numfiles
CLOSE infile(filenum, FILE.NUMBER)
NEXT filenum
RETURN
******************************************************
* Print the configuration file information that the
* program has loaded. This is for debugging purposes.
******************************************************
print.configuration:
PRINT "The parameters are:"
FOR filenum = 1 TO numfiles
PRINT "FILE #":filenum
STATUS fvstat from infile(filenum, 1)
ELSE
PRINT "error statusing file"
GOTO print.next.file
END
PRINT " LOCATION='":fvstat<20>:"'"
PRINT " should be='":infile(filenum, LOCATION):"'"
PRINT " size=":fvstat<6>
PRINT " ROW SEPARATOR(s)='":infile(filenum, ROW.SEP.CHAR):"'"
PRINT " COLUMN SEPARATOR(s)='":infile(filenum, COL.SEP.CHAR):"'"
PRINT " QUOTE CHARACTER='":infile(filenum, QUOTE.CHAR):"'"
PRINT " ALT QUOTE CHARACTER='":infile(filenum, ALT.QUOTE.CHAR):"'"
PRINT " ESCAPE CHARACTER='":infile(filenum, ESCAPE.CHAR):"'"
no.cols = DCOUNT(infile(filenum,COL.NUMBER),@FM)
PRINT " number of columns=":no.cols
FOR thiscolnum = 1 TO no.cols
PRINT " COLUMN #":infile(filenum,COL.NUMBER)<thiscolnum>
PRINT " SOURCE FORMAT=":
PRINT infile(filenum, SRC.FORMAT)<thiscolnum>
PRINT " WIDTH=":
PRINT infile(filenum, WIDTH)<thiscolnum>
PRINT " NEXT.SEPARATOR='":
PRINT infile(filenum, NEXT.SEP.CHAR)<thiscolnum>:"'"
NEXT thiscolnum
print.next.file:
NEXT filenum
dfilenum = 1
PRINT "DESTINATION FILE"
PRINT " LOCATION='":destfile(dfilenum, LOCATION):"'"
PRINT " CREATE FILE=":destfile(dfilenum, CREATE.FILE)
PRINT " CREATE TABLE=":destfile(dfilenum, CREATE.TABLE)
PRINT " PARAMETER(S)='":destfile(dfilenum, PARAMS):"'"
PRINT " AUTOSIZE=":destfile(dfilenum, AUTOSIZE)
PRINT " AUTOSIZE ROWS=":destfile(dfilenum, AUTOSIZE.ROWS)
PRINT " KEY COLUMN(S)=":destfile(dfilenum, KEY.COLUMNS)
no.cols = DCOUNT(destfile(dfilenum,DCOL.NUMBER),@FM)
PRINT " number of columns=":no.cols
FOR i = 1 to no.cols
PRINT " COLUMN NUMBER=":destfile(dfilenum,DCOL.NUMBER)<i>
PRINT " COLUMN NAME='":destfile(dfilenum,DCOL.NAME)<i>:"'"
PRINT " SOURCE FILE NUMBER(S)='":destfile(dfilenum,SRC.FILE.NO)<i>:"'"
PRINT " SOURCE COLUMN NUMBER(S)='":destfile(dfilenum,SRC.COL.NO)<i>:"'"
PRINT " CONVERSION TYPE='":destfile(dfilenum,CONV.TYPE)<i>:"'"
PRINT " CONVERSION CODE='":destfile(dfilenum,CONV.CODE)<i>:"'"
NEXT i
RETURN
****************************
* End of program *
****************************
program.end:
END