home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga MA Magazine 1998 #3
/
amigamamagazinepolishissue1998.iso
/
bazy
/
genealogist
/
arexx
/
gedcom2scion.rexx
< prev
next >
Wrap
OS/2 REXX Batch file
|
1994-05-21
|
21KB
|
730 lines
/****************************************************************************
* *
* *
* $VER: GEDCOM2Scion.rexx 1.08 (1 Mar 1994)
* *
* Written by Freddy Ariës *
* *
* This program was created to import GEDCOM data into the Scion database. *
* It is still very basic and not user-friendly at all. *
* I assume it will only be able to parse the most basic GEDCOM files, and *
* I can't even guarantee that it will handle these correctly... *
* *
* Even though this script does no parsing of dates, it's safer if they are *
* in the exact format "DD MMM YYYY". *
* All unrecognized fields or fields that Scion doesn't use, are skipped. *
* The database must be running for this AREXX script to work. *
* NOTE: The program generates a file DATABASE.err (where DATABASE is the *
* name of the current Scion database), containing parsing info about *
* which lines were skipped and which non-fatal errors were encountered. *
* It may be a good idea to read this file! *
* *
* TO DO: *
* - Better solution for the user-defined PERSONAL and FAMILY fields *
* (PERSUSER1, PERSUSER2, PERSUSER3, FAMUSER1, FAMUSER2) *
* Current solution: assume defaults *
* - Better parsing of dates *
* Recognition and use of ABT, BEF, AFT notations *
* *
****************************************************************************/
options failat 20; options results
arg inname inval
versionstr = "1.08"
outp = 1; usereq = 1; output = stdout
NL = '0A'x
signal on IOERR
/* parse command line options, to enable calling the script automatically,
* eg. from a function key
*/
do while inname = '?'
writeln(stdout, "INFILE/A,QUIET/S,NOREQ/S ")
pull inname inval
end
if inname ~= "" then do
if inname = "QUIET" | inname = "NOREQ" then do
inval = inname; inname = ""
end
end
if inval = "QUIET" then do
outp = 0; usereq = 0
end
else if inval = "NOREQ" then usereq = 0
if usereq & ~show('l','rexxreqtools.library') then do
if exists('libs:rexxreqtools.library') then
call addlib('rexxreqtools.library',0,-30,0)
else do
usereq = 0; outp = 1
Tell("Unable to open rexxreqtools.library - using text output")
end
end
/* These first few lines are stolen from Peter Billings - thanks Peter ;-) */
if ~show('P','SCIONGEN') then do
TermError('I am sorry to say that the SCION Genealogist' || NL ||,
'database is not available. Please start the' || NL ||,
'SCION program BEFORE using this script!')
end
myport = "SCIONGEN"
address value myport
GETDBNAME
dbname = upper(RESULT)
if outp & ~usereq then do
Tell("GEDCOM to Scion conversion script v"||versionstr||" by Freddy Ariës")
Tell("Scion (output) database: "||dbname)
end
if inname = "" then do
/* ignore the value of outp; if we can't ask for the input file,
* we can't do anything!
*/
if usereq then do
/* We need a file requester for further data */
inname = rtfilerequest('RAM:',,"GEDCOM Input File")
end
else do
Tell("Please enter the filename (with complete path) of the GEDCOM file:")
TellNN("Input file: ")
pull inname
end
if inname = '' then
TermError("ERROR: No Input File!")
end
if ~open(infile, inname, "r") then
TermError("ERROR: Input file '"inname"' not found!")
if ~open(errfile, dbname||".err", "w") then
errfile = stdout
if ~usereq then
Tell("Be patient - this may take a while...")
/* Initialize line count, individual counter and family counter */
ink = GetNextLine()
if left(ink, 6) ~= "0 HEAD" then do
close(infile)
TermError("ERROR: Invalid beginning of file - not a valid GEDCOM format")
end
lvlstr = '0'; lvl = 1; atlvl = 1
IRNArr. = 0; FGRNArr. = 0
/* Read the "HEAD" section until we find something else of level "0" */
prstot = ""
ink = ParseHeader(atlvl)
GETPROGVERSION
prsr = RESULT
prsr = "Destination: Scion Genealogist "||prsr
if ~usereq then
Tell(prsr)
else
prstot = prstot||prsr||NL
prsr = "Dest. file: "||dbname
if ~usereq then
Tell(prsr)
else do
prstot=prstot||prsr||NL||NL||"Parsing will take a while - be patient."||,
NL||"Click to start parsing..."
rtezrequest(prstot,'_Continue','Converter Message:')
end
/* Now scan the following level "0" fields for individuals;
* skip the families, for the moment
*/
irn = 0
replay = 0
do while ~eof(infile)
lvlstr = word(ink, 1)
lvl = GetNumType(lvlstr)
if lvl = atlvl then do
tagstr = upper(word(ink, words(ink)))
if tagstr = "INDI" then do
nstr = strip(word(ink, 2),'B','@'||xrange('A','Z'))
if DATATYPE(nstr) = 'NUM' then do
tp = GGetIRN(nstr)
if tp ~= 0 then
writeln(errfile, "ERROR: Duplicate person encountered: "||nstr||" (IRN "||tp||")")
irn = irn + 1
ink = ParsePerson(nstr, lvl)
if ink ~= "" then replay = 1
end
else TermError("ERROR: Cannot determine the Individual Record Number!")
end
end
/* Skip all lines with level ~= current level (0) */
if replay = 0 then ink = GetNextLine()
else replay = 0
end
if ~usereq then do
Tell("Number of persons parsed: "||irn)
GETTOTALIRN
tot = RESULT
Tell("Total number of persons in the Scion database: "||tot)
end
/* Now rescan the entire file for families; I know it is quite
* inefficient this way, but it's better to add all the persons first,
* and then establish the relations...
*/
close(infile)
if ~open(infile, inname, "r") then
TermError("ERROR: Unable to read relations!")
if ~usereq then
Tell("Scanning file again to establish relations...")
lvlstr = '0'; lvl = 1; atlvl = 1
fgrn = 0
replay = 0
do while ~eof(infile)
if replay = 0 then ink = GetNextLine()
else replay = 0
lvlstr = word(ink, 1)
lvl = GetNumType(lvlstr)
if lvl = atlvl then do
tagstr = upper(word(ink, words(ink)))
if tagstr = "FAM" then do
nstr = strip(word(ink, 2),'B','@'||xrange('A','Z'))
if DATATYPE(nstr) = 'NUM' then do
fp = GGetFGRN(nstr)
if fp ~= 0 then
writeln(errfile, "ERROR: Duplicate family encountered: "||nstr||" (FGRN "||fp||")")
fgrn = fgrn + 1
ink = ParseFamily(nstr, lvl)
if ink ~= "" then replay = 1
end
else TermError("ERROR: Cannot determine the Family Group Record Number!")
end
else if tagstr = "TRLR" then do
close(infile)
GETTOTALFGRN
ftot = RESULT
if usereq then do
GETTOTALIRN
itot = RESULT
TermError("PARSING DONE:"||NL||"Number of persons parsed: "||irn||,
NL||"Total number of persons in the Scion database: "||itot||,
NL||"Number of families parsed: "||fgrn||,
NL||"Total number of families in the Scion database: "||ftot||,
NL||NL||"DON'T FORGET TO SAVE YOUR SCION FILE!!!")
end
else do
Tell("Number of families parsed: "||fgrn)
Tell("Total number of families in the Scion database: "||ftot)
TermError("DONE! DON'T FORGET TO SAVE YOUR SCION FILE!!!")
end
end
end
/* Skip all the fields at lvl ~= this level */
end
close(infile)
if ink ~= "0 TRLR" then
TermError("ERROR: Unexpected end of file")
else
TermError("ERROR: Trailer not recognized!")
ParseHeader: PROCEDURE EXPOSE infile prstot NL outp usereq
parse arg inilvl
do while ~eof(infile)
ins = GetNextLine()
if ins = "" then
TermError("ERROR: Unexpected end of file")
lvlstr = word(ins, 1)
lvl = GetNumType(lvlstr)
if lvl <= inilvl then RETURN ins
if lvl = inilvl+1 then do
lstr = strip(delstr(ins, 1, length(lvlstr)))
curr = upper(word(lstr, 1))
if curr = "SOUR" then do
lstr = strip(delstr(lstr, 1, length(curr)))
prsr = "Source system: "||lstr
if ~usereq then
Tell(prsr)
else
prstot = prstot||prsr||NL
ins = ParseSource(lvl)
lvlstr = word(ins, 1)
lvl = lvlstr + 1
if lvl <= inilvl then RETURN ins
if lvl = inilvl+1 then do
lstr = strip(delstr(ins, 1, length(lvlstr)))
curr = upper(word(lstr, 1))
end
else TermError("ERROR: This should never happen [1]")
end
if curr = "DATE" then do
lstr = strip(delstr(lstr, 1, length(curr)))
prsr = "Creation date: "||lstr
if ~usereq then
Tell(prsr)
else
prstot = prstot||prsr||NL
end
else if curr = "FILE" then do
lstr = strip(delstr(lstr, 1, length(curr)))
prsr = "Source file: "||lstr
if ~usereq then
Tell(prsr)
else
prstot = prstot||prsr||NL
end
/* add COPR (copyright) and GEDC VERS parsing
*/
end
end
TermError("ERROR: Unexpected end of file")
ParseSource: PROCEDURE EXPOSE infile prstot NL outp usereq
parse arg namlvl
/* Scan for "NAME" and "VERS" */
do while ~eof(infile)
ins = GetNextLine()
if ins = "" then
TermError("ERROR: Unexpected end of file")
lvlstr = word(ins, 1)
lvl = GetNumType(lvlstr)
if lvl <= namlvl then RETURN ins
if lvl = namlvl+1 then do
lstr = strip(delstr(ins, 1, length(lvlstr)))
curr = upper(word(lstr, 1))
if curr = "VERS" then do
lstr = strip(delstr(lstr, 1, length(curr)))
prsr = "Version: "||lstr
if ~usereq then
Tell(prsr)
else
prstot = prstot||prsr||NL
end
else if curr = "NAME" then do
lstr = strip(delstr(lstr, 1, length(curr)))
prsr = "Created by: "||lstr
if ~usereq then
Tell(prsr)
else
prstot = prstot||prsr||NL
end
end
end
TermError("ERROR: Unexpected end of file")
ParsePerson: PROCEDURE EXPOSE infile IrnArr. errfile outp usereq
parse arg pnum, inilvl
replay = 0
prn = GetNewPerson()
IRNArr.pnum = prn
do while ~eof(infile)
if replay = 0 then ins = GetNextLine()
else replay = 0
if ins = "" then
TermError("ERROR: Unexpected end of file")
lvlstr = word(ins, 1)
lvl = GetNumType(lvlstr)
if lvl <= inilvl then RETURN ins
if lvl = inilvl + 1 then do
lstr = strip(delstr(ins, 1, length(lvlstr)))
curr = upper(word(lstr, 1))
end
if curr = "NAME" then StorePersName(strip(delstr(lstr, 1, length(curr))), prn)
else if curr = "SEX" then StorePersSex(strip(delstr(lstr, 1, length(curr))), prn)
else if curr = "BIRT" | curr = "DEAT" | curr = "BURI" then do
ins = ParsePersDatePlace(curr, prn, lvl)
replay = 1
end
else if curr = "OCCU" then StoreUser1(strip(delstr(lstr, 1, length(curr))), prn)
else if curr = "NOTE" then do
ins = StoreUser2(strip(delstr(lstr, 1, length(curr))), prn, lvl)
replay = 1
end
else
writeln(errfile, "SKIPPED: Field "||curr||" for person "||prn||"!")
end
TermError("ERROR: Unexpected end of file")
ParseFamily: PROCEDURE EXPOSE infile FGRNArr. IRNArr. errfile outp usereq
parse arg fnum, inilvl
replay = 0
do while ~eof(infile)
if replay = 0 then ins = GetNextLine()
else
replay = 0
if ins = "" then
TermError("ERROR: Unexpected end of file!")
lvlstr = word(ins, 1)
lvl = GetNumType(lvlstr)
if lvl <= inilvl then RETURN ins
if lvl = inilvl + 1 then do
lstr = strip(delstr(ins, 1, length(lvlstr)))
curr = upper(word(lstr, 1))
end
if curr = "HUSB" then StoreFamHusband(strip(delstr(lstr, 1, length(curr)), 'B', ' @'), fnum)
else if curr = "WIFE" then StoreFamWife(strip(delstr(lstr, 1, length(curr)), 'B', ' @'), fnum)
else if curr = "CHIL" then StoreFamChild(strip(delstr(lstr, 1, length(curr)), 'B', ' @'), fnum)
else if curr = "MARR" then do
ins = ParseFamDatePlace(curr, fnum, lvl)
replay = 1
end
else if curr = "NOTE" then do
ins = StoreFamUser2(strip(delstr(lstr, 1, length(curr))), fnum, lvl)
replay = 1
end
else
writeln(errfile, "SKIPPED field "||curr||" in family "||fnum||"!")
end
TermError("ERROR: Unexpected end of file!")
GetNumType: PROCEDURE EXPOSE outp infile usereq
parse arg str
if DATATYPE(str) ~= 'NUM' then
TermError("ERROR: Level indicator expected -> error in GEDCOM specification? String is "||str)
return str + 1
GetNextLine: PROCEDURE EXPOSE infile outp usereq
ins = ""
do while ins = "" & ~eof(infile)
ins = readln(infile)
if ins ~= "" then ins = strip(ins)
/* so we can check if strip(ins) is still ~= "" */
end
return ins
StorePersName: PROCEDURE
parse arg nstr, pnum
nstr = strip(nstr, 'B', '/')
ps = pos('/', nstr)
if ps = 0 then do
fname = ""
lname = nstr
end
else do
fname = left(nstr, ps-1)
lname = right(nstr, length(nstr)-ps)
end
PUTLASTNAME pnum lname
PUTFIRSTNAME pnum fname
return 1
StorePersSex: PROCEDURE
parse arg nstr, pnum
sxstr = upper(left(nstr, 1))
if sxstr ~= 'M' then sxstr = 'F'
PUTSEX pnum sxstr
return 1
ParsePersDatePlace: PROCEDURE EXPOSE infile outp usereq
parse arg idstr, pnum, inilvl
datstr = ""
plcstr = ""
do while ~eof(infile)
ins = GetNextLine()
if eof(infile) then
TermError("ERROR: Unexpected end of file at (3)!")
lvlstr = word(ins, 1)
lvl = GetNumType(lvlstr)
if lvl <= inilvl then do
select
when idstr = "BIRT" then do
if datstr ~= "" then
PUTBIRTHDATE pnum datstr
if plcstr ~= "" then
PUTBIRTHPLACE pnum plcstr
end
when idstr = "DEAT" then do
if datstr ~= "" then
PUTDEATHDATE pnum datstr
if plcstr ~= "" then
PUTDEATHPLACE pnum plcstr
end
when idstr = "BURI" then do
if datstr ~= "" then
PUTBURIALDATE pnum datstr
if plcstr ~= "" then
PUTBURIALPLACE pnum plcstr
end
otherwise
/* do nothing */
end
/* "BIRT", "DEAT" or "BURI" event */
RETURN ins
end
if lvl = inilvl+1 then do
lstr = strip(delstr(ins, 1, length(lvlstr)))
curr = upper(word(lstr, 1))
if curr = "DATE" then do
datstr = strip(delstr(lstr, 1, length(curr)))
end
else if curr = "PLAC" then do
plcstr = strip(delstr(lstr, 1, length(curr)))
end
else if curr = "QUAY" then do
lstr = strip(delstr(lstr, 1, length(curr)))
if DATATYPE(lstr) = 'NUM' & lstr < 2 then do
if datstr ~= "" then datstr = datstr||'?'
if plcstr ~= "" then plcstr = plcstr||'?'
end
end
end
/* Skip all fields of lvl > inilvl */
end
return 0
ParseFamDatePlace: PROCEDURE EXPOSE infile errfile FGRNArr. outp usereq
parse arg idstr, fnum, inilvl
datstr = ""; plcstr = ""
ff = GGetFGRN(fnum)
if ff = 0 then
writeln(errfile, "ERROR: Family Not Found: "||fnum||" when parsing date")
do while ~eof(infile)
ins = GetNextLine()
if ins = "" then
TermError("ERROR: Unexpected end of file (Parsing Family Events)!")
lvlstr = word(ins, 1)
lvl = GetNumType(lvlstr)
if lvl <= inilvl then do
if idstr = "MARR" & ff ~= 0 then do
if datstr ~= "" then
PUTMARRYDATE ff datstr
if plcstr ~= "" then
PUTMARRYPLACE ff plcstr
end
RETURN ins
end
if lvl = inilvl+1 then do
lstr = strip(delstr(ins, 1, length(lvlstr)))
curr = upper(word(lstr, 1))
if curr = "DATE" then do
datstr = strip(delstr(lstr, 1, length(curr)))
end
else if curr = "PLAC" then do
plcstr = strip(delstr(lstr, 1, length(curr)))
end
else if curr = "QUAY" then do
lstr = strip(delstr(lstr, 1, length(curr)))
if DATATYPE(lstr) = 'NUM' & lstr <= 1 then do
if datstr ~= "" then datstr = datstr||'?'
if plcstr ~= "" then plcstr = plcstr||'?'
end
end
end
/* Skip all fields of lvl > inilvl */
end
TermError("ERROR: Unexpected end of file (Parsed Family Events)!")
GetNewPerson: PROCEDURE EXPOSE infile outp usereq
PUTNEWPERSON
newpnum = RESULT
if newpnum = 0 then TermError("ERROR: Cannot allocate new person!")
/* if you want to see Scion in action, uncomment the next line */
/* GETPERSONWIN newpnum */
return newpnum
GetNewFamily: PROCEDURE EXPOSE infile outp usereq
parse arg irn
PUTNEWFAMILY irn
newfnum = RESULT
if newfnum = 0 then TermError("ERROR: Cannot allocate new family!")
/* if you want to see Scion in action, uncomment the next line */
/* GETFAMILYWIN newfnum */
return newfnum
StoreUser1: PROCEDURE
parse arg nstr, pnum
PUTPERSUSER1 pnum nstr
/* default: OCCUPATION */
return 1
StoreUser2: PROCEDURE EXPOSE infile outp usereq
parse arg nstr, pnum, lvl
PUTPERSUSER2 pnum nstr
/* default: COMMENTS */
l1 = lvl||" CONT"
l2 = length(l1)
ins = GetNextLine()
if length(ins) > l2 & left(ins, l2) = l1 then do
StoreUser3(right(ins, length(ins)-l2), pnum)
ins = GetNextLine()
end
return ins
StoreUser3: PROCEDURE
parse arg nstr, pnum
PUTPERSUSER3 pnum nstr
/* default: REFERENCES */
return 1
StoreFamHusband: PROCEDURE EXPOSE IRNArr. FGRNArr. errfile infile outp usereq
parse arg nstr, fnum
nstr = strip(nstr,'B','@'||xrange('A','Z'))
if DATATYPE(nstr) = 'NUM' then
do
ii = GGetIRN(nstr)
if ii = 0 then
writeln(errfile, "ERROR: Missing Personal Record for HUSBAND "||nstr)
else do
ff = GGetFGRN(fnum)
if ff = 0 then do
/* This goes wrong for multiple marriages */
ff = GetNewFamily(ii)
FGRNArr.fnum = ff
end
else do
/* There is already a family, so there is a principal; assume
* that that is the wife - add the husband as spouse
*/
PUTSPOUSE ff ii
ers = RESULT
if ers ~= 1 then do
writeln(errfile, "ERROR "||ers||" in PUTSPOUSE (HUSB) "||ff||' '||ii)
GETPRINCIPAL ff
prc = RESULT
GETSPOUSE ff
spc = RESULT
writeln(errfile, "Principal: "||prc||", Spouse: "||spc)
end
end
end
end
return 1
StoreFamWife: PROCEDURE EXPOSE IRNArr. FGRNArr. errfile infile outp usereq
parse arg nstr, fnum
nstr = strip(nstr,'B','@'||xrange('A','Z'))
if DATATYPE(nstr) = 'NUM' then
do
ii = GGetIRN(nstr)
if ii = 0 then
writeln(errfile, "ERROR: Missing Personal Record for WIFE "||nstr)
else do
ff = GGetFGRN(fnum)
if ff = 0 then do
ff = GetNewFamily(ii)
FGRNArr.fnum = ff
end
else do
PUTSPOUSE ff ii
ers = RESULT
if ers ~= 1 then do
writeln(errfile, "ERROR "||ers||" in PUTSPOUSE (WIFE) "||ff||' '||ii)
GETPRINCIPAL ff
prc = RESULT
GETSPOUSE ff
spc = RESULT
writeln(errfile, "Principal: "||prc||", Spouse: "||spc)
end
end
end
end
return 1
StoreFamChild: PROCEDURE EXPOSE IRNArr. FGRNArr. errfile infile outp usereq
parse arg nstr, fnum
nstr = strip(nstr,'B','@'||xrange('A','Z'))
if DATATYPE(nstr) = 'NUM' then
do
ii = GGetIRN(nstr)
if ii = 0 then
writeln(errfile, "ERROR: Missing Personal Record for CHILD "||nstr)
else do
ff = GGetFGRN(fnum)
if ff = 0 then do
writeln(errfile, "ERROR: Family for child "||ii||" doesn't exist! Child SKIPPED!")
end
else do
PUTCHILD ff ii
ers = RESULT
if ers ~= 1 then
writeln(errfile, "ERROR "||ers||" in PUTCHILD "||ff||' '||ii)
end
end
end
return 1
StoreFamUser1: PROCEDURE EXPOSE infile outp usereq
parse arg nstr, fnum
if fnum ~= 0 then
PUTFAMUSER1 fnum nstr
/* Default: CELEBRANT, but I use it as a CONT field for comments */
return 1
StoreFamUser2: PROCEDURE EXPOSE infile outp usereq FGRNArr.
parse arg nstr, fnum, lvl
fid = GGetFGRN(fnum)
if fid ~= 0 then
PUTFAMUSER2 fid nstr
/* Default: COMMENTS */
l1 = lvl||" CONT"
l2 = length(l1)
ins = GetNextLine()
if length(ins) > l2 & left(ins, l2) = l1 then do
StoreFamUser1(right(ins, length(ins)-l2), fid)
ins = GetNextLine()
end
return ins
/* Return the Scion IRN belonging to the GEDCOM Personal number pnum */
/* If there is no entry yet, allocate one! */
GGetIRN: PROCEDURE EXPOSE IRNArr.
parse arg pnum
return IRNArr.pnum
/* Return the Scion FGRN belonging to the GEDCOM Family number fnum */
GGetFGRN: PROCEDURE EXPOSE FGRNArr.
parse arg fnum
if FGRNArr.fnum = '' then
writeln(stdout, "ERROR: empty field in FGRN Array")
return FGRNArr.fnum
Tell: PROCEDURE EXPOSE outp
parse arg str
if outp then writeln(stdout, str)
return 0
TellNN: PROCEDURE EXPOSE outp
parse arg str
if outp then writech(stdout, str)
return 0
TermError: PROCEDURE EXPOSE infile outp usereq
parse arg str
/* If you turned off stdout, no error messages will be shown! */
if usereq then
rtezrequest(str,'E_xit','Converter Message:')
else do
Tell(str || '0A'x)
end
close(infile)
EXIT
/* Let's make sure you get a nice message when you turn off the printer :-) */
IOERR:
bline = SIGL
say "I/O error #"||RC||" detected in line "||bline||":"
say sourceline(bline)
EXIT