zOs/REXX/DBX0823

/* rexx ****************************************************************
synopsis:     DBX fun args                                       v1.4

edit macro fuer CS Nutzung von DB2 AdminTool 10.1
           (die a* Funktionen gehen auch mit tso dbx ...)

    ?            diese Hilfe
    a,aw,ac pr   naechste AuftragsId suchen fuer praefix pr
                 a: anzueigen, aw, ac entsprechendes Member editieren
    n, nt        neuen Auftrag erstellen (nt = test)
    q subSys?    query und expandiert Scope Zeilen vom Db2Catalog
                     * fuegt alle bestehenden Objekte ein
                     * ergaenzt scope Zeile mit infos, z.B tb -> ts
                     * UNDO um Expansion rueckgaengig zu machen
                     * mit q, qq, etc. Zeile selekieren,
                               sonst werden alle expandiert
                     * funktioniert nicht nur in Auftrag
                 falls SubSys angegeben wird da gesucht sonst DBAF/DBOF
    c opt?       compare source gegen target
    i subs nct changes in Db2Subsystem subSys importieren
                 subs = sub(,sub)*: Liste von Subsystemen/Umgebungen
                 sub: DBAF (lokal), RR2/DBOF (im PTA), RZ8(betr. Subsys)
                      ET, IT, PA (pta), PR (prod), pq(pta+rq2)
                          ==> Rz/Subsys des PromotionPaths
                 nct: Nachtraege:
                     leer: noch nicht in dieses SubSys importierte
                     =   : vom letzten import plus neue
                     89A : Nachtraege 8, 9 und A
    v opt?       version files erstellen für altes Verfahren
    vc vj vs vt ec ej es et nt?   : view or edit cdl,jcl,srcDdl,trgDdl
    sw rz?       WSL ins RZ rz schicken und clonen, ohne rz multiclone
    do cmd for auftraege: batchfunktion cmd fuer jeden auftrag

    opt?         Optionale Optionen =, -f, -=f etc. (fuer c, v, st)
        =        statt aktuelle source aus Db2 extrahieren
                       letzte extrahierte Version als Source brauchen
        -f       force: ignoriere QualitaetsVerletzungen

    cloneWsl     dbaMulti Funktionalitaet ist hier implementiert

Typen fuer scope und dbx q (analog adminTool, grossOderKlein ist egal):
    type                DB TS TB VW AL IX UDT UDF TG SP SQ SY
    1stelliges Kuerzel  D  S  T  V  A  X  E   F   J  O  Q  Y
Optionen im Auftrag: v10, v72, keeptgt 0, qCheck 0, dbaCheck 0
Variabeln im Auftrag (expandiert werden $varName imd ${varName})
                      varName ist case sensitive|)
    srcNm        NamensKonvention compare source (z.B. DBAF)
    trgNm        NamensKonvention compare target (z.B. DBAF)
    impNm        NamensKonvention import Ziel (z.B. DBOF)
    subsys       Db2 Subsystem (source, target, import, je nachdem)
     Optionen:   ca, bmc, ibm
   Funktionen:   ia (imp+ana), ie (ia+exe), ee,ve (exejcl), ew,vw (wsl)
************************************************************************
23. 8.2012 W. Keller v1015 für extract
               */ /* end of help
13. 7.2012 W. Keller typo in query scope
18. 5.2012 W. Keller im neuen Auftrag source DX0G target DC0G/DCVG @rz8
                     PromotionPaths angepasst und vereinfacht
 4. 4.2012 W. Keller sqlQuery via csm for dbx q and qualitycheck
 4. 4.2012 W. Keller fix, import submits after last import empty, chroi
14. 2.2012 W. Keller ca  prototype
23.12.2011 W. Keller bmc prototype
13.12.2011 W. Keller RZ0, ToolPrefix, csmCopy
 3.11.2011 W. Keller Zuegeltermine 2012
 6. 9.2011 W. Keller v10 RZ8 DE0G UND DM0G ausgebaut
29.08.2011 W. Keller v10 default mit p8 und p9 alias
24.06.2011 W. Keller v10 und v72
 8.06.2011 W. Keller stored procedures editiert und version=%
26.05.2011 W. Keller qCheck 0 eingefügt
30.03.2011 W. Keller import et, it, .... , DD0G -> it, trg dc0g, i pa
24.12.2010 W. Keller plexZ (mit location CHROI00Z...)
10.12.2010 W. Keller db2 release 915, damit instead of trigger kommen
19.11.2010 W. Keller viewEdit macht jetzt view für v* statt immer edit
14.10.2010 W. Keller dp4g und dp2g in rz4.* bzw rz2.*, Zuegelsch 2012
24. 8.2010 W. Keller zusätzliche Typen für scope und dbx q
23. 8.2010 W. Keller dbzf ausgebaut
 6. 8.2010 W. Keller vc vj vs vt ec ej es et nt? eingebaut
 5. 8.2010 W. Keller stepName recTrg instead of recSrc for target
12. 7.2010 W. Keller sq=sequence für compare (fehlt noch in q)
18. 2.2010 W. Keller class=BS0 für PTA (wegen RR25 und RR26)
11. 2.2010 W. Keller dbaCheck mit ex0
 9. 2.2010 W. Keller sendJob timeout 600 auch in extractScopeVersion
 1.12.2009 W. Keller CSM.RZ1.P0.EXEC statt CMS.DIV...
12.11.2009 W. Keller Qualitätsfehler ohne -f stoppt wieder
25.10.2009 W. Keller DeltaNew überall, -a Optione, batch und do removed
28.09.2009 W. Keller Optionen keepTgt 0 und dbaCheck 0
10.09.2009 W. Keller mask ausschalten falls kein maskFile
07.09.2009 W. Keller fix error with dbaMulti
25.08.2009 W. Keller err statt fehl in line 1001
12.08.2009 W. Keller batch (ohne for) geflickt, ac pr -r: return new mbr
12.08.2009 W. Keller Zuegelschub wird nach Datum ausgesucht
19.05.2009 P. Kuhn   DEFER im Masking ignorieren
05.03.2009 P. Kuhn   Qualitaets-Check "ts not logged" eingebaut
18.12.2008 p. kuhn   neues Delta Merge Verfahren im import fuer DQ0G
18.12.2008 p. kuhn   SW fuer DVBP im RZ2 (frueher im RZ4)
10.12.2008 p. kuhn   Timeout vom Sendjob von 30 auf 600 Sek. erhoeht.
28.11.2008 w. keller v9 Fehler bei leerer Selektion
24.09.2008 p. kuhn   v9 checks
15.09.2008 p. kuhn   beim ersten import wird cdl dbaCheck't und editiert
09.09.2008 w. keller target mit RZ (job holt ddl, version aus RZ), opt =
08.08.2008 w. keller import mit wildcards
24.07.2008 w. keller overwrite ausgebaut +25.7
24.06.2008 w. keller fix spezialFall für DB         +18.7
19.05.2008 w. keller qualitaetsKontolle in c,v und st und -f Option
13.05.2008 w. keller rw, rs, sw, st und do ... for ... eingebaut
25.02.2008 w. keller subSys argument für dbx q
22.11.2007 w. keller fun v für ObjectCompare Verfahren inkl db ddl save
                     dq0g mit eigenen Libraries
12.11.2007 w. keller DSN für neuen Wartungsstand
05.06.2007 w. keller neu
***********************************************************************/
/* Ideen, Wünsche ******************************************************
     AuftragsId aus Prototyp bestimmen
     LCTL
     sämtliche infos aus XLS
     jedesmal Zwischenspeichern mit und restore Funktion
     analyze generieren, falls möglich
     batch Funktionen ganzen Zügelschub importieren usw. ==> mit wsh|
     generierte Runs starten in richtiger Reihenfolge
     mails an Entwickler schicken
     Rückmeldung falls keine changes (leeres cdl)

     Type Tabelle
                       char   type Variabeln
                                   fuer extract
        db             d      DB
        ts             s      TS
        tb/vw/alias    a v t  -    own  name
        ix             x      IX
        userDefinedTy  e      -    sch  udt
        function       f      -    sch  udf
        trigger        j      TG   qual name
        storedProc     o      SP   qual name
        sequence       q      SQ   qual name
        synonym        y      SY   qual name

**** alte Funktion (braucht es nicht mehr) *****************************
    sw rz?       WSL aus RZ rz holen und clonen, ohne rz mulitclone
    rs rz        source ddl und version aus RZ rz holen
    st opt? rz   target ddl und version extrahieren und ins rz schicken

***********************************************************************/
    m.debug = 0
    call errReset h
    if sysvar(sysispf) = 'ACTIVE' then
        call adrIsp 'Control errors return'
    call jIni
    parse upper arg oArgs
    oArg1 = word(oArgs, 1)
    m.auftrag.dataset = 'DSN.DBX.AUFTRAG'
    m.editMacro = 0
    m.editProc  = 0
    if oArgs = '' then do
        if adrEdit('macro (oArgs) NOPROCESS', '*') <> 0 then
            call errHelp('keine Argumente und kein editMacro rc =' rc)
        m.editMacro = 1
        call adrEdit 'caps off'
        call adrEdit '(x) = member'
        m.auftrag.member = x
        m.edit.member = x
        call adrEdit '(x) = dataset'
        m.auftrag.dataset = x
        m.edit.dataset = x
        end
    if oArgs = '' | pos('?', oArgs) > 0 then
        exit help()
    call dbxIni
    m.exitValue = 0
    call work oArgs
    call sqlDisconnect
    exit m.exitValue

/*--- hier wählen wir die wirklich Arbeit aus -----------------------*/
work: procedure expose m.
parse arg wArgs
    parse upper var wArgs fun args
    args = strip(args)
    call mapReset e, 'K'
    call mapPut e, 'dol', '$'
    m.auftrag.force = 0
    do while abbrev(fun, '-')
        r = substr(fun, 3)
        if abbrev(fun, '-A') then do
             if verify(r, '.()', 'm') < 1 then do
                  m.auftrag.member = r
                  end
             else do
                 m.auftrag.dataset = dsnSetMbr(r)
                 m.auftrag.member =  dsnGetMbr(r)
                 end
             end
        else if abbrev(fun, '-F') then do
             m.auftrag.force = 1
             end
        else do
            call err 'bad opt' fun 'in' wArgs
            end
        parse var args fun args
        if fun = '' then
            return errHelp('fun missing in args:' wArgs)
        end

    if m.auftrag.dataset = 'A540769.DBX.AUFTRAG' then do
        m.libSkels = 'A540769.wk.skels(dbx'
        m.libPre   = 'A540769.DBX'
        end
    else if m.auftrag.dataset = 'DSN.DBQ.AUFTRAG' then do
        m.libSkels = 'ORG.U0009.B0106.KIDI63.SKELS(dbx'
        m.libPre   = 'DSN.DBQ'
        end
    else do
        m.libPre   = 'DSN.DBX'
        m.libSkels = 'ORG.U0009.B0106.KIDI63.SKELS(dbx'
        end
    if 0 then do   /* ??? testSkels */
        if userid() = 'A540769' then
            m.libSkels = 'A540769.wk.skels(dbx'
        say '??? test skels' m.libSkels '|||'
        end
    m.libSpezial = m.libPre'.spezial'
    call configureRZ sysvar('SYSNODE')
    m.sysRz = m.myRz
    call mapPut e, 'rexxLib', 'ORG.U0009.B0106.KIDI63.EXEC'
    call mapPut e, 'ovrDD', 'DISP=SHR,DSN='m.libPre'.MASK(OVERRIDE)'
    call mapPut e, 'libPre', m.libPre

    if fun = 'Q' then              /* macht process selber | */
        return queryScope(args)
    if m.editMacro & ^ m.editProc then do
        call adrEdit 'process'
        m.editProc = 1
        end
    if wordPos(fun, 'A AC AW') > 0 then
        return nextAuftrag(word(args, 1), substr(fun, 2), word(args, 2))
    else if fun = 'COPYDUMMY' then
        return copyDummy(args)
    else if fun = 'CLONEWSL' then
        return cloneWsl(word(args, 1), word(args, 2), 1==word(args, 3))

    call memberOpt
    if wordPos(fun, 'N NT') > 0 then
        call neuerAuftrag (fun = 'NT'), args, m.auftrag.member
    else if fun = 'C' | fun = 'V' | fun = 'ST' then
        call compare fun, args
    else if wordPos(fun, 'I IA IE') > 0 then
        call import fun, args
    else if fun = 'N' then
        call neuerNachtrag args
    else if fun = 'RS' then
        call receiveSource args
    else if fun = 'RW' then
        call receiveWSL args
    else if fun = 'SW' then
        call sendWSL args
    else if wordPos(fun, 'VC VE VJ VS VT VW EC EE EJ ES ET EW') > 0 then
        call viewEdit fun, args
    else
        call errHelp 'bad fun' fun 'in args' args, , ' '
    if m.auftrag.orig = m.auftrag.0 | m.auftrag.0 = '' then do
        end
    else do
        if abbrev(m.auftrag.orig, 'rmQu') then do
                     /* alte | Zeilen loeschen */
            oldOr = word(m.auftrag.orig, 2)
            ox = 0
            do ix = 1 to m.auftrag.0
                if abbrev(word(m.auftrag.ix, 1), '|') & ix <= oldOr then
                    iterate
                ox = ox + 1
                m.auftrag.ox = m.auftrag.ix
                end
            m.auftrag.0 = ox
            m.auftrag.orig = 'rep'
            end
        if m.editMacro & m.auftrag.dataset = m.edit.dataset ,
                            & m.auftrag.member  = m.edit.member then do
            if m.auftrag.orig = 'rep' then do
                call adrEdit 'delete .zf .zl'
                m.auftrag.orig = 0
                end
            do lx = m.auftrag.orig+1 to m.auftrag.0
                li = left(m.auftrag.lx, 72)
                call adrEdit "line_after .zl = (li)"
                end
            call adrEdit 'save', 4
            end
        else do
            call writeDsn dsnSetMbr(m.auftrag.dataset,
                                  ,m.auftrag.member), m.auftrag.,,1
            end
        end
    return
endProcedure work

/*--- ini the CS config ----------------------------------------------*/
dbxIni: procedure expose m.
    m.timeout = 600
    m.uId = strip(userid())
    if m.uId = 'A234579' then
        m.uNa = 'Marc'
    else if m.uId = 'A390880' then
        m.uNa = 'Martin'
    else if m.uId = 'A540769' then
        m.uNa = 'Walter'
    else if m.uId = 'A666308' then
        m.uNa = 'Frank'
    else if m.uId = 'A754048' then
        m.uNa = 'Alessandro'
    else if m.uId = 'A790472' then
        m.uNa = 'Agnes'
    else if m.uId = 'A828386' then
        m.uNa = 'Reni'
    else if m.uId = 'A914227' then
        m.uNa = 'Gerrit'
    else
        m.uNa = m.uId
    m.scopeTypes = 'DB TS TB VW AL IX UDT UDF TG SP SQ SY'
    m.scopeType1 = 'D  S  T  V  A  X  E   F   J  O  Q  Y'
    m.nachtragChars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
           /* PromotionPaths */
    m.iProm.1 = 'ET IT PQ PA PR'
    m.iProm.2 = 'RZ1/DBAF,RZ8/DC0G RZ1/DBTF,RZ8/DD0G,RZZ/DE0G' ,
                        'RQ2/DBOF,RR2/DBOF RR2/DBOF RZ2/DBOF'
    m.iProm.3 = 'RZ1/DBBA,RZ8/DCVG RZ1/DVTB,RZ8/DDVG,RZZ/DEVG' ,
                        'RQ2/DVBP,RR2/DVBP RR2/DVBP RZ2/DVBP'
    m.iProm.0 = 3
    return
endProcedure dbxIni

/*--- expand the import target list entered by the user
          to a list or rz/subsys, with mySub first and the rest sorted*/
iListExpand: procedure expose m.
parse upper arg srcList
    tl = iPromExpand(translate(space(srcList, 0), '/ ', '.,'))
    local = ''
    remote = ''
    do tx=1 to words(tl)
       t1 = word(tl, tx)
       if abbrev(t1, m.myRz'/') then
           local = wordInsAsc(local, t1)
       else
           remote = wordInsAsc(remote, t1)
       end
    return local remote
endProcedure iListExpand

/*--- expand a target using iProm infos to a list of rz/subsys -------*/
iPromExpand: procedure expose m.
parse arg inp
    if words(inp) <> 1 then do   /* several words, expand each */
        out = ''
        do wx=1 to words(inp)
           out = out iPromExpand(word(inp, wx))
           end
        return out
        end
    if pos('/', inp) > 0 then   /* already expanded */
        return inp
    if inp == '?*?' then do /* find current promotionPath */
        tg = m.scopeTrg.rz'/'m.scopeTrg.subSys
        do tx=2 to m.iProm.0
            if pos(tg, m.iProm.tx) > 0 then
                return m.iprom.tx
            end
        call err 'target' tg 'not in any PromotionPath'
        end
    px = wordPos(inp, m.iProm.1) /* one promotion environment */
    if px > 0 then
        return translate(word(iPromExpand('?*?'), px), ' ', ',')
    if length(inp) = 4 then     /* prepend rz to subsys */
        return m.myRz'/'inp
          /* all subsys that match something */
    alOr = iPromExpand('?*?')
    all = translate(alOr, ' ', ',')
    out = ''
    do ax = 1 to words(all)
        if pos(inp, word(all, ax)) > 0 then
            if wordPos(word(all, ax), out) < 1 then
                out = out word(all, ax)
        end
    if out \== '' then
        return out
    call err 'inp' inp ' not found in promotionPath' alOr
endProcedure iPromExpand

wordInsAsc: procedure expose m.
parse arg lst, wrds
     do wx=1
        w = word(wrds, wx)
        if w == '' then
            return space(lst, 1)
        do rx=1 to words(lst) while w > word(lst, rx)
            end
        r1 = word(lst, rx)
        if r1 == '' then
            lst = lst w
        else if w < r1 then
            lst = subWord(lst, 1, rx-1) w subWord(lst, rx)
        end
endProcedure wordInsAsc

charInsAsc: procedure expose m.
parse arg lst, chrs
     do wx=1 to length(chrs)
        c = substr(chrs, wx, 1)
        do rx=1 to length(lst) while c > substr(lst, rx, 1)
            end
        r1 = substr(lst, rx, 1)
        if rx > length(lst) then
            lst = lst || c
        else if c < r1 then
            lst = left(lst, rx-1) || c || substr(lst, rx)
        end
    return lst
endProcedure wordInsAsc

/*--- batch funktionen -----------------------------------------------*/
batchOld: procedure expose m.
parse upper arg args
    m.auftrag.dataset = 'DSN.DBX.AUFTRAG'
    wx = 1
    do forever
        w1 = word(args, wx)
        if w1 = '' then
            return 0
        if w1 = 'ADATASET' then do
            m.auftrag.dataset = word(args, wx+1)
            wx = wx+2
            end
        else if w1 = 'DO' then do
            fx = wordPos('FOR', args, wx)
            if fx < 1 then
                 call err 'DO ohne FOR in' args
            cmd = subWord(args, wx+1, fx-wx-1)
            do wx=fx+1
                ww = word(args, wx)
                if ww = '' then
                    leave
                m.auftrag.member = ww
                say 'batch do' cmd 'for' ww '...'
                call work cmd
                end
            end
        else do
            if wordPos(translate(w1), 'A AC AW') > 0 then do
                drop m.auftrag.member
                cmd = subword(args, wx)
                end
            else do
                m.auftrag.member = w1
                cmd = subword(args, wx+1)
                end
            say 'batch do' cmd 'for mbr' m.auftrag.member
            call work cmd
            return 0
            end
        end
    return 0
endProcedure batchOld

/*--- create the necessary dbx libries in the specified rz -----------*/
copyDummy: procedure expose m.
parse arg rz
    call copyDummy1 rz, 'DSN.DBX.AUFTRAG(DUMMY)'
    call copyDummy1 rz, 'DSN.DBX.AUTO(DUMMY)'
    call copyDummy1 rz, 'DSN.DBX.CDL(DUMMY)'
 /* call copyDummy1 rz, 'DSN.DBX.DBAUTO(DUMMY)'
    call copyDummy1 rz, 'DSN.DBX.DBSRCCAT(DUMMY)'
    call copyDummy1 rz, 'DSN.DBX.DBSRCDDL(DUMMY)'
    call copyDummy1 rz, 'DSN.DBX.DBTRGCAT(DUMMY)'
    call copyDummy1 rz, 'DSN.DBX.DBTRGDDL(DUMMY)'
 */
    call copyDummy1 rz, 'DSN.DBX.JCL(DUMMY)'
    call copyDummy1 rz, 'DSN.DBX.MASK'
 /* call copyDummy1 rz, 'DSN.DBX.OVRCAT(DUMMY)' */
    call copyDummy1 rz, 'DSN.DBX.SENDCF(DUMMY)'
    call copyDummy1 rz, 'DSN.DBX.SPEZIAL(DUMMY)'
    call copyDummy1 rz, 'DSN.DBX.SRCCAT(DUMMY)'
    call copyDummy1 rz, 'DSN.DBX.SRCDDL(DUMMY)'
    call copyDummy1 rz, 'DSN.DBX.TRGCAT(DUMMY)'
    call copyDummy1 rz, 'DSN.DBX.TRGDDL(DUMMY)'
    return 0
 endProcedure copyDummy

copyDummy1: procedure expose m.
parse arg sys, dsn
    if sysDsn("'"dsn"'") <> 'OK' then
        call writeDsn dsn, x, 0, 1
    call csmCopy dsn, sys'/'dsn
    return

/*--- die Konfiguration pro db2 Subsys -------------------------------*/
configureSubsys: procedure expose m.
    parse arg rz, subsys
    call mapPut e, 'subsys', subsys
    if rz = 'RZ8' then
        call mapPut e, 'location', 'CHROI000'subsys
    else if rz = 'RZZ' then
        call mapPut e, 'location', 'CHROI00Z'subsys
    else
        call mapPut e, 'location', 'CHSKA000'subsys
    return
endProcedure configureSubsys

/*--- die Konfiguration pro RZ ---------------------------------------*/
configureRZ: procedure expose m.
    parse arg rz, rel px, toolV
    if rz = 'RZ0T' then
        rz = 'RZ0'
    if rz = '' then
        rz = m.myRz
    else
        m.myRz = rz
    m.jobCard = 'jobCa'
    call mapPut e, 'jobCla', 'LOG'
    rx = pos(rz'/', m.iProm.2)
    if rx < 1 then
        m.mySub = '?noSubsys?'
    else
        m.mySub = substr(m.iProm.2, rx+4, 4)
    call mapPut e, 'rz', rz
    zz = overlay('Z', rz, 2)
    call mapPut e, 'zz', zz
    if rel == '' then
        rel = 1015
    if px == '' then
        px = if(rz\=='RZ0', 'P0', 'PA')
    call mapPut e, 'db2rel', rel
    call mapPut e, 'db2relAl', px
    call mapPut e, 'dsnload', if(rz=='RZ0', 'DSN', 'DB2@') ,
           || '.'zz'.'px'.DSNLOAD'
    if toolV \== '' then do
        say 'v72 nicht mehr untertstuetzt, v10 wird benutzt'
        toolV = mapGet(e, 'toolVers', 10)
        toolV = ''
        end
    call mapPut e, 'toolVers', toolV
 /* if toolV == 10 then do  */
        call mapPut e, 'toolPreP', 'DSN.TOOLS.'zz'.P0'
        call mapPut e, 'toolPreC', 'DSN.TOOLS.'zz'.C0'
 /*     end
    else if toolV == 72 then do
        call mapPut e, 'toolPreP', 'DSN.TOOLS.'zz'.P8'
        call mapPut e, 'toolPreC', 'DSN.TOOLS.'zz'.C8'
        end
    else
        call err 'bad toolVersion' toolV
 */ if rz = 'RZ1' then do
        if m.libPre = 'DSN.DBQ' then do
            m.mySub  = 'DQ0G'
            m.jobCard = 'jobCQ'
            call mapPut e, 'toolPreP', 'DSN.ADB72.P0'
            call mapPut e, 'toolPreC', 'DSN.ADB72.C0'
            end
        end
    else if rz = 'RR2' then do
        call mapPut e, 'jobCla', 'BS0'
        end
    return
endProcedure configureRZ

/*--- Member Namen prüfen und Auftrag lesen---------------------------*/
memberOpt: procedure expose m.
    if m.auftrag.dataset <> m.libPre'.AUFTRAG' then
        call err 'dbx sollte' m.libPre'.AUFTRAG editieren, nicht' ,
                    m.auftrag.dataset
    m8 = substr(m.auftrag.member, 8, 1)
    if pos(m8, 'CW')  < 1 then
        call err 'Member muss 8 stellig sein und mit C oder W enden',
                       'nicht' m.auftrag.member
    m.optOvr = 0 /* (m8 == 'W') & (m.libPre ^== 'DSN.DBQ') */
    m.optAuto = 1
    call readAuftrag '', m.auftrag.dataset, m.auftrag.member
    return
endProcedure memberOpt

/*--- Auftrag einlesen -----------------------------------------------*/
readAuftrag: procedure expose m.
parse arg sys, pds, mbr
    editingAuftrag = 0
    if sys = '' & m.editMacro then do
        call adrEdit '(em) = member'
        call adrEdit '(ed) = dataset'
        editingAuftrag = ed = pds & em = mbr
        end
    if editingAuftrag then do
        if adrEdit('(zl) = lineNum .zl', 4) = 4 then
            zl = 0
        m.auftrag.0 = zl
        do lx=1 to zl
            call adrEdit "(li) = line" lx
            m.auftrag.lx = li
            end
        end
    else do
        dsn = dsnSetMbr(m.auftrag.dataset, m.auftrag.member)
        if sys = '' then
          if sysDsn("'"dsn"'") <> 'OK' then
            call err 'auftrag' dsn 'existiert nicht:' sysDsn("'"dsn"'")
        call readDsn sys'/'dsn, m.auftrag.
        end
    m.auftrag.orig = m.auftrag.0
    return
endProcedure readAuftrag

/*--- naechste AuftragsNummer suchen ---------------------------------*/
nextAuftrag: procedure expose m.
parse arg pre, make, rz opt
    if abbrev(rz, '-') then do
        opt = rz
        rz = ''
        end
    opt = translate(opt)
    if rz = '' | rz = '*' then
        rz = m.myRz
    if m.myRz <> 'RZ1' then
        call err 'Auftrag für RZ' rz 'muss aus RZ1 erstellt werden'
    auft = m.libPre'.AUFTRAG'
    call mAdd mCut(na, 0), auft, 'DSN.DBA.CLON.WSL'
    max = pre
    do nx=1 to m.na.0
        lmm = lmmBegin(dsnSetMbr(m.na.nx, pre'*'))
        mb = lmmNext(lmm)
        fi = mb
        la = ''
        do cnt=2 by 1 while mb <> ''
            la = mb
            mb = lmmNext(lmm)
            end
        call lmmEnd lmm
        say left(fi, 8) '-' left(la, 8)right(cnt-2, 5) ,
            'member in' dsnSetMbr(m.na.nx, pre'*')
        if la >> max then
            max = la
        end
    nn = left(max, 7, '0')
    do cx=7 by-1 to length(pre)+1,
            while pos(substr(nn, cx, 1), '0123456789') > 0
        end
    if cx >= 7 then
        nn = ''
    else do
        pp = 1 + substr(nn, cx+1)
        if length(pp) > 7-cx then
            nn = ''
        else
            nn = left(nn, cx) || right(pp, 7-cx, 0)
        end
    if length(nn) <> 7 then do
        say 'max Auftrag' max 'kein naechster bestimmbar'
        end
    else if make = '' then do
        say 'max Auftrag' max 'naechster' nn'?'
        end
    else do
        nn = nn || make
        say 'max Auftrag' max 'naechster' nn
        m.auftrag.0 = 0

        call neuerAuftrag 0, rz, nn
        dsnNN = dsnSetMbr(auft, nn)
        call writeDsn dsnNN, m.auftrag.
        if opt = '-R' then
            nop
        else if rz = 'RZ1' then
            call adrIsp "edit dataset('"dsnNN"')", 4
        else
            call writeDsn rz'/'dsnNN, m.auftrag.
        end
    m.auftrag.0 = '' /* do not write back the new auftrag | */
    if opt = '-R' then
        m.exitValue = nn
    return 0
endProcedure nextAuftrag

/*--- einen neuen Auftrag initialisieren -----------------------------*/
neuerAuftrag: procedure expose m.
parse arg isTst, rz, auftName
    if  rz = '' then
        rz = m.myRz
    else
        call configureRz rz
    if isTst then do
        ow = m.uid
        maPr = 'T' || left(translate(m.uNa), 3, 'X')
        comMask = m.libPre'.MASK('maPr'PROT)'
        impMask = m.libPre'.MASK('maPr'$subsys)'
        end
    else do
        ow = 'S100447'
        comMask = m.libPre'.MASK(PROT$trgNm)'
        impMask = m.libPre'.MASK($trgNm$impNm)'
        end
    comIgno = m.libPre'.MASK(IGNORE)'
    impIgno = ''
    if m.auftrag.0 <> 0 then
        call err 'fun n erstellt neuen Auftrag nur in leeres Member'
                             /* wahrscheinlichen Zügelschub bestimmen*/
    zglS = '20120210 20120511 20120810 20121109 2013???? 2014????'
    zi = date('s')
    zi = overlay(right(substr(zi, 5, 2)+1, 2, 0), zi, 5)
    do wx=1 while zi >> word(zglS, wx)
        end
    zglSchub = if(isTst, 'test', word(zglS, wx) '??:00')

    call mAdd auftrag                                      ,
        , addDateUs('auftrag ' auftName ow)                ,
        , '  Zuegelschub' zglSchub                         ,
        , '  Besteller   pid     name    tel'              ,
        , '  comMask    ' comMask                          ,
        , '  comIgno    ' comIgno                          ,
        , '  impMask    ' impMask                          ,
        , '  impIgno    ' impIgno                          ,
        , 'source RZ8.DX0G'                                ,
        , '  ts' left(auftName, 4)'A1P.A%'                 ,
        , 'target RZ8.'if(left(auftName, 2) == 'XB', 'DCVG', 'DC0G')
    return
endProcedure neuerAuftrag

neuerNachtrag: procedure expose m.
parse upper arg opt
    call analyseAuftrag
    call addNachtrag
    return
endProcedure neuerNachtrag

nextNachtrag: procedure expose m.
    parse arg nt
    nx = pos(nt, m.nachtragChars) + 1
    if nx > length(m.nachtragChars) then
        call err 'kein Nachtrag char mehr nach' nt
    return substr(m.nachtragChars, nx, 1)
    m.e.nachtrag = nt
    return nt
endProcedure nextNachtrag

/*--- compare: Funktionen c, v und st --------------------------------*/
compare: procedure expose m.
parse upper arg fun, sendToRz
    opts = ''
    do forever
        if abbrev(sendToRz, '=') then do
            sendToRz = strip(substr(sendToRz, 2))
            opts = opts'='
            end
        else if abbrev(sendToRz, '-') then do
            opts = opts || substr(word(sendToRz, 1), 2)
            sendToRz = subword(sendToRz, 2)
            end
        else
            leave
        end
    cmpLast = pos('=', opts) > 0
    if fun = 'C' then
        function = 'compare'
    else if fun = 'ST' then do
        if sendToRz = '' | sendToRz = '*' then
            call errHelp 'ST without sendToRz'
        call mapPut e, 'toRz', sendToRz
        function = 'sendTarget' sendToRz
        end
    else if fun = 'V' then
        function = 'version'
    else
        call err 'bad fun' fun
    call analyseAuftrag
    if (m.scopeSrc.rz = m.sysRz ,
       | (wordPos(m.scopeSrc.rz, 'RZ8 RZZ') > 0 & m.sysRZ = 'RZ1') ,
       ) & m.e.qCheck \== 0 then do
        if qualityCheck(getDb2Catalog('SRC')) then
            if pos('F', opts) < 1 & \ m.auftrag.force then
                return
            else
                say 'wegen Option -f Verarbeitung',
                      'trotz Qualitaetsfehlern'
        end
    nacLast = m.e.nachtrag
    if nacLast = '?' & cmpLast then
        call err 'c = oder v = ohne vorangaengiges c oder v'
    if nacLast = '?' | m.nacImp then
        m.e.nachtrag = nextNachtrag(nacLast)
    call mapPut e, 'mbrNac', left(m.e.auftrag, 7)m.e.nachtrag
    m.o.0 = 0
    call readDsn m.libSkels || m.jobCard')', m.i.
    call mapPut e, 'jobName', 'Y'left(m.e.auftrag, 7)
    call mapPut e, 'fun', function opts
    call namingConv m.scopeTrg.rz, m.scopeTrg.subsys, 'trgNm'
    call namingConv m.scopeSrc.rz, m.scopeSrc.subsys, 'srcNm'
    call mapExpAll e, o, i

    call mapPut e, 'mask', shrDummy(mapExp(e, m.e.comMask), 1)
    call mapPut e, 'ignore', shrDummy(mapExp(e, m.e.comIgno))
    call bmcVarsProf 0

    if 0 then   /* db ddl extrahieren ja / nein ???? */
        call extractSrcTrg o, 'DB', cmpLast left(m.e.auftrag, 7)nacLast
    if m.e.tool == ibm then
        call extractSrcTrg o, '', cmpLast left(m.e.auftrag, 7)nacLast
    else if m.e.tool == bmc then
        call bmcSrcTrg cmpLast m.e.auftrag
    else if m.e.tool == ca  then do
        call configureSubsys m.scopeSrc.rz, m.scopeSrc.subsys
        end
    if fun = 'C' then do
        if m.optOvr then do
            call mapPut e, 'ovr', 'OVR'
            call readDsn m.libSkels'Ovr)', m.ovr.
            call mapExpAll e, o, ovr
            call mapPut e, 'src', 'OVR'
            end
        if m.e.keepTgt == 0 then
            call mapPut e, 'keepTgt', ''
        else
            call mapPut e, 'keepTgt', 'KEEPTGT,'
        call readDsn m.libSkels ,
                || if(m.e.tool=='IBM', 'comp', left(m.e.tool, 1)'Com'),
                || ')', m.cmp.
        call mapExpAll e, o, cmp
        if m.e.tool == ca  then
            call caDDL o, scopeSrc, dsnGetMbr(mapGet(e, 'mask'))
        end
    if fun = 'ST' then do
        call readDsn m.libSkels'ST)', m.st.
        call mapExpAll e, o, st
        end
    call writeSub o
    call mAdd auftrag, addDateUs(function ,
                    left('===', 3*cmpLast)m.e.nachtrag,
                    m.scopeTrg.rz'.'m.scopeTrg.subSys ,
                    mapExp(e, "'${libPre}." ,
                ||  if(m.e.tool=="IBM","srcCat",m.e.tool"Cdl") ,
                ||  "($mbrNac)'"))
    return
endProcedure compare

/*--- find the naming convention for a rz and subsystem --------------*/
namingConv: procedure expose m.
parse arg rz, subsys, var
    if rz = '.' then do
        if pos('.', subSys) > 0 then
            call err 'namingConv old target' subsys
        if pos('/', subSys) > 0 then
            parse var subsys rz '/' subsys
        else
            rz = m.sysRz
        end
    if strip(rz) = 'RZ1' then
        t = strip(subsys)
    else
        t = 'DBOF'
    if var ^== '' then
        call mapPut e, var, t
    return t
endProcedure namingConv

/*--- write jcl and submit it ----------------------------------------*/
writeSub: procedure expose m.
parse arg o, rz, noWri
    userSubmits = 0 /* edit jcl and user submits it */
    if noWri <> 1 then do
        jcl = m.libPre'.JCL('m.e.auftrag')'
        call mStrip o, 't'
        do ox=1 to m.o.0
            if length(m.o.ox) > 70 then
                call debug 'o.'ox 'len' length(m.o.ox)':' m.o.ox
            end
        call writeDsn jcl, m.o., ,1
        if userSubmits then /* edit dataset and user may submit it */
            call adrIsp "edit dataset('"jcl"')", 4
        end
    if (noWri <> 1) & (rz = '' | rz = m.sysRz) then do
         if ^ userSubmits then
            call adrTso "sub '"jcl"'"
         end
    else do  /* submit jcl in another rz */
        sysl = csmSysDsn(rz'/')
        if sysl = '*/' then
            sysl = ''
        iral = dsnAlloc(sysl 'SYSOUT(T) dd(ir) .WRITER(INTRDR)')
        call writeDDBegin ir
        call writeDD ir, m.o.
        call writeDDend 'IR'
        interpret subword(irAl, 2)
        end
    return
endProcedure writeSub


/*--- view or edit a member -----------------------------------------*/
viewEdit: procedure expose m.
parse upper arg fun 2 wh, nac
    call analyseAuftrag
    if wh = 'C' then
        d = copies(m.e.tool, m.e.tool \== 'IBM')'CDL'
    else if wh = 'E' then
        d = 'EXEJCL'
    else if wh = 'J' then
        d = 'JCL'
    else if wh = 'S' then
        d = 'SRCDDL'
    else if wh = 'T' then
        d = 'TRGDDL'
    else if wh = 'W' then
        d = 'BMCWSL'
    if nac == '' then
        nac = m.e.nachtrag
    if wh == 'J' then
        d = m.libPre'.'d'('m.e.auftrag')'
    else
        d = m.libPre'.'d'('left(m.e.auftrag,7)nac')'
    if fun == 'E' then
        call adrIsp "edit dataset('"d"')", 4
    else
        call adrIsp "view dataset('"d"')", 4
    return
endProcedure viewEdit
/*--- return jcl either dd dummy or dd disp=shr,dsn=... --------------*/
shrDummy: procedure expose m.
parse arg dsn, keepEmpty
    if dsn \= '' then
        return 'DISP=SHR,DSN='translate(dsn)
    else if keepEmpty == 1 then
        return ''
    else
        return 'DUMMY'
endProcedure shrDummy

/*--- funktion i -----------------------------------------------------*/
import: procedure expose m.
parse upper arg fun, rzSubSysList opt .
    call analyseAuftrag
    if m.e.nachtrag = '?' then
        call err 'vor i=import braucht es compare'
    if opt <> '' then
        nop
    else if words(m.targets) > 1 then
            call err 'i=import mit mehreren targets muss Nachtraege',
               'explizit angeben, z.B. dbx i' rzSubSysList m.e.nachtrag
    else if m.cmpLast then
            call err 'i=import mit c = oder v = muss Nachtraege',
               'explizit angeben, z.B. dbx i' rzSubSysList m.e.nachtrag
    if ^ m.nacImp then do
        cdl = cdlDsnCheck(m.e.nachtrag)
        if m.e.dbaCheck == 0 then nop
        else if m.e.tool \== 'IBM' then
            say 'dbaCheck for' m.e.tool 'not implemented'
        else do
            if m.editMacro then
                dbaParm = 'EX0'
            else
                dbaParm = 'END'
            call adrIsp "edit dataset('"cdl"') macro(dbacheck)",
                         "parm(dbaParm)", 4
            end
        end
    call readDsn m.libSkels || m.jobCard')', m.jc.
    call readDsn m.libSkels || left(m.e.tool, m.e.tool\=='IBM')'Imp)',
        , m.ic.
    list = iListExpand(rzSubSysList, 0)
    if list = '' then
        call err 'no targets in list "'rzSubSysList'"'
    impCnt = 0
    call configureRz m.sysRz
    if fun = 'IA' then
        fu2 = 'Ana'
    else if fun = 'IE' then
        fu2 = 'AnaExe'
    else
        fu2 = ''
    call mapPut e, 'fun', strip('import'fu2 left(rzSubSysList, 30))
    call mapPut e, 'jobName', 'Y'left(m.e.auftrag, 7)
    fu2 = fun fu2
    m.jOut.0 = 0
    call mapExpAll e, jOut, jc  /* Jobcard expandieren */
    j0 = m.jOut.0
    rz = '?'
    do lx = 1
        r1 = word(list, lx)
        parse var r1 r '/' subsys
        if r <> rz | subsys = '' then do
            if impCnt <> 0 then do
                if rz <> m.sysRz then do
                    if symbol('m.sCdl.0') \== 'VAR' then do
                        call readDsn m.libSkels'sCdl)', m.sCdl.
                        call readDsn m.libSkels'subRz)', m.subRz.
                        end
                    if m.impMbrs == '' then
                        call err 'int no impMbrs'
                    call mapPut e, 'mbrNac',
                            , left(m.e.auftrag, 7)left(m.impMbrs, 1)
                    call mapPut e, 'toRz', m.myRz
                    call mapExpAll e, jOut, sCdl
                    jy = m.jOut.0
                    jx = jy-1
                    m.jOut.0 = jx
                    jla = m.jOut.jy
                    cx = pos(')-', m.jOut.jx)
                    if cx < 1 | substr(m.jout.jx, cx+2) \= '' then
                        call err 'bad sCdl line' jx':'m.jOut.jx
                    m.jOut.jx = left(m.jOut.jx,cx-1) '-'
                    do mx=2 to length(m.impMbrs)
                        call mAdd jOut, left('', cx-10)',' ,
                                || left(m.e.auftrag,7) ,
                                || substr(m.impMbrs, mx,1) '-'
                        end
                    call mAdd jOut, left('', cx-10)') -'
                    call mAdd jOut, jLa
                    call mapExpAll e, jOut, subRz
                    jy = m.jOut.0
                    jla = m.jOut.jy
                    m.jOut.0 = jy-1
                    call mAddSt jOut, jAft
                    call mAdd jOut, jLa
                    end
                end
            if subsys = '' then do
                if m.jout.0 > j0 then
                    call writeSub jOut
                return
                end
            rz = r
            if rz = m.sysRz then do
                job = jOut
                m.jAft.0 = 'noUse'
                end
            else do
                job = jAft
                m.jAft.0 = 0
                end
            m.impMbrs = ''
            call configureRz rz
            impCnt = 0
            call mapPut e, 'fun', 'import'fu2 rz
            call mapPut e, 'fu2', fun
            call configureSubsys rz
            end
        if length(subsys) <> 4 then
            call err 'ungueltiges db2SubSys' subsys 'im import' rz
        call configureSubsys rz, subsys
        if rz = m.sysRz then
            impCnt = impCnt + importAdd(job, subsys,      opt, ic, fu2)
        else if m.sysRz == 'RZ1' then
            impCnt = impCnt + importAdd(job, rz'/'subsys, opt, ic, fu2)
        else
            call err 'cannot import into' rz 'from' m.sysRz
        end
endProcedure import

/*--- add a single import to jcl in o --------------------------------*/
importAdd: procedure expose m.
parse upper arg o, rzSubSys, opt, ic, fun fu2
    /* Uebergang altes auf neue Delta Merge Verfahren ++++++++ begin */
    deltaNew = 1 /* abbrev(rzSubSys,'RZ1.'),
            | (pos('.',rzSubSys) < 1 & m.sysRz = 'RZ1') */
    if deltaNew then do   /* neues delta merge verfahren */
        inDdn = 'DCHG'
        call mapPut e, 'cType', "''''T''''"
        end
    else do               /* altes delta merge verfahren */
        inDdn = 'SRCDDN2'
        call mapPut e, 'cType', "''''C''''"
        end
    call mapPut e, 'inDdn', inDdn
    /* Uebergang altes auf neue Delta Merge Verfahren ++++++++ end   */
    if opt ^= '' & opt ^= '=' then do
        nachAll = opt
        end
    else if symbol('m.imp.rzSubSys.nachtrag') ^== 'VAR' then do
        nachAll = m.compares
        end
    else do
        if opt = '=' then
            la = left(m.imp.rzSubSys.nachtrag, 1)
        else
            la = right(m.imp.rzSubSys.nachtrag, 1)
        cx = pos(la, m.compares)
        if cx < 1 then
            call err 'nachtrag' la 'von' rzSubSys m.rzSubSys.change ,
                     'nicht in Compare Liste' m.compares
        nachAll = substr(m.compares, cx + (opt ^= '='))
        end
    if nachAll = ' ' then do
        say  'alle Nachtraege schon importiert fuer' rzSubSys
        return 0
        end
    call mapPut e, 'mbrChg', left(m.e.auftrag, 7)right(nachAll, 1)
    if length(nachAll) = 1 then
        nachVB = nachAll
    else
        nachVB = left(nachAll, 1)'-'right(nachAll, 1)
    trgNm = ''
    do nx=1 to m.nachtrag.0
        if pos(m.nachtrag.nx, nachAll) < 1 then
            iterate
        act = namingConv('.', m.nachtrag.nx.trg)
        if trgNm = '' then
            trgNm = act
        else if trgNm <> act then
            call err 'targetNaming' trgNm 'wechselt zu' act ,
                'fuer nachtrag' m.nachtrag.nx 'auf' m.nachtrag.nx.trg
        end
    m.imp.seq = m.imp.seq + 1
    if length(m.imp.seq) > 3 then
        call err 'import Sequenz Ueberlauf' m.imp.seq
    m.imp.seq = right(m.imp.seq, 3, 0)
    chaPre = m.e.auftrag'.'nachVB'.'m.imp.seq
    zs = translate(strip(right(m.e.zuegelSchub, 6)))
    if m.e.tool = 'IBM' then
        call mapPut e, 'change',chaPre'.'zs/*'.V'mapGet(e,'toolVers')*/
    else
        call mapPut e, 'change',m.e.auftrag || m.imp.seq'_'zs
    call mapPut e, 'changeRem', 'zs' m.e.zuegelSchub ,
                                'auf' m.e.auftrag nachAll 'import DBX'
    call mapPut e, 'deltaVers', chaPre'.DLT'
    call namingConv '.', rzSubSys, 'impNm'
    call namingConv m.scopeSrc.rz, m.scopeSrc.subsys, 'srcNm'
    call mapPut e, 'trgNm', trgNm
    call mapPut e, 'mask',   shrDummy(mapExp(e, m.e.impMask), 1)
    call mapPut e, 'ignore', shrDummy(mapExp(e, m.e.impIgno))
    cdlPds = m.libPre'.CDL'
    call mapPut e, 'cdlPds', cdlPds
    call bmcVarsProf 1
    if m.impMbrs = '' & m.myRz \== m.sysRz then
        call mapExpAll e, o, jc  /* Jobcard expandieren */
    m.impMbrs = charInsAsc(m.impMbrs, nachAll)
    if m.e.tool = 'CA' then do
        call mapPut e, 'mbrNac', left(m.e.auftrag, 7)right(nachAll, 1)
        call mapPut e, 'impMaskMbr', dsnGetMbr(mapExp(e, m.e.impMask))
        call mapPut e, 'comIgnoMbr', dsnGetMbr(mapExp(e, m.e.comIgno))
        call mapPut e, 'bpid', m.libPre'.CASTRAT-'mapGet(e, 'mbrNac') ,
                                            || '-'m.imp.seq
        end
    sto = mapExpAllAt(e, o, ic, 1, 1)
    do while sto ^= ''
        parse var sto lx cx
        w = word(substr(m.ic.lx, cx), 1)
        if w == '$@maskDD' then do
            if mapGet(e, 'mask') \= '' then
                call mAdd o, '//MSKDDN1     DD' mapGet(e, 'mask')
            end
        else if w == '$@maskII' then do
            if mapGet(e, 'mask') \= '' then
                call mAdd o, "  MSKDDN='MSKDDN1',",
                           , " MSKOWN='DUMMY',",
                           , "  MSKNAME='DUMMY',"
            end
        else if w == '$@bmcCdl' then do
            le = left('//IMPORTIN', 13)
            do ix=1 to length(nachAll)
                call mAdd o, le || 'DD DISP=SHR,DSN=',
                                || cdlDsnCheck(substr(nachAll, ix, 1))
                le = left('//', 13)
                end
            end
        else if w ^== '$@cdl' then do
            call err 'unbekannte Anweisung' w 'in Zeile' cx m.ic.cx
            end
        else if deltaNew then do
            do ix=1 to length(nachAll)
                call mAdd o, left('//'inDdn || right(ix,3,0), 13) ,
                                || 'DD DISP=SHR,DSN=',
                                || cdlDsnCheck(substr(nachAll, ix, 1))
                end
            end
        else do
            le = left('//'inDdn, 13)
            do ix=1 to length(nachAll)
                call mAdd o, le || 'DD DISP=SHR,DSN=',
                                || cdlDsnCheck(substr(nachAll, ix, 1))
                le = left('//', 13)
                end
            end
        sto = mapExpAllAt(e, o, ic, lx, cx + length(w))
        end
    if wordPos(fun, 'IA IE') > 0 then do /* analyse step */
        if m.e.tool = ibm then
            call err 'fun' fun 'not implemented for' m.e.tool
        call readDsn m.libSkels || left(m.e.tool, 1)'Ana)', m.ia.
        call mapExpAll e, o, ia
        end
    if wordPos(fun, 'IE') > 0 then do /* analyse step */
        call readDsn m.libSkels || left(m.e.tool, 1)'Exe)', m.ie.
        call mapExpAll e, o, ie
        ej = mapExp(e, "'${libPre}.EXEJCL($mbrChg)'")
        j.1 = 'exeJcl for' m.e.auftrag 'noch nicht generiert'
        call writeDsn ej, j., 1, 1
        end
    call mAdd auftrag,
         ,  addDateUs("import" rzSubsys nachAll mapGet(e, 'change') fu2)
    return 1
endProcedure importAdd

/*--- DSN für CDL des Nachtrags zurückgeben und auf Existenz prüfen---*/
cdlDsnCheck: procedure expose m.
parse arg nt
    cdl = m.libPre'.'copies(m.e.tool, m.e.tool\=='IBM') ,
        || 'CDL('left(m.e.auftrag, 7) || nt')'
    rr = sysDsn("'"cdl"'")
    if rr <> 'OK' then
        call err 'cdl fuer' nt'. Nachtrag fehlt:' cdl rr
    return cdl
endProcedure cdlDsnCheck

/*--- Date und user rechtsbuendig einfuegen --------------------------*/
addDateUs: procedure expose m.
parse arg le
    return le right(date(s) time() m.uNa, 71-length(le))
endProcedure addDateUs                                      "'"

/*--- den aktuellen Auftrag analysieren ------------------------------*/
analyseAuftrag: procedure expose m.
    m.scopeSrc.0 = 0
    m.scopeSrc.subSys = m.mySub
    m.scopeSrc.rz     = m.myRz
    m.catSrc.0 = ''
    m.scopeTrg.0 = 0
    m.scopeTrg.subSys = m.mySub
    m.scopeTrg.rz = m.myRz
    m.catTrg.0 = ''
    m.imp.seq = -1
    m.nacImp = 0
    m.e.impMask = ''
    m.e.comMask = ''
    m.e.tool = 'IBM'
    if m.auftrag.0 = 0 then
        call err 'Auftrag ist leer'
    vaWo = 'AUFTRAG'
    varWo =  'ZUEGELSCHUB BESTELLER COMMASK COMIGNO IMPMASK IMPIGNO' ,
             'KEEPTGT DBACHECK QCHECK V72 V10 CA BMC IBM'
    ignWo = 'SW SENDWSL RECEIVEWSL RECEIVESOURCE'
    ignCh = '*|'
    lev1Wo = 'SCOPE SOURCE TARGET COMPARE VERSION IMPORT SENDTARGET' ,
             varWo 'PROTOTYPERZ'
    do lx=1 to m.auftrag.0
        li = m.auftrag.lx
        parse upper var li w1 w2 w3 .
        if w1 = '' | pos(left(w1, 1), ignCh) > 0 ,
                   | wordPos(w1, ignWo) > 0 then
            iterate
        if wordPos(w1, vaWo) < 1 then
            call err 'operation' w1 ', erwartet' vaWo 'in Zeile' lx li
        w2 = translate(word(li, 2))
        if w1 = 'AUFTRAG' then do
            if w2 ^= m.auftrag.member then
                call err 'auftrag' w2 '<> member' m.auftrag.member
            m.e.auftrag = w2
            m.e.nachtrag = '?'
            m.nachtrag.0 = 0
            if dataType(left(w3, 1), 'U') & length(w3) <= 8 then
                ow = w3
            else
                ow = 'S100447'
            call mapPut e, 'chgOwn', ow
            vaWo = lev1Wo
            end
        else if wordPos(w1, 'V72 V10') > 0 then do
            call configureRZ , , substr(w1, 2)
            end
        else if wordPos(w1, 'CA BMC IBM') > 0 then do
            m.e.tool = w1
            end
        else if wordPos(w1, varWo) > 0 then do
            m.e.w1 = word(li, 2)
            end
        else if w1 = 'PROTOTYPERZ' then do /* alte syntax sep08 ??? */
            m.scopeSrc.rz = word(li, 2)
            end
        else if wordPos(w1, 'SCOPE SOURCE TARGET') > 0 then do
            suSy = ''
            if w1 = 'SOURCE' then do
                scp = 'SCOPESRC'
                suSy = w2
                end
            else if w1 = 'TARGET' then do
                scp = 'SCOPETRG'
                if abbrev('EXPLICIT', w2, 2) then do
                    m.optAuto = 0
                    suSy = w3
                    end
                else do
                    suSy = w2
                    if abbrev('EXPLICIT', w3, 2) then
                        m.optAuto = 0
                    end
                end
            else do /* alte syntax */
                if abbrev('SOURCE', w2) then
                    scp = 'SCOPESRC'
                else if abbrev('TARGET', w2) then
                    scp = 'SCOPETRG'
                else
                    call err 'scope' w2 'nicht abk. von SOURCE TARGET',
                                        'in Zeile' lx li
                end
            if (abbrev(suSy, 'DQ0') | abbrev(suSy, 'RZ1.DQ0')) ,
                                   <> (m.libPre == 'DSN.DBQ') then
                call err 'subSys' suSy 'mit Auftrag in' m.libPre
            m.scp.0 = 0
            if pos('.', suSy) > 0 then
                parse var suSy suRz '.' suSy
            else if pos('/', suSy) > 0 then
                parse var suSy suRz '/' suSy
            else
                suRZ = ''
            if suSy <> '' then
                m.scp.subsys = suSy
            if suRz <> '' then
                m.scp.rz = suRz
            vaWo = m.scopeTypes m.scopeType1 lev1Wo
            call debug 'scope' scp m.scp.rz'.'m.scp.subsys
            end
        else if wordPos(w1, m.scopeTypes m.scopeType1) > 0 then do
            parse value analyseScope(li) with ty nm qu
            if ty = '?' then
                call err nm qu 'in scope line' lx':' strip(li)
            aa = mAdd(scp, 'scope')
            m.aa.type = ty
            m.aa.qual = qu
            m.aa.name = nm
            end
        else if wordPos(w1, 'COMPARE VERSION SENDTARGET') > 0 then do
            if w1 = 'SENDTARGET' then
                w2 = w3
            cmpLast = abbrev(w2, '=')
            w2 = strip(w2, 'l', '=')
            if length(w2) <> 1 | pos(w2, m.nachtragChars) < 1 then
                call err 'nachtrag' w2 'in Zeile' lx li
            if pos(w2, m.nachtragChars) ,
                    < pos(m.e.nachtrag, m.nachtragChars) then
                call err 'nachtrag' w2 '< vorherigem' m.e.nachtrag ,
                        'in Zeile' lx li
            if m.e.nachtrag <> w2 then do
                m.e.nachtrag = w2
                nx = m.nachtrag.0 + 1
                m.nachtrag.0 = nx
                m.nachtrag.nx = w2
                end
            m.nachtrag.nx.fun = ''
            m.nachtrag.nx.last = cmpLast
            if pos(left(w1, 1), 'CV') > 0 then
                m.nachtrag.nx.fun = left(w1, 1)
            if abbrev(w3, "'") | verify(w3, '/.', 'm') < 1 then
                t1 = m.myRz'/'m.mySub
            else
                t1 = translate(w3, '/', '.')
            m.nachtrag.nx.trg = t1
            call debug 'nachtr' nx m.nachtrag.nx 'trg' m.nachtrag.nx.trg
            m.nacImp = (w1 <> 'COMPARE')
            end
        else if w1 = 'IMPORT' then do
            parse upper var li . subsys nachAll chg .
            subsys = translate(subsys, '/', '.')
            if chgAuf <> m.e.auftrag then
            if right(nachAll, 1) <> m.e.nachtrag then
                call err 'aktueller Nachtrag' m.e.nachtrag ,
                         'aber import' nachAll 'in Zeile' lx li
            parse var chg chgAuf '.' chgNac '.' chgSeq '.' chgImp
            aa = m.e.auftrag
            if chgAuf = aa then do
                if left(chgNac, 1) <> left(nachAll, 1) then
                    call err 'Nachtrag von mismatch in Zeile' lx li
                if right(chgNac, 1) <> right(nachAll, 1) then
                    call err 'Nachtrag bis mismatch in Zeile' lx li
                end
            else if abbrev(chgAuf, aa) ,
                    & substr(chgAuf, length(aa)+4, 1) == '_' then do
                chgSeq = substr(chgAuf, length(aa)+1, 3)
                end
            else
                call err 'Auftrag mismatch in Zeile' lx li
            if chgSeq <= m.imp.seq then
                call err 'seq' chgSeq 'nicht > letzte' m.imp.seq,
                             'in Zeile' lx li
            m.nacImp = 1
            m.imp.subSys.nachtrag = nachAll
            m.imp.subSys.change   = chg
            m.imp.seq = chgSeq
            end
        else do
            call err 'ungültiger Operator' w1 'in Zeile' lx':' strip(li)
            end
        end
                               /* nachtrae durchgehen und kumulieren */
    m.targets = ''
    m.compares = ''
    m.versions = ''
    drop cmpLast
    m.cmpLast = 0
    do nx=1 to m.nachtrag.0
        m.cmpLast = m.cmpLast | m.nachtrag.nx.last
        if wordPos(m.nachtrag.nx.trg, m.targets) < 1 then
            m.targets = m.targets m.nachtrag.nx.trg
        if m.nachtrag.nx.fun = 'C' then
            m.compares = m.compares || m.nachtrag.nx
        if m.nachtrag.nx.fun = 'V' then
            m.versions = m.versions || m.nachtrag.nx
        call debug 'nachtrag' nx m.nachtrag.nx 'trg' m.nachtrag.nx.trg,
                 'all' m.targets 'fun' ,
                  m.nachtrag.nx.fun 'com' m.compares 'ver' m.versions,
                  'cmpLast' m.cmpLast
        end
    if 0 & abbrev(m.scopeSrc.subSys, 'DQ0') then
        call configureRz , '915 P0'
    if 0 then do
        say 'auftrag ' m.e.auftrag m.e.nachtrag mapGet(e, 'chgOwn')
        say '  comMask  ' m.e.comMask
        say '  comIgno  ' m.e.comIgno
        say '  impMask  ' m.e.impMask
        say '  impIgno  ' m.e.impIgno
        scp = 'SCOPESRC'
        drop subsys
        say '  scope ' m.scp.0 m.scp.subsys ,
            '  target ' m.scopeTrg.0 m.scopeTrg.subsys
        do sx=1 to m.scp.0
            say '   ' m.scp.sx.type m.scp.sx.qual'.'m.scp.sx.name
            end
        end
    call bmcVars
    return
endProcedure analyseAuftrag

/*--- eine Scope Zeile analysieren -----------------------------------*/
analyseScope: procedure expose m.
parse arg li
    parse upper var li ty w1 rest
    if wordPos(ty, m.scopeType1) > 0 then
        ty = word(m.scopeTypes, wordPos(ty, m.scopeType1))
    if wordPos(ty, m.scopeTypes) < 1 then
        return '?'
    cx = pos('.', w1)
    if cx < 1 then do
        qu = w1
        end
    else do
        qu =strip(left(w1, cx-1))
        rest = substr(w1, cx) rest
        end
    if qu = '' then
        return '? leerer Qualifier'
    if ty = 'DB' then
        return ty qu
    if left(rest, 1) = '.' then
        rest = substr(rest, 2)
    nm = word(rest, 1)
    if nm = '' then
        return '? leerer Name'
    return ty nm qu
endProcedure analyseScope

/*--- jcl generieren um Src und Trg Version und DDL zu extrahieren ---*/
extractSrcTrg: procedure expose m.
parse arg o, xx, oldSrc mbrLast
    call readDsn m.libSkels'ExVe)', m.exVe.
    call configureSubsys m.scopeSrc.rz, m.scopeSrc.subsys
    call mapPut e, 'auto', xx'AUTO'
    call mapPut e, 'src', xx'SRC'
    call mapPut e, 'trg', xx'TRG'
    mbrNac = mapGet(e, 'mbrNac')
    call mapPut e, 'what', xx'SRC'
    if ^ oldSrc then do
        call extractScopeVersion o, exVe, xx, 'SRC'
        end
    else if mbrNac <> mbrLast then do
        pr = m.libPre'.'xx'SRC'
        call copyDsn pr'DDL('mbrLast')', pr'DDL('mbrNac')', 1
        call copyDsn pr'CAT('mbrLast')', pr'CAT('mbrNac')', 1
        end
    call configureSubsys m.scopeTrg.rz, m.scopeTrg.subsys
    call mapPut e, 'what', xx'TRG'
    if m.optAuto then do
        call readDsn m.libSkels'AutMa)', m.autoMap.
        call readDsn m.libSkels'AutEx)', m.autoExt.
        call mapExpAll e, o, autoMap
        if m.sysRz = m.scopeTrg.rz then do
            call mapExpAll e, o, autoExt
            end
        else do
            mbrN = mapGet(e, 'mbrNac')
            mark = mbrN'@'time()
            autD = mapExp(e, '${libPre}.$auto($mbrNac)')
            cf   = mapExp(e, '${libPre}.SENDCF($mbrNac)')
            sndIn = sendJob1(o, m.scopeTrg.rz, 'RECTRG',
                , 'send'    autD                      ,
                , 'job      -ddJob' m.timeout'//??' cf mark       ,
                , 'receive' mapExp(e, '${libPre}.${what}DDL($mbrNac)') ,
                , 'receive' mapExp(e, '${libPre}.${what}CAT($mbrNac)') )
            call mapExpAll e, o, autoExt
            call sendJob2 o, sndIn, cf mark
            end
        end
    else do
        call extractScopeVersion o, exVe, xx, 'TRG'
        end
    return
endProcedure extractSrcTrg

/*--- Version + DDL zu extrahieren -----------------------------------*/
extractScopeVersion: procedure expose m.
parse arg o, i, ty, what
    scp = 'SCOPE'what
    /* say m.scp.rz  'scp' scp */
    if m.sysRz = m.scp.rz then do
        call extractVersionStep o, i, ty, what
        end
    else do
        mbrN = mapGet(e, 'mbrNac')
        mark = mbrN'@'time()
        cf   = mapExp(e, '${libPre}.SENDCF($mbrNac)')
        sndIn = sendJob1(o, m.scp.rz, 'REC'what,
            , 'job      -ddJob' m.timeout'//??' cf mark       ,
            , 'receive' mapExp(e, '${libPre}.${what}DDL($mbrNac)') ,
            , 'receive' mapExp(e, '${libPre}.${what}CAT($mbrNac)') )
        call extractVersionStep o, i, ty, what
        call sendJob2 o, sndIn, cf mark
        end
    return
endProcedure extractScopeVersion

/*--- einen Step um Version + DDL zu extrahieren ---------------------*/
extractVersionStep: procedure expose m.
parse arg o, i, ty, what
    scp = 'SCOPE'what
    call mapPut e, 'what', ty || what
    sto = mapExpAllAt(e, o, i, 1, 1)
    do while sto ^== ''
        parse var sto lx cx
        w = word(substr(m.i.lx, cx), 1)
        if w == '$@scope' then do
            if ty == '' then do
                do sx=1 to m.scp.0
                    sn = scp'.'sx
                    if wordPos(m.sn.type, 'UDT UDF') > 0 then do
                        t = "SCH = '"m.sn.qual"', " ,
                            m.sn.type "= '"m.sn.name"';"
                        end
                    else do
                        t = "TYPE = '"m.sn.type"',"
                        if m.sn.type <> 'DB' then
                            t = t "QUAL = '"m.sn.qual"',"
                        t = t "NAME = '"m.sn.name"'"
                        if m.sn.type = 'SP' then
                            t = t", VERSION='%'"
                        t = t';'
                        end
                    if length(t) < 30 then do
                        call mAdd o, ' ' t
                        end
                    else do
                        cx = lastPos(',', t)
                        call mAdd o, ' ' left(t, cx),
                                   , '  ' substr(t, cx+1)
                        end
                    end
                end
            else if ty == 'DB' then do
                c = getDb2Catalog(what)
                do x=1 to m.c.0
                    d1 = m.c.x.db
                    if db.d1 == 1 then
                        iterate
                    db.d1 = 1
                    call mAdd o, "  TYPE = 'DB,' NAME = '"d1"';"
                    end
                end
            else
                call err 'extractVersionStep bad ty' ty
            end
        else do
            call err 'implement stop' sto 'word' w 'in line' lx m.i.lx
            end
        sto = mapExpAllAt(e, o, i, lx, cx + length(w))
        end
    return
endProcedure extractVersionStep

/*--- add jcl to stem o to send a job to rz toRz with stepname step
         and add the remaining arguments as sendJob statements
         afterwards the caller must add the jcl and call sendJob2 ----*/
sendJob1: procedure expose m.
parse arg o, toRz, step
    oldRz = m.myRz
    call configureRz toRz
    call readDsn m.libSkels'SendJ)', m.sendJob.
    call mapPut e, 'step', step
    call mapExpAll e, o, sendJob
    do ax=4 to arg()
        call debug 'sendJob1 le' length(arg(ax)) arg(ax)'|'
        call mAdd o, arg(ax) left('-', (ax < arg()))
        end
    call mAdd o, '//DDJOB     DD *'
    stReX = m.o.0+1
    call readDsn m.libSkels || m.jobCard')', m.i.
    call mapPut e, 'jobName', 'Y'left(m.e.auftrag, 7)
    call mapPut e, 'fun', 'extract data from' toRz
    call mapExpAll e, o, i
    return oldRz stReX
endProcedure sendJob1

/*--- add the mark step to the job, translate leading // to ??
          and switch back to original rz -----------------------------*/
sendJob2: procedure expose m.
parse arg o, oldRz stReX, cfMark
    if cfMark ^= '' then do
        call mAdd o, '//         IF NOT ABEND' ,
                      'AND RC >= 0 AND RC <= 4 THEN'
        call mapPut e, 'step', 'MARKOK'
        call mapExpAll e, o, sendJob
        call mAdd o, 'mark' cfMark 'okRc0'
        call mAdd o, '//         ELSE'
        call mapPut e, 'step', 'MARKERR'
        call mapExpAll e, o, sendJob
        call mAdd o, 'mark' cfMark 'badRcOrAbend'
        call mAdd o, '//         ENDIF'
        end
    do ox = stReX to m.o.0
        if abbrev(m.o.ox, '//') then
            m.o.ox = overlay('??', m.o.ox)
        end
    call configureRz oldRz
    return
endProcedure sendJob2

/*--- return Stem fuer die CatalogInfo für Src oder Trg
          falls noetig aus Db2Catalog einlesen -----------------------*/
getDb2Catalog: procedure expose m.
parse arg wh
    st = 'CAT'wh
    if datatype(m.st.0, n) then
        return st
    else if m.st.0 ^== '' then
        call err 'getDb2Catalog('wh') bad value m.'st'.0' m.st.0
    scp = 'SCOPE'wh
    if m.sysRz = m.scp.rz then
        call sqlOConnect m.scp.subSys
    else
        call sqlOConnect m.scp.rz'/'m.scp.subSys
    call queryDb2Catalog st, wh
    m.v9.0 = 0
    call queryDb2V9 st, 'V9'
    return st
endProcedure getDb2Catalog

/*--- Information aus Db2Catalog fuer einen Scope einlesen -----------*/
queryDb2Catalog: procedure expose m.
parse arg st, what
    scp = 'SCOPE'what
    /* m.scopeTypes = 'DB TS TB VW IX AL' */
    ts = ''
    tb = ''
    ix = ''
    unQueried = 0
    do sx=1 to m.scp.0
        sn = scp'.'sx
        t = "  TYPE = '"m.sn.type"',"
        if m.sn.type <> 'DB' then
            t = t "QUAL = '"m.sn.qual"',"
        t = t "NAME = '"m.sn.name"';"
        call debug 'queryDb2Catalog' sx t
        if m.sn.type = 'DB' then
            ts = ts 'or s.dbName' sqlClause(m.sn.name)
        else if m.sn.Type = 'TS' then
            ts = ts 'or (s.dbName' sqlClause(m.sn.qual) ,
                    'and s.name' sqlClause(m.sn.name)')'
        else if m.sn.Type = 'TB' then
            tb = tb 'or (t.creator' sqlClause(m.sn.qual) ,
                    'and t.name' sqlClause(m.sn.name)')'
        else if m.sn.Type = 'IX' then
            ix = ix 'or (x.creator' sqlClause(m.sn.qual) ,
                    'and x.name' sqlClause(m.sn.name)')'
        else
            unQueried = unQueried + 1
        end
    sel = 'select s.dbName db, s.name ts , s.type, ',
                  's.partitions, s.segSize, s.log, ',
                  't.creator cr, t.name tb,' ,
                  't.status tbSta, t.tableStatus tbTbSta',
              'from sysibm.sysTableSpace S, sysibm.sysTables T'
    wh = "where s.dbName = t.dbName and s.name = t.tsName",
               "and t.type = 'T'"
    sql = ''
    if ts <> '' then
        sql = sql 'union' sel wh 'and ('substr(ts, 5)')'
    call debug 'ts sql' sql
    if tb <> '' then
        sql = sql 'union' sel wh 'and ('substr(tb, 5)')'
    call debug 'tb sql' sql
    if ix <> '' then
        sql = sql 'union' sel ', SYSIBM.SYSINDEXES X',
                    wh 'and t.creator=x.tbCreator and t.name=x.tbName',
                       'and ('substr(ix, 5)')'
    call debug 'ix sql' sql
    if sql = '' then do
         m.st.0 = 0
         if unQueried < 1 then
             say 'nothing specified in source scope'
         return 0
         end
    drop db ts cr tb type
    call sql2St substr(sql, 8), st
    if m.debug == 1 then do
        say m.st.0
        do sx = 1 to m.st.0
           say strip(m.st.sx.db)'.'strip(m.st.sx.ts) m.st.sx.type ,
                      m.st.sx.partitions m.st.sx.segSize
           end
        end
    return m.st.0
endProcedure queryDb2Catalog

/*--- haben wir schon DB2 V9 Objekte ? -------------------------------*/
queryDb2V9: procedure expose m.
parse arg sc, vv
    m.vv.0 = 0
    wh =''
    do x = 1 to m.sc.0
        wh = wh "or (cr='"m.sc.x.cr"' and tb='"m.sc.x.tb"')"
        end
    if wh == '' then
        return 0
    return sql2st("select tp,nm,v9",
             "from s100447.db2v9",
             "where V9 <> '' and (" substr(wh, 5) ")",
             "order by cr,tb,cl", vv)
endProcedure queryDb2V9

/*--- Qualitaets Pruefung fuer CatalogInfo Stem c --------------------*/
qualityCheck: procedure expose m.
parse arg c
    if m.libPre <> 'DSN.DBQ' then
        call maskIni
    o = 'AUFTRAG'
    m.o.orig = 'rmQu' m.o.orig
    m.spezialFall.done = ''
    aufOld = m.o.0
    do x=1 to m.c.0
        vDb = strip(m.c.x.db)
        n = '|| db' vDb
        if d.n ^== 1 then do
            d.n = 1
            call spezialfall vDb, substr(n, 4)
            end
        n = '|| ts' vDb'.'strip(m.c.x.ts)
        if d.n ^== 1 then do
            d.n = 1
            call spezialfall vDb, substr(n, 4)
            if m.c.x.log <> 'Y' then
                call mAdd o, n 'not logged'
            if m.c.x.partitions > 0 then
                nop
            else if m.c.x.segSize = 0 then
                call mAdd o, n 'simple TS'
            end
        n = '|| tb' strip(m.c.x.cr)'.'strip(m.c.x.tb)
        if d.n ^== 1 then do
            d.n = 1
            call spezialfall vDb, substr(n, 4)
            if m.c.x.tbTbSta = 'L' then
                call mAdd o, n 'auxilary index oder table fehlt'
            else if m.c.x.tbTbSta = 'P' then
                call mAdd o, n 'primary index fehlt'
            else if m.c.x.tbTbSta = 'R' then
                call mAdd o, n 'index auf Row ID fehlt'
            else if m.c.x.tbTbSta = 'U' then
                call mAdd o, n 'index auf unique key fehlt'
            else if m.c.x.tbTbSta = 'V' then
                call mAdd o, n 'Regenerierung interne ViewDarstellung'
            else if m.c.x.tbTbSta ^= '' then
                call mAdd o, n 'TableStatus' m.c.x.tbTbSta 'unbekannt'
            end
        end
    do vx=1 to m.v9.0
        call mAdd o, '|| V9' m.v9.vx.tp left(m.v9.vx.nm, 30) ,
                                        left(m.v9.vx.v9, 30)
        end
    return aufOld < m.o.0
endProcedure qualityCheck

/*--- für jedes Objekt aus Source Scope Eintrage in der
      Spezialfall Library suchen und falls vorhanden anzeigen --------*/
spezialFall: procedure expose m.
parse upper arg db, typ qua '.' nam
    if m.libPre = 'DSN.DBQ' then
        return
    pDb = mask2prod('DBNAME', db)
    if (typ = 'DB' | typ = 'TS') & db <> qua then
        call err 'db mismatch spezialFall('db',' typ qua'.'nam')'
    if typ = 'DB' then
        srch = typ pDb'.'
    else if typ = 'TS' then
        srch = typ pDb'.'mask2prod('TSNAME', nam)
    else if typ = 'TB' then
        srch = typ mask2prod('TBOWNER', qua)'.'mask2prod('TBNAME', nam)
    else
        call err 'bad typ' typ
    st = spezialFall'.'pDb
    if symbol('m.st.0') <> 'VAR' then do
        dsn = m.libSpezial"("pDb")"
        sy = sysDsn("'"dsn"'")
        if sy <> 'OK' then do
            m.st.0 = 0
            if sy <> 'MEMBER NOT FOUND' then
                call err 'spezialFall library' dsn':' sy
            end
        else do
           call readDsn dsn, 'M.SPEZIALFALL.'pDB'.'
           end
        end
    if m.st.0 < 1 then
        return
    offs = 999
    found = 0
    do sx = 1 to m.st.0
        fx = verify(m.st.sx, ' ')
        if fx = 0 | fx > 72 then
           iterate
        if substr(m.st.sx, fx, 1) = '*' then
           iterate
        if fx <= offs then do
            offs = fx
            m.st.sx = left(m.st.sx, 72)
            n = ''
            if pos('.', m.st.sx) > 0 then
                parse upper var m.st.sx t q '.' n .
            else
                parse upper var m.st.sx t q .
            if wordPos(t, 'DB TS TB') < 1 then
              call err 'spezialFall' pDb 'line' sx 'ungueltig:' m.st.sx
            found = match(srch, t strip(q)'.'strip(n)) ,
                    & ^ (wordPos(pDb'.'sx, m.spezialFall.done) > 0)
            if found then
                m.spezialFall.done = m.spezialFall.done  pDb'.'sx
            end
        if found then
            call mAdd auftrag, '|-'left(m.st.sx, 78)
        end
    return
endProcedure spezialFall

/*--- mask handling initialise ---------------------------------------*/
maskIni: procedure expose m.
    call maskHierarchy
    call maskRead mask.prot, m.libPre'.MASK(PROTDBAF)'
    call maskRead mask.dbof, m.libPre'.MASK(DBAFDBOF)'
    return
endProcedure maskIni

/*--- mask a whole scope  --------------------------------------------*/
maskScope: procedure expose m.
parse arg mskDsn, fr, to
    call maskRead masc, mskDsn
    do fx=1 to m.fr.0
        ty = m.fr.fx.type
        m.to.fx.type = ty
        if wordPos(ty, 'DB SG') > 0 then
            m.to.fx.qual = ''
        else if wordPos(ty, 'TS') > 0 then
            m.to.fx.qual = maskTrans(masc, 'DBNAME', m.fr.fx.qual)
        else
            m.to.fx.qual = maskTrans(masc, 'SCHEMA', m.fr.fx.qual)
        if wordPos(ty, 'DB') > 0 then
            m.to.fx.name = maskTrans(masc, 'DBNAME', m.fr.fx.name)
        else if wordPos(ty, 'TB VW AL') > 0 then
            m.to.fx.name = maskTrans(masc, 'TBNAME', m.fr.fx.name)
        else if wordPos(ty, 'SP') > 0 then
            m.to.fx.name = maskTrans(masc, 'STPNAME', m.fr.fx.name)
        else
            m.to.fx.name = maskTrans(masc, ty'NAME', m.fr.fx.name)
        end
    m.to.0 = m.fr.0
    return
endProcedure maskScope

/*--- mask test functions --------------------------------------------*/
testMask: procedure expose m.
    call maskIni
    call maskTT OWNER, GDB9998
    call maskTT DBNAME, DGDB9998
    call maskTT DBNAME, DGDB0287
    call maskTT OWNER, GDB0301
    call maskTT TSNAME, AGRFX12A2
    call maskTT OWNER, SYS123EF
    return 0
endProcedure testMask

maskTT: procedure expose m.
parse arg ty, na
    say 'maskTrans(prot,' ty',' na') =' maskTrans(mask.prot, ty, na) ,
        ' -> DBOF' maskTrans(mask.dbof, ty, maskTrans(mask.prot,ty,na))
    return

/*--- translate a prototype object to DBOF naming -------------------*/
mask2Prod: procedure expose m.
parse arg ty, na
    return translate(strip(,
        maskTrans(mask.dbof, ty, maskTrans(mask.prot,ty,na))))

/*--- translate an object of type ty and name na
           by the masking file in st --------------------------------*/
maskTrans: procedure expose m.
parse arg st, ty, na
    if symbol('m.mask.hier.ty') <> 'VAR' then
        call err 'bad type' ty
    types = m.mask.hier.ty
    do sx=1 to m.st.0
        if wordPos(m.st.sx.typ, types) < 1 then
            iterate
        if match(na, m.st.sx.in, vv) then
            return matchTrans(m.st.sx.out, vv)
        end
    return na
endProcedure maskTrans

/*--- read and analyse the masking file dsn into stem st ------------*/
maskRead: procedure expose m.
parse arg st, dsn
    maskIgno = 'COMPRESS SEGSIZE DEFER DEFINE PRIQTY SECQTY'
    call readDsn dsn, i.
    j = 0
    do i=1 to i.0
        parse var i.i t ':' s ',' d
        t = strip(t)
        if symbol('m.mask.hier.t') == 'VAR' then
            nop
        else if wordPos(t, maskIgno) > 0 then
            iterate
        else
            call err 'bad typ' t 'in maskline' i':' i.i
        j = j+1
        m.st.j.typ = t
        m.st.j.in = strip(s)
        m.st.j.out = word(d, 1)
        end
    m.st.0 = j
    return
    drop typ in out
    do wx=1 to m.st.0
        say wx m.st.wx.typ':' m.st.wx.in'==>'m.st.wx.out'|'
        end
endProcedure maskRead

/*--- initialise the type hierarchy of masking
           as input use the msgLines of the mask editor --------------*/
maskHierarchy: procedure expose m.
                     /* copy der hierarch aus masking template */
    call mAdd mCut('T', 0)                           ,
      , 'COLNAME                                     ' ,
      , 'NAME                                        ' ,
      , '  DBNAME,TSNAME,TBNAME,IXNAME,UDFNAME,      ' ,
      , '  UDTNAME,COLLNAME,PKGNAME,PGMNAME,PLNNAME  ' ,
      , '  DBRMNAME,STPNAME,SFNAME,TGNAME,GRPNAME,   ' ,
      , '  VCATNAME,GBPNAME                          ' ,
      , '  BPNAME                                    ' ,
      , '    TSBPNAME,IXBPNAME                       ' ,
      , '  SGNAME                                    ' ,
      , '    TSSGNAME,IXSGNAME                       ' ,
      , 'AUTHID                                      ' ,
      , '  SQLID,SCHEMA                              ' ,
      , '  OWNER                                     ' ,
      , '    DBOWNER,TSOWNER,TBOWNER,IXOWNER         ' ,
      , '  GRANTID                                   ' ,
      , '    GRANTOR,GRANTEE                         '
    qx = 0
    lOff = -1
    m.mask.hier = ''
    do x=1 to m.t.0
        of = verify(m.t.x, ' ', 'n')
        li = translate(m.t.x, ' ', ',')
        do while qx > 0 & word(q.qx, 1) >= of
            qx = qx -1
            end
        px = qx - 1
        if (qx = 0 | of > word(q.qx, 1))  & words(li) = 1 then do
            px = qx
            qx = qx + 1
            if qx = 1 then
                q.qx = of word(li, 1)
            else
                q.qx = of word(li, 1) subword(q.px, 2)
            end
        do wx=1 to words(li)
            w1 = word(li, wx)
            m.mask.hier = m.mask.hier w1
            if wordPos(w1, subWord(q.qx, 2)) < 1 then
                m.mask.hier.w1 = w1 subWord(q.qx, 2)
            else
                m.mask.hier.w1 = subWord(q.qx, 2)
            end
        end
    return
endProcedure maskHierarchy

/*--- Funktion q: scope Zeilen pruefen -------------------------------*/
queryScope: procedure expose m.
parse arg subSys
    subs2 = ''
    rf = 1
    isConn = 0
    if adrEdit('(rl) = lineNum .zl', 4) = 4 then
            rl = 0
    if ^ m.editMacro then
        call err 'q nicht als Macro'
    if ^ m.editProc then do
        if adrEdit('PROCESS RANGE Q', 0 4) = 0 then do
            call adrEdit '(rf) = lineNum .zfrange'
            call adrEdit '(rl) = lineNum .zlrange'
            end
        m.editProc = 1
        end
    do rx = rf by 1 while rx <= rl
        call adrEdit '(li) = line' rx
        parse value analyseScope(li) with ty nm qu
        if ty = '?' then do
            if nm <> '' then
                say nm qu 'in line' rx':' strip(li)
            if \ isConn & subsys == '' then do
                parse upper var li w1 w2 .
                if wordpos(w1, 'SOURCE TARGET') > 0 then do
                    if length(w2) = 4 | ( length(w2) = 8 ,
                          & pos(substr(w2,4,1), './') > 0) then
                    subs2 = translate(w2, '/', '.')
                    end
                end
            iterate
            end
        if \ isConn then do
            isConn = 1
            if subSys = '' then
                subSys = if(subs2 == '', m.mySub, subs2)
            subsys = translate(subsys, '/', '.')
            call sqlConnect subSys
            subsys = translate(subsys, m.mAlfLC, m.mAlfUC)
            end
        call expandScope mCut(qq, 0), ty, qu, nm
        do qx=1 to m.qq.0
            neu = m.qq.qx
            if length(neu subSys) < 70 then
                neu = left(neu, 68 - length(subSys)) '*'subSys
            else if length(neu subSys) < 80 then
                neu = neu '*'subSys
            liCm = if(qx=1, "line" rx, "line_after" (rx+qx-2))
            if adrEdit(liCm "= (neu)", 4) = 4 then
                say 'truncation line' rx':' neu
            end
        rx = rx + m.qq.0 - 1
        rl = rl + m.qq.0 - 1
        end
    call sqlDisConnect
    return 0
endProcedure queryScope

/*--- einen Scope Ausdruck expandieren -------------------------------*/
expandScope: procedure expose m.
parse arg o, ty, qu, nm
     sql = ''
     dec2s = "strip(case when abs(?) > 9223372036854775807",
                       "then char(real(?)) else char(bigint(?)) end)"
     if ty = 'DB' then
         sql = "select 'db', name, '' from sysibm.sysDatabase",
                    "where name" sqlClause(nm)
     else if ty = 'TS' then
         sql = "select 'ts', strip(dbName) || '.' || strip(tsName)," ,
                    "case when count(*) = 1 then 'tb '" ,
                         "else strip(char(count(*))) || ' tables||| '",
                    "end || min(strip(creator) ||'.'|| strip(name))",
                    "from sysibm.systables" ,
                    "where type = 'T' and dbName" sqlClause(qu),
                                    "and tsName" sqlClause(nm),
                    "group by dbName, tsName"
     else if ty = 'TB' | ty = 'VW' | ty = 'AL' then
         sql = "select case when type = 'T' then 'tb'",
                           "when type = 'V' then 'vw'",
                           "when type = 'A' then 'al'",
                                           "else '?' || type end,",
                    "strip(creator) || '.' || strip(name),",
                    "case when type = 'A' then 'for '",
                              "|| strip(location) || '.'" ,
                              "|| strip(tbCreator)||'.'||strip(tbName)",
                          "else 'ts ' || strip(dbName) ||'.'",
                                      "|| strip(tsName)",
                    "end",
                    "from sysibm.systables" ,
                    "where type =" quote(left(ty, 1), "'"),
                        "and creator" sqlClause(qu),
                        "and name" sqlClause(nm)
     else if ty = 'IX' then
         sql = "select 'ix', strip(creator) || '.' || strip(name),",
                        "'tb ' || strip(tbCreator)||'.'||strip(tbName)",
                    'from sysibm.sysIndexes' ,
                    'where creator' sqlClause(qu),
                           'and name' sqlClause(nm)
     else if ty = 'UDT' then
         sql = "select 'udt', strip(schema) || '.' || strip(name),",
                        "'source ' || strip(sourceSchema)",
                        "|| '.' || strip(sourceType)",
                    "from sysibm.sysDataTypes",
                    'where schema' sqlClause(qu),
                           'and name' sqlClause(nm)
     else if ty = 'UDF' | ty = 'SP' then
         sql = "select case when routineType = 'F' then 'udf'",
                           "when routineType = 'P' then 'sp'",
                           "else '?' || routineType end, ",
                      "strip(schema) || '.' || strip(name),",
                      "'otp=' || origin || function_type" ,
                        "|| strip(char(parm_count))",
                      "|| ' spec=' || strip(specificName)",
                      "|| ' a=' || active || ' vers=' || version",
                    "from sysibm.sysRoutines",
                    'where routineType =' quote(right(ty, 1), "'"),
                         'and schema' sqlClause(qu) ,
                         'and name' sqlClause(nm)
     else if ty = 'TG' then
         sql = "select 'tg', strip(schema) || '.' || strip(name),",
                        "'teg ' || trigTime || trigEvent||granularity",
                        "|| ' tb ' || strip(tbOwner) || '.'",
                        "||           strip(tbName)",
                    "from sysibm.sysTriggers",
                    'where seqNo=1 and schema' sqlClause(qu),
                           'and name' sqlClause(nm)
     else if ty = 'SQ' then
         sql = "select 'sq', strip(schema) || '.' || strip(name),",
                        "'start ' ||" repAll(dec2s, "?", "start"),
                        "|| ': ' ||" repAll(dec2s, "?", "minValue"),
                        "|| '-' ||" repAll(dec2s, "?", "maxValue"),
                        "|| ' inc ' ||" repAll(dec2s, "?", "increment"),
                    "from sysibm.sysSequences",
                    "where seqType='S' and schema" sqlClause(qu),
                           "and name" sqlClause(nm)
     else if ty = 'SY' then
         sql = "select 'sy', strip(creator) || '.' || strip(name),",
                        "'for ' || strip(tbCreator) || '.'" ,
                               "||strip(tbName)",
                    "from sysibm.sysSynonyms",
                    "where creator" sqlClause(qu),
                         "and name" sqlClause(nm)
     else do
        call mAdd o, ty left(qu'.'nm, 30) '* query nicht implementiert'
        return
        end
     call sqlQuery 1, sql 'order by 2', ,
        , classNew('n* SQL u f FT v, f FN v, f FI v')
     do cx=0 by 1 while sqlFetch(1, d)
             call mAdd o, lefA(m.d.ft, 3) lefA(m.d.fn, 30) m.d.fi
             end
     call  sqlClose 1
     if cx = 0 then
         call mAdd o, lefA(ty, 3) lefA(strip(qu)left('.', qu \== '') ,
                   || strip(nm), 30) '* nicht gefunden'
    return
endProcedure expandScope

/*--- return sql condition (= String oder like String) ---------------*/
sqlClause: procedure expose m.
parse arg val
     val = translate(val, '%_', '*?')
     if verify(val, '%_', 'm') > 0 then
          return 'like' quote(val, "'")
     else
          return '=' quote(val, "'")
endProcedure sqlClause

lefA: procedure expose m.
parse arg s, len
    if length(s) < len then
        return left(s, len)
    else
        return s
endProcedure lefA

/*--- copy srcDdl und srcCat aus anderem rz --------------------------*/
receiveSource: procedure expose m.
parse arg rz
    if rz = '' | rz = '*' | rz = m.myRz then
        call err 'rs receiveSource mit ungueltigem rz' rz
    call readAuftrag rz, m.auftrag.dataset, m.e.auftrag
    call analyseAuftrag
    if m.e.nachtrag = '?' then
        call err 'keine Version in Auftrag' m.e.auftrag 'im' rz
    nacMbr = left(m.e.auftrag, 7) || m.e.nachtrag
    call csmCopy rz'/'m.libPre'.srcDdl('nacMbr')', m.libPre'.'rz'DDL'
    call csmCopy rz'/'m.libPre'.srcCat('nacMbr')', m.libPre'.'rz'Cat'
    call readAuftrag   , m.auftrag.dataset, m.auftrag
    call mAdd auftrag, addDateUs('receiveSource' rz,
                        m.libPre'.'rz'Cat('nacMbr')')
    return
endProcedure receiveSource

/*--- copy wsl aus anderem rz ----------------------------------------*/
receiveWSL: procedure expose m.
parse arg rz
    if rz = m.myRz then
        rz = ''
    call analyseAuftrag
    sWsl = 'dsn.dba.clon.wsl('left(m.e.auftrag, 7)'Q)'
    sIff = 'dsn.dba.'left(m.e.auftrag, 7)'Q.IFF'
    if rz <> '' then do
        call csmCopy rz'/'sWsl, sWsl
        if adrCsm("dslist SYSTEM("rz") DSNMASK('"sIff"') SHORT", 4) = 0,
               & stemSize = 1 then
            call csmCopy rz'/'sIff, sIff
        else
            say 'iff existiert nicht im' rz
        end
    call cloneWsl '', m.e.auftrag, 1
    call mAdd auftrag, addDateUs('receiveWSL' rz)
    return
endProcedure receiveWSL

/*--- send wsl to the argument rz ------------------------------------*/
sendWSL: procedure expose m.
parse arg rz
    if rz = m.myRz then
        rz = ''
    call analyseAuftrag
    if m.versions = '' | m.compares <> '' then
        call warn 'sendWSL ohne versions oder mit compares'
    sWsl = 'dsn.dba.clon.wsl('left(m.e.auftrag, 7)'Q)'
    sIff = 'dsn.dba.'left(m.e.auftrag, 7)'Q.IFF'
    if sysDsn("'"sWsl"'") <> 'OK' then
        call err 'source wsl fehlt:' sWsl sysDsn("'"sWsl"'")
    if rz <> '' then do
        call csmCopy sWsl, rz'/'sWsl
        if sysDsn("'"sIff"'") <> 'OK' then
            say 'iff existiert nicht' sIff sysDsn("'"sIff"'")
        else
            call csmCopy sIff, rz'/'sIff
        end
    call cloneWsl rz, m.e.auftrag, 1
    call mAdd auftrag, addDateUs('sendWSL' rz)
    return
endProcedure sendWSL

/*--- clone a wsl mbr in the rz sys,
          if doRemove=1 remove old members first ---------------------*/
cloneWsl: procedure expose m.
parse arg sys, mbr, doRemove
                 /* copy multi clone jcl from target rz */
    jcl = csmSysDsn(sys'/DSN.DBA.MULTI.CLONE'right(mbr, 1)'.JCL', 0)
    CALL READdsn jcl, m.jj.
                                /* modify the jcl */
    do jx=1 to m.jj.0
        if word(m.jj.jx, 2) == '=' then do
            if word(m.jj.jx, 1) = 'SRCWSLST' then
                m.jj.jx = 'SRCWSLST =' left(mbr, 7)'Q'
            else if word(m.jj.jx, 1) = 'CLNWSLST' then
                m.jj.jx = 'CLNWSLST =' mbr
            end
        else if space(subword(m.jj.jx, 1, 2) ,1) ,
                == '//DELMBRIF IF' then do
            m.jj.jx = '//DELMBRIF IF RC =' (7 *(^doRemove)) 'THEN'
            end
        else if word(m.jj.jx, 1) == 'DELETE' then do
            nm = word(m.jj.jx, 2)
            cx = pos('(', nm)
            if right(nm, 1) = ')' & cx > 0 then
                m.jj.jx = '  DELETE' left(nm, cx)mbr')'
            else
                call err 'bad delete' jx m.jj.jx
            end
        end
    call writeSub jj, sys, 1
    return 0
endProcedure cloneWsl

warn:
    say '*** warning:' arg(1)
    return
endSubroutine warn

repAll: procedure expose m.
parse arg src, w, new
    res = ''
    cx = 1
    do forever
        nx = pos(w, src, cx)
        if nx < 1 then
            return res || substr(src, cx)
        res = res || substr(src, cx, nx-cx) || new
        cx = nx + length(w)
        end
endProcedure repAll

/**** ca *************************************************************/
caDDl: procedure expose m.
parse arg o, scp, glblCh
    do sx=1 to m.scp.0
        sn = scp'.'sx
        if m.sn.type == 'TS' then do
            call mAdd o, ' TABLESPACE' m.sn.qual m.sn.name
            call caExplode o, TABLE INDEX VIEW SYNONYM TRIGGER ,
                              MQTB_T MQTB_I MQTB_V MQTB_S ,
                              MQVW_VW MQVW_I MQVW_V MQVW_S
            end
        else if m.sn.type == 'VW' then do
            call mAdd o, ' VIEW      ' m.sn.qual m.sn.name
            end
        else
            call err 'implement type' m.sn.type 'for ca'
        end
    call readDsn m.libSkels'CCO2)', m.cco2.
    call mapExpAll e, o, cco2
    call mAdd o, ' GLBLNAME  ' glblCh                  ,
               , ' GLBLCRTR   DBX'
    glblDsn = m.libPre".caGlblCh("glblCh")"
    if sysDsn("'"glblDsn"'") \== 'OK' then
        call err 'mask' glblCh':' glblDsn sysDsn("'"glblDsn"'")
    call readDsn glblDsn, 'M.GLBL.'
    call mAddSt o, glbl
    return
endProcedure caDDL

caExplode: procedure expose m.
parse arg o, expl
    do wx=1
        e1 = word(expl, wx)
        if e1 == '' then
            return
        call mAdd o, '  EXPLODE' e1
        end
endProcedure caExplode
/**** bmc ************************************************************/
/*--- src und trg profile erstellen ----------------------------------*/
bmcVars: procedure expose m.
    m.e.profSrc = m.e.auftrag'_SRC'
    m.e.profTrg = m.e.auftrag'_TRG'
    m.e.profOwn = 'DBXAUFTR'
    return
endProcedure bmcVars

bmcVarsProf: procedure expose m.
parse arg isImport
    m.e.profChg = bmcMask2Prof(m.e.comMask)
    if isImport then
        m.e.profImp = bmcMask2Prof(m.e.impMask)
    return
endProcedure bmcVarsProf

bmcMask2Prof: procedure expose m.
parse arg mask
    m2 = translate(mapExp(e, mask))
    return word(translate(m2, '   ', '.()'), 2)'.'dsnGetMbr(m2)
endProdecure bmcMask2Prof

/*--- src und trg profile erstellen ----------------------------------*/
bmcSrcTrg: procedure expose m.
parse arg oldSrc prof
    if symbol('m.mask.hier') \== 'VAR' then
        call maskHierarchy
    if m.scopeSrc.rz \== m.sysRz | m.scopeTrg.rz \== m.sysRz then
        call err 'bmc compare on other rz not implemented'
    if m.scopeSrc.subsys \== m.scopeTrg.subsys then
        call err 'bmc compare on different subsystems not implemented'
    call configureSubsys m.scopeSrc.rz, m.scopeSrc.subsys
    call bmcProfile m.e.profOwn, m.e.profSrc, 'SCOPESRC'
    if m.optAuto then
        call maskScope m.e.comMask, 'SCOPESRC', 'SCOPETRG'
    call bmcProfile m.e.profOwn, m.e.profTrg, 'SCOPETRG'
    return
endProcedure bmcSrcTrg

bmcProfile: procedure expose m.
parse arg ow, prof, scp
    call sqlOConnect m.scp.subSys
    call sqlExec "insert into bmcacma1.CM_BLPROFILE" ,
                    "(BLPOWNER, blpName, type, template)" ,
         "values('"ow"', '"prof"', 'C', '"prof"####')", -803
    call sqlExec "delete from bmcacma1.CM_SCOPE" ,
                   "where scOwner = '"ow"' and scName = '"prof"'", 100
    do sx=1 to m.scp.0
        sn = scp'.'sx
        if m.sn.type = 'DB' then do
            q1 = m.sn.name
            q2 = ''
            end
        else do
            q1 = m.sn.qual
            q2 = m.sn.name
            end
        call sqlExec "insert into bmcacma1.CM_SCOPE" ,
          "(SCOWNER, SCName, Type, ACTION,OBJECT,NAME_PART1,NAME_PART2",
          ",MIGDT, MIGTS, MIGTB, MIGCK, MIGFK, MIGIX, MIGVW, MIGAU",
          ",MIGSY, MIGAL, MIGRO, MIGTR, MIGUC, MIGAX",
         ")values('"ow"', '"prof"', 'B', 'I'" ,
          ", '"m.sn.type"', '"q1"', '"q2"'" ,
          ",'N'  , 'Y',   'Y',  'Y',    'Y',   'Y',   'Y',  'N'",
          ",'Y'  , 'Y',   '',   'Y',    'Y',   'Y')"
        end
     call sqlCommit
     return
endProcedure bmcProfile
/* copy match begin ***************************************************/
/************************************* begin     copy      match ******/
/*--- wildCard matching with the following wildchars:
          * 0-n chars
          ? 1 char
      fill matched expressions instem st if st is non empty
      return 1 if mask matches wert ----------------------------------*/
match: procedure expose m.
parse arg wert, mask, st
    if st == '' then
        return matchRO(wert, mask)
    m.st.0 = -9
    return matchSt(wert, mask, st, 0)
endProcedure match

/*--- return the fixed prefix of maskt -------------------------------*/
matchPref: procedure
arg mask, suff
    ix = verify(mask, '*?', 'm')
    if ix = 0 then
        return mask
    else
        return left(mask, ix-1)suff
endProcedure matchPref

/*--- return true if mask matches wert -------------------------------*/
matchRO: procedure
arg wert, mask
    ix = verify(mask, '*?', 'm')
    if ix < 1 then return (mask == wert)
    if length(wert) < ix-1 then return 0
    if left(mask, ix-1) \== left(wert, ix-1) then return 0
    if substr(mask, ix, 1) == '?' then do
        if length(wert) < ix then return 0
        return matchRO(substr(wert, ix+1), substr(mask, ix+1))
        end
    mask = substr(mask, ix+1)                /* * 0 - n Chars */
    do ex = 1+length(wert) to ix by -1
        if matchRO(substr(wert, ex), mask) then return 1
        end
    return 0
endProcedure matchRO

/*--- wildCard matching: fill matched expressions instem st
      return 1 if mask matches wert ----------------------------------*/
matchSt: procedure expose m.
parse arg wert, mask, st, sx
    ix = verify(mask, '*?', 'm')
    if ix < 1 then do
        if mask \== wert then
            return 0
        m.st.0 = sx
        return 1
        end
    if \ abbrev(wert, left(mask, ix-1)) then
        return 0
    reMa = substr(mask, ix+1)
    sx = sx + 1
    if substr(mask, ix, 1) == '?' then do /* ? 1 Character */
        if length(wert) < ix then
            return 0
        m.st.sx = substr(wert, ix, 1)
        return matchSt(substr(wert, ix+1), reMa, st, sx)
        end
    do lx = 1+length(wert) to ix by -1 /* greedy: from all to empty */
        if matchSt(substr(wert, lx), reMa, st, sx) then do
            m.st.sx = substr(wert, ix, lx-ix)
            return 1
            end
        end
    return 0
endProcedure matchSt

matchTrans: procedure expose m.
parse arg mask, st
    r = ''
    ox = 1
    sx = 0
    ix = verify(mask, '*?', 'm')
    do sx=1 to m.st.0 while ix > 0
        if sx > m.st.0 then
            call err 'matchTrans('mask',' st') has only' ,
                                     m.st.0 'variables'
        r = r || substr(mask, ox, ix-ox)m.st.sx
        ox = ix+1
        ix = verify(mask, '*?', 'm', ox)
        end
    if ix > 0 then
        call err 'matchTrans('mask',' st') has only' ,
                                     m.st.0 'variables'
    return r || substr(mask, ox)
endProcedure matchTrans
/* copy match end *****************************************************/
/* copy csm begin ******************************************************
    interface to csm,
        it is integrate with adrTso, eg. dsnAlloc , 'RZ3/..' uses csm
***********************************************************************/
adrCsm:
    return adrTso('csmExec' arg(1), arg(2))
endProcedure adrCsm

csmCopy: procedure expose m.
parse arg csnFr, csnTo, ggRet
    if dsnGetMbr(csnTo) \= '' ,
         & dsnGetMbr(csnFr) <> dsnGetMbr(csnTo) then
        call err 'member rename' csnFr 'to' csnTo
    parse value csmSysDsn(csnFr) with sysFr '/' dsnFr
    parse value csmSysDsn(csnTo) with sysTo '/' dsnTo
    if sysFr = '*' & sysTo <> '*' then do
        pdsTo = dsnSetMbr(dsnTo)
        al = "SYSTEM("sysTo") DDNAME(COPYTo)",
             "DATASET('"pdsTo"') DISP(SHR)"
        alRes = dsnAlloc(systo'/'pdsTo, ,'COPYTO', '*')
        if datatype(alRes, 'n') then do
                   /* wir müssen es selbst allozieren csmxUtil
                      vergisst management class ||||| */
            say 'could not allocate' al
            say 'trying to create'
            rc = listDsi("'"dsnSetMbr(dsnFr)"' SMSINFO")
            if rc = 0 then
                mv = ''
            else if rc = 4 & sysReason = 19 then do
                mv = 'UNITCNT(30)'
                say 'multi volume' mv
                end
            else if rc \= 0 then
                call err 'listDsi rc' rc 'reason' sysReason,
                                     sysMsgLvl1 sysMsgLvl2
            al = left(al, length(al)-4)'CAT)'
            if right(sysDsSms, 7) == 'LIBRARY' ,
                | abbrev(sysDsSms, 'PDS') then
                 al = al 'DSNTYPE(LIBRARY)'
            if sysUnits = 'TRACK' then
                sysUnits = 'TRACKS'
            al = al "DSORG("sysDSorg") MGMTCLAS("sysMgmtClass")",
                "RECFM("sysREcFM") LRECL("SYSLRECL")",
                "blksize("sysBLkSIZE")",
                "SPACE("sysPrimary"," sysSeconds")" sysUnits mv
            call adrCsm "allocate" al
            end
        call adrTso 'free dd(copyTo)'
        end
    c = "'COPY" sysFr"/''"dsnFr"'' TO" ,
                        sysTo"/''"dsnSetMbr(dsnTo)"'' REPLACE'"
    return adrTso("exec 'CSM.RZ1.P0.EXEC(CSRXUTIL)'" c , ggRet)
    return
endProcedure csmCopy

csmAlloc: procedure expose m.
parse arg dsn dd disp rest ':' nn, retRc
    sys = ''
    a2 = ''
    parse value csmSysDsn(dsn) with sys '/' dsn
    if disp = '' then
        disp = 'shr'
    al = "SYSTEM("sys") DDNAME("dd")"
    if dsn <> '' then do
        a2 = "DATASET('"dsnSetMbr(dsn)"')"
        mbr = dsnGetMbr(dsn)
        if mbr <> '' then
            a2 = a2 'MEMBER('mbr')'
        end
    if abbrev(disp, 'SYSOUT(') then
        a2 = a2 disp
    else
        a2 = a2 "DISP("disp")"
    if retRc <> '' | nn = '' then do
        alRc = adrCsm('allocate' al a2 rest, retRc)
        if alRc <> 0 then
            return alRc
        return dd 'call adrTso "free dd('dd')";'
        end
    do retry=0 by 1
        alRc = adrCsm('allocate' al a2 rest, '*')
        if alRc = 0 then
            return dd 'call adrTso "free dd('dd')";'
        if retry > 0 | nn = '' | wordPos(disp, 'OLD SHR') < 1 then
            return err('cmsAlloc rc' alRc 'for' al rest)
        say 'csmAlloc rc' alRc 'for' al a2 rest '...trying to create'
        nn = al 'disp(cat)' dsnCreateAtts(dsn, nn, 1)
        call adrCsm 'allocate' nn
        call adrTso 'free  dd('dd')'
        end
endProcedure csmAlloc

csmSysDsn: procedure expose m.
parse upper arg dsn, withStar
    if pos('/', dsn) < 1 then
        sys = '*'
    else
        parse var dsn sys '/' dsn
    if sys <> '' & sys <> '*' & sys <> sysvar(sysnode) then
        return sys'/'dsn
    else if withStar == 0 then
        return dsn
    else
        return '*/'dsn
endProcedure csmSysDsn
/* copy csm end********************************************************/
/* copy sqlO   begin ***************************************************
    sql interface  mit  o und j Anbindung
***********************************************************************/
sqlOini: procedure expose m.
    if m.sqlO.ini == 1 then
        return
    call sqlIni
    m.sqlO.ini = 1
    m.sqlO.cursors  = left('', 200)
    call jIni
    call classNew 'n SqlResultRdr u JRWO', 'm',
        , "jReset m.m.cursor = arg; m.m.type = arg2;",
        , "jOpen  call sqlResultRdrOpen m, opt",
        , "jClose call sqlClose m.m.cursor",
        , "jReadO return sqlSelReadO(m)"
    call classNew 'n SqlSel u JRWO', 'm',
        , "jReset m.m.src = arg; m.m.type = arg2;",
        , "jOpen  call sqlSelOpen m, opt",
        , "jClose call sqlSelClose m",
        , "jReadO return sqlSelReadO(m)"
    call classNew 'n SqlDRS u SqlSel', 'm',
        , "jReset m.m.loc = arg; m.m.type = arg2;",
        , "jOpen  call sqlDRSOpen m, opt",
        , "jClose call sqlSelClose m",
        , "jReadO return sqlSelReadO(m)"
    call classNew 'n SqlRxConnection u', 'm',
        , "sqlQuery  return sqlRxQuery(cx, src, retOk)",
        , "sqlFetch  return sqlRxFetch(cx, dst, retOk)",
        , "sqlClose  return sqlRxClose(cx, retOk)",
        , "sqlUpdate return sqlRxUpdate(cx, src, retOk)",
        , "sqlCall   call err 'implement sqlRxCall"
    call classNew 'n SqlRxStatement u', 'm',
        , "sqlQuery  return sqlRxQuery(m.cx.cursor, src, retOk)",
        , "sqlFetch  return sqlRxFetch(m.cx.cursor, dst, retOk)",
        , "sqlClose  return sqlRxClose(m.cx.cursor, retOk)",
        , "sqlUpdate return sqlRxUpdate(m.cx.cursor, src, retOk)",
        , "sqlCall   call err 'implement sqlRxCall"
    call classNew 'n SqlCsmConnection u', 'm',
        , "sqlQuery  return sqlCsmQuery(cx, src, retOk)",
        , "sqlFetch  return sqlCsmFetch(cx, dst)",
        , "sqlClose  return 0",
        , "sqlUpdate call err 'implement sqlCsmUpdate'"   ,
        , "sqlCall   call err 'implement sqlCsmCall'"
    call classNew 'n SqlCsmStatement u', 'm',
        , "sqlQuery  return sqlCsmQuery(m.cx.cursor, src, retOk)",
        , "sqlFetch  return sqlCsmFetch(m.cx.cursor, dst)",
        , "sqlClose  return 0",
        , "sqlUpdate call err 'implement sqlCsmUpdate'"   ,
        , "sqlCall   call err 'implement sqlCsmCall'"
    return 0
endProcedure sqlOini
/*--- execute a query from sql, with one resultset -------------------*/
sqlQuery: procedure expose m.
parse arg cx, src, retOk, m.sql.cx.type
    if m.sql.cx.type \== '' then
        m.sql.cx.type = class4Name(m.sql.cx.type)
    if datatype(cx, 'n') then
        interpret objMet(m.sql.connection, 'sqlQuery')
    else
        interpret objMet(cx, 'sqlQuery')
/*--- fetch next row to m.dst.* at end return false ------------------*/
sqlFetch: procedure expose m.
parse arg cx, dst, retOk
    if datatype(cx, 'n') then
        interpret objMet(m.sql.connection, 'sqlFetch')
    else
        interpret objMet(cx, 'sqlFetch')
/*--- close cursor 'c'cx ---------------------------------------------*/
sqlClose: procedure expose m.
parse arg cx, retOk
    if datatype(cx, 'n') then
        interpret objMet(m.sql.connection, 'sqlClose')
    else
        interpret objMet(cx, 'sqlClose')
    return 0
/*-- execute an sql with no resultset, but possibly outParms ---------*/
sqlUpdate: procedure expose m.
parse arg cx, src, retOk
    if cx == '' | datatype(cx, 'n') then
        interpret objMet(m.sql.connection, 'sqlUpdate')
    else
        interpret objMet(cx, 'sqlUpdate')
    return sqlCsmUpdate(cx, src, retOk)
/*-- execute an sql call with outParms and multiple resultSets -------*/
sqlCall: procedure expose m.
parse arg cx, src, retOk
    if cx == '' | datatype(cx, 'n') then
        interpret objMet(m.sql.connection, 'sqlCall')
    else
        interpret objMet(cx, 'sqlCall')
    return sqlCsmUpdate(cx, src, retOk)
/*--- connect to the db2 subsystem sys -----------------------------*/
sqlOConnect: procedure expose m.
parse arg sys, retCon
    call sqlOIni
    return sqlConnect(sys, retCon)
endProcedure sqlOConnect

sqlSel: procedure expose m.
parse arg src, type
     s = oNew('SqlSel', inp2str(src, '-sql'), type)
     call pipeWriteAll s
     return m.s.rowCount
endProcedure sqlSel

sqlStmtsOpt: procedure expose m.
parse arg src, opts
    upper opts
    sub = ''
    o = ''
    ggRet = ''
    do wx=1 to words(opts)
        w = word(opts, wx)
        if abbrev(w, '-SQL') then
            o = o'-sql'substr(w, 5)
        else if w == '-O' | w == 'O' then
            o = o'-o'
        else if w = '*' | datatype(w, 'n') then
            ggRet = ggRet w
        else if length(w) == 4 then
            sub = w
        else
            call err 'bad opt' w 'in opts' opts 'not -sql? -o or subsys'
        end
    call sqlOIni
    if sub == '' then
        call sqlOConnect
    else if sub \== m.sql.connected then
        call sqlConnect sub
    return sqlStmts(src, strip(ggRet), strip(o))
endProcedure sqlStmtsOpt

/*** execute sql's in a stream (separated by ;)
       opt: 'o' ==> write objects, otherwise fmtFTab
            's' ==> spufi formatting (window 72) otherwise linebreak */
sqlStmts: procedure expose m.
parse arg src, ggRet, opt
   dlm = ';'
   isStr = oStrOrObj(src, m.j.in)
   fLen = ''
   if pos('sql', opt) > 0 then
       fLen = word(substr(opt, pos('sql', opt)+3), 1)
   if isStr then do
       m.sqlStmts.rdr = ''
       call sbSrc sqlStmts, ggStr
       end
   else do
       fi = jOpen(o2File(ggObj), '<')
       call jCatSqlReset sqlStmts, , fi, fLen
       end
   do forever
       s1 = jCatSqlNext(sqlStmts, dlm)
       if s1 = '' then
           leave
       w1 = translate(word(s1, 1))
       if w1 == 'TERMINATOR' then do
            dlm = strip(substr(m.s.val, 12))
            if length(dlm) \== 1 then
                call scanErr s, 'bad terminator' dlm 'in' strip(m.s.val)
            iterate
            end
       call out sqlStmt(s1, ggRet, opt)
       end
   if \ isStr then
       call jClose fi
   return 0
endProcedure sqlStmts

sqlStmt: procedure expose m.
parse arg src, retOk, opt
    cx = sqlGetCursor()
    r1 = sqlExecute(cx, src, retOK)
    res = 'sqlCode' r1
    if wordPos(m.sql.cx.fun, 'DELETE INSERT UPDATE') > 0 then
        res = res',' m.sql.cx.updateCount 'rows' ,
              translate(fun, m.mAlfLC, m.mAlfUC)'d'
    else if m.sql.cx.updateCount\=='' & m.sql.cx.updateCount\=0 then
        res = res',' m.sql.cx.updateCount 'rows updated'
    aa = strip(src)
    if m.sql.cx.resultSet \== '' then do
        rdr = sqlResultRdr(cx)
        if pos('o', opt) > 0 then
            call pipeWriteAll rdr
        else
            call fmtFTab sqlStmtFmt, rdr
        res = m.rdr.rowCount 'rows fetched'
        end
    ll = 75 - length(res)
    if length(aa) > ll then
        aa = space(aa, 1)
    if length(aa) > ll then
        aa = left(aa,  ll-3)'...'
    call sqlFreeCursor cx
    return res':' aa
endProceduire sqlStmt

removeSqlStmt: procedure expose m.
parse arg src, ggRet, opt
    bx = verify(src, '( ')
    if bx < 1 then
        return ''
    fun = translate(word(substr(src, bx), 1))
    w2  = translate(word(substr(src, bx), 2))
    res = ''
    if fun == 'SELECT' | fun = 'WITH' then do
        s = oNew('SqlSel', inp2str(src, '%S%+Q\s'))
        if pos('o', opt) > 0 then
            call pipeWriteAll s
        else
            call fmtFTab sqlStmtFmt, s
        res = m.s.rowCount 'rows fetched'
        end
    else if  fun = 'SET' &  abbrev(w2, ':') then do
        ex = pos('=', w2)
        if ex > 2 then
            var = strip(substr(w2, 2, ex-2))
        else
            var = strip(substr(w2, 2))
        if var = '' then
            var = 'varUnbekannt'
        call sqlExec src, ggRet
        res = 'sqlCode' sqlCode var'='value(var)
        end
    else if fun = 'SET' | (fun = 'DECLARE' & w2 = 'GLOBAL') then do
        call sqlExImm src, ggRet
        res = 'sqlCode' sqlCode
        end
    else if fun = 'CALL' then do
        res = sqlStmtCall(src, ggRet, opt)
        end
    else do
        if pos('-', ggRet) < 1 & fun = 'DROP' then
            ggRet = -204 ggRet
        call sqlExec src, ggRet
        res = 'sqlCode' sqlCode
        if wordPos(fun, 'DELETE INSERT UPDATE') > 0 THEN
            res = res',' sqlErrd.3 'rows' ,
                  translate(fun, m.mAlfLC, m.mAlfUC)'d'
        end
    aa = strip(src)
    ll = 75 - length(res)
    if length(aa) > ll then
        aa = space(aa, 1)
    if length(aa) > ll then
        aa = left(aa,  ll-3)'...'
    return res':' aa
endProcedure removeSqlStmt

sqlStmtCall: procedure expose m.
parse arg src, ggRet, opt
    s = scanSrc(scanSqlReset(sqlstmtcall, ,0), src)
    if \ scanSqlId(scanSkip(s)) | m.s.val \== 'CALL' then
        call scanErr s, 'not a call'
    if \ scanSqlQuId(scanSkip(s)) then
        call scanErr s, 'qualified id missing after call'
    loc = ''
    if m.s.val.0 = 1 then
        wh = 'name =' quote(m.s.val.1, "'")
    else if m.s.val.0 = 2 then
        wh = "schema = '"strip(m.s.val.1)"'" ,
             "and name = '"strip(m.s.val.2)"'"
    else if m.s.val.0 = 3 then do
        loc = m.s.val.1
        wh = "schema = '"strip(m.s.val.2)"'" ,
             "and name = '"strip(m.s.val.3)"'"
        end
    else
        call scanErr s, 'storedProcedureName' m.s.val ,
               'has' m.s.val.0 'parts, should have 1, 2 or 3'
    pn = m.s.val
    da = sqlStmtCallDa(sqlStmtCall, loc, wh)
    if \ scanLit(scanSkip(s), '(') then
        call scanErr s, '( expected after call' pn
    varChars = f
    do ax=1
        m.da.ax.varName = ''
        isEmpty = 0
        if scanLit(scanSkip(s), ':') then do
             if \ scanVerify(scanSkip(s), m.mAlfDot) then
                 call scanErr s, 'variable expected after : in call' pn
             m.da.ax.varName = m.s.tok
             if m.da.ax.io == 'i' | m.da.ax.io == 'b' then
                 m.da.ax.sqlData = envGet(m.da.ax.varName)
             end
        else if scanString(s) then
            m.da.ax.sqlData = m.s.val
        else if scanVerify(s, ',):;', 'm') then
            m.da.ax.sqlData = strip(m.s.tok)
        else
            isEmpty = 1
        if scanLit(scanSkip(s), ')') then
            leave
        if \ scanLit(s, ',') then
            call scanErr s, if(isEmpty, 'value, var, ') ,
                         || "',' or ')' expected"
        end
    if ax \= m.da.sqlD then
        if \ (ax=1 & m.da.sqlD = 0 & isEmpty) then
            call scanErr s, 'call with' ax 'parms but' ,
                                pn 'needs' m.da.sqld
    caCo = sqlExec('call' pn 'using descriptor :M.'da, 466)
    call out '--- called' pn', sqlCode' caCo
    do ax=1 to m.da.sqlD
        call Out '  parm' ax m.da.ax.io m.da.ax.parmName,
                 || if(m.da.ax.varName \== '',' $'m.da.ax.varName),
               '=' m.da.ax.sqlData
        if m.da.ax.varName \== '' then
            call envPut m.da.ax.varName, m.da.ax.sqlData
        end
    if caCo = 466 then do
        drop sqlDP
        call sqlExec 'describe procedure :pn into :m.sqlDp'
        if m.sqldp.sqlD < 1 then
             call err 'bad sqldp sqlD='m.sqldp.sqlD 'for sqlCode' caCo
        do dx=1 to m.sqldp.sqlD
            call out '  dynamic result set' dx m.sqldp.dx.sqlName ,
                     'locator='m.sqldp.dx.sqlLocator
            end
        do dx=1 to m.sqldp.sqlD
            drs = 'dynamic result set' dx'='m.sqldp.dx.sqlName 'of' pn
            call out '--- begin of' drs
            rdr = sqlDRS(m.sqldp.dx.sqlLocator)
            if pos('o', opt) > 0 then
                call pipeWriteAll rdr
            else
                call fmtFTab sqlStmtFmt, rdr
            call out '---' m.rdr.rowCount 'rows fetched from' drs
            end
        end
    return 'sqlCode' caCo
endProcedure sqlStmtCall

sqlStmtCallDa: procedure expose m.
parse arg da, loc, wh
    cr = if(loc=='',,loc'.')'sysIbm'
    sql = "select 'SCHEMA=''' || strip(schema) || ''''",
             "|| ' and name='''   || strip(name  ) || ''''",
             "|| ' and specificName=''' || strip(specificName) || ''''",
             "|| ' and routineType =''' || strip(routineType ) || ''''",
             "|| ' and VERSION     =''' || strip(VERSION     ) || ''''",
          "from" cr".SysRoutines ",
          "where" wh "and active = 'Y'"
    if sqlpreAllCl(49, sql, rou, ':m.rou') <> 1 then
        call err m.rou.0 'routines found for' wh
    rdr = jOpen(sqlRdr('select * from' cr'.sysParms where' m.rou,
         'order by ordinal'), '<')
    do ix=1 while assNN('A', jReadO(rdr))
         if m.a.ordinal <>  ix then
             call err 'ix' ix 'mismatch ordinal' m.a.ordinal
         ty = m.a.dataTypeId
         m.da.ix.sqlType = ty
         m.da.ix.sqlLen  = m.a.length
         m.da.ix.sqlLen.sqlPrecision = m.a.length
         m.da.ix.sqlLen.sqlScale     = m.a.scale
         if wordPos(ty, 384 385) > 0 then        /* date */
             m.da.ix.sqlLen  = 10
         else if wordPos(ty, 388 389) > 0 then   /* time */
             m.da.ix.sqlLen  = 8
         else if wordPos(ty, 392 393) > 0 then   /* timestamp */
             m.da.ix.sqlLen  = 26
         m.da.ix.sqlData = ''
         m.da.ix.parmName= m.a.parmName
         m.da.ix.io      = translate(m.a.rowType, 'iob', 'POB')
         m.da.ix.sqlInd  = 1
         end
    m.da.sqlD = ix - 1
    return da
endProcedure sqlStmtCallDa

sqlResultRdr: procedure expose m.
parse arg cx, type
     return oNew('SqlResultRdr', cx, type)
endProcedure sqlRdr

sqlRdr: procedure expose m.
parse arg src, type
     return oNew('SqlSel', inp2str(src, '%S%+Q\s'), type)
endProcedure sqlRdr

sqlResultRdrOpen: procedure expose m.
parse arg m, opt
    if opt\== m.j.cRead then
        call err 'opt not' m.j.cRead 'sqlResultRdrOpen('m',' opt')'
    m.m.jReading = 1
    m.m.rowCount = 0
    return m
endProcedure sqlResultRdrOpen

/*--- prepare and open cursor
      generate type and fetchList ------------------------------------*/
sqlSelOpen: procedure expose m.
parse arg m, opt
    m.m.cursor = sqlGetCursor()
    call sqlQuery m.m.cursor, m.m.src, ,m.m.type  /* ????? */
    return sqlResultRdrOpen(m, opt)
endProcedure sqlOpen

/*--- dynamic result sets --------------------------------------------*/
sqlDRS: procedure expose m.
parse arg loc, type
     return oNew('SqlDRS', loc, type)
endProcedure sqlDRS

sqlDRSOpen: procedure expose m.
parse arg m, opt
    if opt\== m.j.cRead then
        call err 'opt not' m.j.cRead 'sqlDRSOpen('m',' opt')'
    crs = sqlGetCursor('a')
    crN = 'C'crs
    m.m.cursor = crs
    call sqlexec 'allocate C'crs 'cursor for result set :m.m.loc'
    call sqlExec('describe cursor :crN into :M.SQL.'crs'.D')
    m.m.jReading = 1
    m.m.rowCount = 0
    return m
endProcedure sqlDRSOpen

/*--- return a free cursor -------------------------------------------*/
sqlGetCursor: procedure expose m.
parse arg rng
    if rng == '' then
        return sqlGetCursorRng(rng, 10, 49)
    else if rng == 'h' then
        return sqlGetCursorRng(rng, 60, 99)
    else if rng == 'a' then
        return sqlGetCursorRng(rng, 110, 199)
    else
        call err 'bad cursor range' rng
endProcedure sqlGetCursor

sqlGetCursorRng: procedure expose m.
parse arg rng, fr, to
    cx = pos(' ', m.sqlO.cursors, fr)
    if cx < fr & cx > to then
        call err "no more '"rng"' cursors between" fr "and" to,
                 ":"m.sqlO.cursors
    m.sqlO.cursors = overlay('u', m.sqlO.cursors, cx)
    return cx
endProcedure sqlGetCursorRNG

/*--- mark a cursor as closed ----------------------------------------*/
sqlFreeCursor: procedure expose m.
parse arg cx
    if substr(m.sqlo.cursors, cx, 1) \== 'u' then
         call err 'sqlFreeCursor('cx') not in use :'m.sqlo.cursors
    m.sqlO.cursors = overlay(' ', m.sqlO.cursors, cx)
    return
endProcedure sqlFreeCursor
/*--- create the type, fetch vars etc. from the sqlDA ---------------*/
sqlFetchClass: procedure expose m.
parse arg cx
    if m.sql.cx.type = '' then do
        ff = mCat('SQL.'cx'.COL', '%+Q v, f ')
        m.sql.cx.type = classNew('n* SQL u f' ff 'v')
        end
    return m.sql.cx.type
endProcedure sqlFetchClass

/*--- fetch cursor for this sqlSel -----------------------------------*/
sqlSelReadO: procedure expose m.
parse arg m
    cx = m.m.cursor
    v = mNew(sqlFetchClass(cx))
    if \ sqlFetch(cx, v) then
        return ''
    m.m.rowCount = m.m.rowCount + 1
    return v
endProcedure sqlSelReadO

/*--- close sql Cursor -----------------------------------------------*/
sqlSelClose: procedure expose m.
parse arg m, v
    call sqlClose m.m.cursor
    call sqlFreeCursor m.m.cursor
    m.m.cursor = ''
    return m
endProcedure sqlSelClose
/*--- generate the format ff for a sql cx as specified in sp
          use the information from the sqlDa -------------------------*/
deleteSqlGenFmt: procedure expose m.
parse arg ff, cx, sp
    if abbrev(sp, '=') then
        return substr(sp, 2)
    if sp = '' then
        sp = '*st'
    m.ff.0 = m.sql.cx.d.sqlD
    m.ff.flds = oFlds(sqlType(cx))
    if abbrev(sp, '*') then do
        do ix=1 to m.ff.0
            m.ff.ix = substr(sp, 2)
            end
        return ff
        end
    if abbrev(fmts, '=') then
        m.Sql.cx.FMT = substr(fmts, 2)
    defs = 'ir7 fr9 sl12 Tl26' sp
    do wx = 1 to words(defs)
        parse value word(defs, wx) with ty 2 fo
        select
            when ty = 'd' then      t.384 = fo
            when ty = 'f' then      t.480 = fo'/f'
            when ty = 'i' then      t.496 = fo'/i'
            when ty = 'n' then      t.484 = fo'/n'
            when ty = 's' then      t.448 = fo
            when ty = 't' then      t.388 = fo
            when ty = 'T' then      t.392 = fo
            otherwise          call err 'bad type' ty 'for format' fo
            end
        end
    if symbol('t.496') == 'VAR' then
        t.500 = t.496
    if symbol('t.448') == 'VAR' then do
        t.452 = t.448
        t.456 = t.448
        t.464 = t.448
        end
    do wx = 1 to m.ff.0
        ty = m.sql.cx.d.wx.sqlType
        le = m.sql.cx.d.wx.sqlLen
        withNulls = ty // 2
        ty = ty - withNulls
        if symbol('t.ty') <> 'VAR' then
            call err 'sqlType' ty 'not supported'
        parse var t.ty fo 2 fl '/' op
        if op = 'i' then
             if le = 2 then le = 6
             else           le = 12
        else if op <> '' then
            call err 'length for sqlType' ty 'op' op 'not implemented'
        if fl = '=' then
            fl = le
        else if abbrev(fl, '<') then
            fl = min(le, substr(fl, 2))
        m.ff.wx = fo || fl
        end
    return ff
endProcedure sqlGenFmt
/* copy sqlO   end   **************************************************/
/* copy sqlCsm begin **************************************************/
/*--- send an sql to csm an handle sqlCode ---------------------------*/
sqlCsmExe:
parse arg cx, ggSqlStmt, ggRetOk
    sql_HOST =  m.sql.conHost
    SQL_DB2SSID = m.sql.conSSID
    sql_query = ggSqlStmt
    address tso "CSMAPPC START PGM(CSMASQL)"
    if \ (rc = 0 |  rc = 4) then
        call err 'csmappc rc' rc
    if sqlCode = 0 then
        return 0
    else if pos('*', ggRetOk) > 0 | wordPos(sqlCode, ggRetOk) > 0 ,
            then do
        if sqlCode < 0 & pos('say', ggRetOk) > 0 then
            call errSay sqlmsg(sqlCA2Rx(sqlCa))
        return sqlCode
        end
    else if sqlCode < 0 then
        call err sqlmsg(sqlCA2rx(sqlCa))
    else if pos('w', ggRetOk) < 1 then
        if sqlCode = 100 then
            call errSay 'sqlCode +100 row not found\nstmt =' ggSqlStmt
        else
            call errSay sqlMsg(sqlCA2rx(sqlCa)), ,'w'
    return sqlCode
endProcedure sqlCsmExe

/*--- execute a query from sql, with one resultset -------------------*/
sqlCsmQuery: procedure expose m.
parse arg cx, sqlSrc, retOk, src
    res = sqlCsmExe(cx, sqlSrc, 100 retOk)
    if res < 0 then
        return res
    f = m.sql.cx.type
    if src == '' then
        src = 'SQL.'cx'.DATA'
    m.sql.cx.data = src
    if f \== '' then do
        f = f'.FLDS'
        if m.f.0 < sqlD then
            call err 'not enough fields in type'
        end
    do kx=1 to sqlD
        rxNa = SQLDA_REXXNAME.kx
        cn = sqlVarName(f, kx, sqlDa_name.kx)
        m.sql.cx.col.kx = cn
        do rx=1 to sqlRow#
            if substr(sqlIndicator.rx, kx ,1) == 'ff'x then
                m.src.rx.cn = m.sqlNull
            else
                m.src.rx.cn = value(rxNa'.'rx)
            end
        end
    m.src.0 = sqlRow#
    m.sql.cx.col.0 = sqlD
    m.sql.cx.daIx = 0
    return 0
endProcedure sqlCsmQuery

sqlCsmFetch: procedure expose m.
parse arg cx, dst
    src = m.sql.cx.data
    rx = m.sql.cx.daIx + 1
    if rx > m.sql.cx.data.0 then
        return 0
    m.sql.cx.daIx = rx
    do kx = 1 to m.sql.cx.col.0
        c = m.sql.cx.col.kx
        m.dst.c = m.src.rx.c
        end
    return 1
endProcedure sqlCsmFetch
/* copy sqlCsm end   **************************************************/
/* copy sql    begin ***************************************************
    sql interface
***********************************************************************/
/*--- initialize sqlRx (belongs to sqlQ, but currently only one|) ----*/
sqlIni: procedure expose m.
    if m.sql.ini == 1 then
        return
    m.sqlNull = '---'
    m.sqlInd = 'sqlInd'
    m.sqlRetOK.0 = 0
    m.sqlCAMsg = 0
    m.sqlSuMsg = 2
    call sqlPushRetOk
    m.sql.ini = 1
    m.sql.conType = ''
    return 0
endProcedure sqlIni

/*--- connect and/or disconnect to DB2 -------------------------------*/
sqlConnect: procedure expose m.
parse upper arg sys, retOk
    if sys \== '' then
        nop
    else if sysvar(sysnode) == 'RZ1' then
        sys = 'DBAF'
    else
        call err 'no default subsys for' sysvar(sysnode)
    call sqlOIni
    hst = ''
    if pos('/', sys) > 0 then do
        parse value space(sys, 0) with hst '/' sys
        cTy = 'Csm'
        end
    else do
        cTy = 'Rx'
        end
    if m.sql.conType == cTy & m.sqlHost==hst & m.sqlConSSID == sys then
        return 0
    if m.sql.conType \== '' then
        call sqlDisconnect
    res = 0
    if cTy = 'Rx' then
        res = sqlRxConnect(sys, retOk)
    if res < 0 then
        return res
    m.sql.conType = cTy
    m.sql.conhost = hst
    m.sql.conSSID = sys
    m.sql.connection = oNew('Sql'cTy'Connection')
    return res
endProcedure sqlConnect

sqlDisconnect: procedure expose m.
parse arg retOk
    if m.sql.conType == 'Rx' then
        call sqlRxDisconnect
    m.sql.conType = ''
    m.sql.conhost = ''
    m.sql.conSSID = ''
    return 0
endProcedure sqlDisonnect

/*--- execute a query from sql, with one resultset -------------------*/
sqlRxQuery: procedure expose m.
parse arg cx, src, retOk
     m.sql.cx.updateCount = ''
     m.sql.cx.resultSet   = ''
     m.sql.cx.needDesc    = 1
     res = sqlPrepare(cx, src, retOk, 1)
     if res < 0 then
         return res
     res = sqlExec('declare c'cx 'cursor for s'cx, retOk)
     if res < 0 then
         return res
     res = sqlExec('open c'cx, retOk)
     if res < 0 then
         return res
     call sqlRxFetchVars cx
     m.sql.cx.updateCount = sqlErrd.3
     m.sql.cx.resultSet = cx
     return res
endProcedure sqlRxQuery

/*--- fetch next row to m.dst.* at end return false ------------------*/
sqlRxFetch: procedure expose m.
parse arg cx, dst, retOk
    if retOk == '' then
        retOk = 100 m.sqlRetOk
    fetCode = sqlExec('fetch c'cx 'into' sqlRxFetchVars(cx), retOk)
    if fetCode == 100 then
        return 0
    if fetCode < 0 then
        return fetCode
    call sqlSetNull cx, dst
    return 1
endProcedure sqlRxFetch

/*--- close cursor 'c'cx ---------------------------------------------*/
sqlRxClose: procedure expose m.
parse arg cx, retOk
     return sqlExec('close c'cx, retOk)
endProcedure sqlRxClose

/*-- execute an sql with no resultset, but possibly outParms ---------*/
sqlRxUpdate: procedure expose m.
parse arg cx, src, retOk
    m.sql.cx.updateCount = ''
    m.sql.cx.resultSet   = ''
    bx = verify(src, '( ')
    if bx > 0 then
        fun = translate(word(substr(src, bx), 1))
    if  fun = 'SET' then do
        w2 = translate(word(substr(src, bx), 2))
        if \ abbrev(w2, ':') then
            return sqlExImm(src, ggRet)
        trace ?r
        ex = pos('=', w2)
        if ex = 0 then
            ex = length(w2)+1
        var = strip(substr(w2, 2, ex-2))
        if var = '' then
            call err 'bad hostVar in' src
        m.sql.outVar = var
        src2 = 'set :M.sql.out.'var substr(w, ex) subword(src, 3)
        return sqlExec(src2, ggRet)
        end
    if fun == 'DECLARE'  then do
        if 'GLOBAL' == translate(word(substr(src, bx), 2)) then
            return sqlExImm(src, ggRet)
        end
    res = sqlExec(src, ggRet)
    if wordPos(fun, 'DELETE INSERT UPDATE') > 0 then
        m.sql.cx.updateCount = sqlErrd.3
    return res
endProcedure sqlRxUpdate

/*-- execute a query, update or call ---------------------------------*/
sqlExecute: procedure expose m.
parse arg cx, src, retOk
    src = inp2Str(src, '-sql')
    f = translate(word(substr(src, max(verify(src, '( '), 1)), 1))
    m.sql.cx.fun = f
    if f == 'SELECT' | fun == 'WITH' then
        return sqlQuery(cx, src, retOk)
    else if f == 'CALL' then
        call err 'implement sql call for:' src
    else
        return sqlUpdate(cx, src, retOk)
endProcedure sqlExecute

/*-- execute a query, copy result to stem ----------------------------*/
sql2St: procedure expose m.
parse arg src, dst, retOk, type
    cx = sqlGetCursor()
    res = sqlQuery(cx, src, retOk, type)
    if res >= 0 then do
        do sx=1 while sqlFetch(cx, dst'.'sx)
           end
        res = sx-1
        end
    m.dst.0 = res
    call sqlClose cx
    call sqlFreeCursor cx
    return res
endProcedure sql2St

/*-- execute a query and return value of the first column
           if > 1 row fail, if 0 rows return arg(3) or fail ----------*/
sql2One: procedure expose m.
parse arg src, dst
    cx = sqlGetCursor()
    call sqlQuery cx, src
    if \ sqlFetch(cx, dst) then
        if arg() > 2 then
            return arg(3)
        else
            call err 'no row returned for:' src
    if sqlFetch(cx, dst.2) then
        call err '2 or more rows for' src
    c1 = m.sql.cx.col.1
    res = m.dst.c1
    call sqlClose cx
    call sqlFreeCursor cx
    return res
endProcedure sql2One

/*--- prepare statement 's'cx from sql src into descriptor desc ------*/
sqlPrepare: procedure expose m.
parse arg cx, src, ggRetOk, descOut
     s = ''
     src = inp2str(src, '%+Q\s')
     m.sql.cx.d.sqlD = 'noSqlDA'
     m.sql.cx.i.sqlD = 'noDescInp'
     m.sql.cx.fetchVars = ''
     m.sql.cx.col.0 = ''
     m.sql.cx.into = ''
     if descOut == 1 then
         s = 'into :M.SQL.'cx'.D'
     return sqlExec('prepare s'cx s 'from :src', ggRetOk)
endProcedure sqlPrepare

/*--- open cursor 'c'cx using arguments arg(2), arg(3)... ------------*/
sqlOpen: procedure expose m.
parse arg cx
    if arg() <=  1 then
        return sqlExec('open c'cx)
    call sqlDescribeInput cx
    do ix=1 to arg()-1
        call sqlDASet cx , 'I', ix, arg(ix+1)
        end
    return sqlExec('open c'cx 'using descriptor :M.SQL.'cx'.I')
endProcedure sqlOpen

/*--- describe output (if not already done)
         and return size of sqlDa ------------------------------------*/
sqlDescribeOutput: procedure expose m.
parse arg cx, force
    if force == 1 | \ datatype(m.sql.cx.d.sqlD, 'n') then
         call sqlExec 'describe s'cx 'into :M.SQL.'cx'.D', 0
    return m.sql.cx.d.sqlD
endProcedure sqlDescribeOutput

/*--- describe input (if not already done)
         and return size of input sqlDA ------------------------------*/
sqlDescribeInput: procedure expose m.
parse arg cx, force
    if force == 1 | \ datatype(m.sql.cx.i.sqlD, 'n') then
         call sqlExec 'describe input s'cx 'into :M.SQL.'cx'.I'
    return m.sql.cx.i.sqlD
endProcedure sqlDescribeInput

/*--- put sqlNull in all vars where indicator says so ---------------*/
sqlSetNull: procedure expose m.
    parse arg cx, dst
    do nx=1 to m.sql.cx.sqlNull.0
        col = m.sql.cx.sqlNull.nx
        if m.dst.col.sqlInd < 0 then
            m.dst.col = m.sqlNull
        end
    return
endProcedure sqlSetNull

/*--- use describe output to generate column names,
                fetchVariables and sqlNull names ---------------------*/
sqlRxFetchVars: procedure expose m.
parse arg cx
    if m.sql.cx.fetchVars \== '' then
        return m.sql.cx.fetchVars
    call sqlDescribeOutput cx
    f = m.sql.cx.type
    if f \== '' then do
        f = f'.FLDS'
        if m.f.0 < m.sql.cx.d.sqlD then
            call err 'not enough column names'
        end
    m.sql.cx.col.0 = m.sql.cx.d.sqlD
    nx = 0
    vars = ''
    do kx=1 to m.sql.cx.d.sqlD
        cn = sqlVarName(f, kx, m.sql.cx.d.kx.sqlName)
        m.sql.cx.col.kx = cn
        vars = vars', :m.dst.'cn
        if m.sql.cx.d.kx.sqlType // 2 = 1 then do
            vars = vars' :m.dst.'cn'.sqlInd'
            nx = nx + 1
            m.sql.cx.sqlNull.nx = cn
            end
        end
    m.sql.cx.sqlNull.0 = nx
    m.sql.cx.fetchVars = substr(vars, 3)
    return m.sql.cx.fetchVars
endProcedure sqlRxFetchVars

sqlVarName: procedure expose m. sqlVarName.
parse arg f, kx, sNa
    if f == '' then do
        cn = translate(word(sNa, 1))
        if cn == '' | symbol('sqlVarName.cn') == 'VAR' then
                cn = 'COL'kx
        sqlVarName.cn = 1
        return cn
        end
    else do
        if m.f.kx == '' then
            call err 'implement empty varName'
        return substr(m.f.kx, 2)
        end
endProcedure sqlVarName
/*--- set one value in a DA, handle nulls ----------------------------*/
sqlDASet: procedure expose m.
parse arg cx, da, ix, val
    m.sql.cx.da.ix.sqlData = val
    m.sql.cx.da.ix.sqlInd = - (val == m.sqlNull)
    /* data types schienen einmal nicht zu funktionieren .......
    if wordPos(m.da.ix.sqlType, '384 385 388 389 392 393') > 0 then
        m.da.ix.sqlType = 448 + (m.da.ix.sqlType // 2) */
    return
endProcedure sqlDASet

sqlExImm:
parse arg ggSrc, ggRetOk
     return sqlExec('execute immediate :ggSrc', ggRetOk)
endProcedure sqlExImm

sqlCommit: procedure expose m.
parse arg src
     return sqlExec('commit')
endProcedure sqlCommit

/*--- execute sql thru the dsnRexx interface -------------------------*/
sqlExec: /* no procedure, to keep variables sql... */
    parse arg ggSqlStmt, ggRetOk
    address dsnRexx 'execSql' ggSqlStmt
    /* say 'sqlCode' sqlCode 'rc' rc 'for' ggSqlStmt ggNo */
sqlHandleRCSqlCode:
    if rc = 0 then
        return 0
    if ggRetOk = '' then
        ggRetOk = m.sqlRetOk
    if wordPos(rc, '1 -1') < 0 then
        call err 'dsnRexx rc' rc sqlmsg()
    else if pos('*', ggRetOk) > 0 | wordPos(sqlCode, ggRetOk) > 0 ,
            then do
        if sqlCode < 0 & pos('say', ggRetOk) > 0 then
            say 'sqlError' sqlmsg()
        return sqlCode
        end
    else if rc < 0 then
        call err sqlmsg()
/*???lse if sqlCode <> 0 | (pos('w',ggRetOk)<1 & sqlWarn.0^==' ') then*/
    else if (sqlCode <> 0 | sqlWarn.0^==' ') & pos('w',ggRetOk)<1 then
        call errSay sqlMsg(), ,'w'
    return sqlCode
endSubroutine sqlExec

/*--- connect to the db2 subsystem sys -----------------------------*/
sqlRxConnect: procedure expose m.
parse upper arg sys, ggRetOk
    call sqlIni
    address tso "SUBCOM DSNREXX"
    if rc <> 0 then do
       sRc = RXSUBCOM('ADD','DSNREXX','DSNREXX') /* ADD HOST CMD ENV  */
       if sRc <> 0 then
           call err 'rc' sRc 'from RXSUBCOM(ADD, DSNREXX, DSNREXX)'
       end
    if sys = '-' then
        return 0
    ggSqlStmt =  'connect' sys
    address dsnRexx ggSqlStmt
    return sqlHandleRcSqlCode()
endProcedure sqlRxConnect

/*--- diconnect from db2 ---------------------------------------------*/
sqlRxDisconnect: procedure expose m.
parse arg retOk
    ggSqlStmt =  'disconnect'
    address dsnRexx ggSqlStmt
    return sqlHandleRcSqlCode()
endProcedure sqlDisconnect

/*--- send a command to db2 through the TSO dsn processor ------------*/
sqlDsn: procedure expose m.
parse arg st, sys, cmd, rcOk
    x = outtrap('M.'st'.')
    push 'END'
    push cmd
    address tso 'DSN SYSTEM('sys')'
    rr = rc
    x = outtrap(off)
    if rr = 0 | rcOk = '*' | wordPos(rr, rcOk) > 0 then
        return rr
    fl = max(1, m.st.0 - 10)
    em = 'rc' rr 'for DSN SYSTEM('sys') cmd' cmd,
          '\nOuputlines' fl '-' m.st.0':'
    do lx=fl to m.st.0
        em = em '\n' strip(m.st.lx, 't')
        end
    call err em
endProcedure sqlDsn
/*--- push and pop currently accepted sqlCodes -----------------------*/
sqlPushRetOk: procedure expose m.
parse arg rr
    nx = m.sqlRetOk.0 + 1
    m.sqlRetOk.0 = nx
    m.sqlRetOk.nx = rr
    m.sqlRetOk    = rr
    return
endProcedure sqlPushRetOk

sqlPopRetOk: procedure expose m.
    nx = m.sqlRetOk.0 - 1
    if nx < 1 then
        call err 'sqlPopRetOk with .0' m.sqlRetOk.0
    m.sqlRetOk    = m.sqlRetOk.nx
    m.sqlRetOk.0 = nx
    return
endProcedure sqlPopRetOk

/*--- issue an sql error message -------------------------------------*/
sqlMsg: /* no procedure, to keep variables sql... */
    if \ dataType(sqlCode, 'n') then do
        ggRes = 'sqlCode' sqlCode 'not numeric\n'sqlCaMsg()
        end
    else do
        ggRes = sqlDsntiar(sqlRx2CA())
        ggWa = sqlMsgWarn()
        if ggWa \= '' then
            ggRes = ggRes'\nwarnings' ggWa
        if m.sqlCAMsg == 1 then
           ggRes = ggRes'\n'sqlCaMsg()'\n'sqlCaMsg(sqlCa2Rx(sqlCa))
        end
    ggSt = 'SQL.HOST'
    ggVa = 'SQL.HOST.VAR'
    ggBe = 'SQL.HOST.BEF'
    call sqlHostVars ggSqlStmt, 12, ggSt
    if datatype(sqlErrd.5, 'n') & sqlErrd.5 > 0 then do
        ggW1 = translate(word(ggSqlStmt, 1))
        ggW2 = translate(word(ggSqlStmt, 2))
        if ggW1 == 'PREPARE' then
            ggVV = sqlHostVarFind(ggSt, 'FROM')
        else if ggW1 ggW2 == 'EXECUTE IMMEDIATE' then
            ggVV = sqlHostVarFind(ggSt, 1)
        else
            ggVV = ''
        if ggVV == '' then
            ggRes = ggRes || sqlMsgSrcPos(ggSqlStmt, sqlErrd.5)
        else
            ggRes = ggRes || sqlMsgSrcPos(value(ggVV), sqlErrd.5)
        end
    ggRes = ggRes'\nstmt =' ggSqlStmt
    ggPref = '\nwith'
    do ggXX=1 to m.ggSt.0
        ggRes = ggRes || ggPref m.ggBe.ggXX ':'m.ggVa.ggXX ,
                      '=' value(m.ggVa.ggXX)
        ggPref = '\n    '
        end
    if m.sqlSuMsg == 1 | (m.sqlSuMsg == 2 & m.sql.conHost \== '') then
        ggRes = ggRes'\nsubsys =' ,
                if(m.sql.conHost=='',,m.sql.conHost'/'),
                || m.sql.conSSID', interfaceType' m.sql.conType
    return  ggRes
endSubroutine sqlMsg

/*--- use dsnTiar to translate sql Info to error text ----------------*/
sqlDsnTiar: procedure expose m.
parse arg ca
    if -438  = sqlCa2Rx(ca) then
        return '\nSQLCODE = -438:',
           'APPLICATION RAISED ERROR WITH sqlState' sqlState ,
           'and DIAGNOSTIC TEXT:' sqlErrMc
    liLe = 78
    msLe = liLe * 10
    msg = d2c(msLe,2) || left('', msLe)
    len = d2c(liLe, 4)
    ADDRESS LINKPGM "DSNTIAR ca msg len"
    if rc <> 0 then
        call err 'linkPgm dsnTiar rc' rc 'sqlCa' ca
    res = strip(substr(msg, 13, liLe-10))
    cx = pos(', ERROR: ', res)
    if cx > 0 then
        res = left(res, cx-1)':' strip(substr(res, cx+9))
    do c=3+liLe by liLe to msLe while substr(msg, c, 10) = ''
            res = res'\n    'strip(substr(msg, c+10, liLe-10))
        end
    return res
endProcedure sqlDsnTiar

/*--- format all rexx sqlCa fields into a message --------------------*/
sqlCaMsg:
    return 'sqlCode' sqlCode 'sqlState='sqlState                    ,
           '\n    errMC='translate(sqlErrMc, ',', 'ff'x)            ,
           '\n    warnings='sqlWarnCat('+') 'erP='sqlErrP           ,
           '\n    errD.1='sqlErrD.1 '2='sqlErrD.2 '3='sqlErrD.3     ,
           '\n    errD.4='sqlErrD.4 '5='sqlErrD.5 '6='sqlErrD.6
endProcedure sqlCaMsg

/*--- format the sqlCA into the dsnTiar SQLCA ------------------------*/
sqlRx2Ca:
    if \ (datatype(sqlcode, 'n') & datatype(sqlErrD.1, 'n') ,
                                 & datatype(sqlErrD.3, 'n')) then
        return err('sqlCode etc. not numeric\nsqlCa =' sqlCaMsg())
    if digits() < 10 then
        numeric digits 10
    sqlCa = 'SQLCA   ' || d2c(136, 4) || d2c(sqlCode, 4) ,
            || d2c(min(70, length(sqlErrMc)), 2)left(sqlErrMc, 70) ,
            || left(sqlErrP, 8) ,
            || d2c(sqlErrD.1, 4)d2c(sqlErrD.2, 4)d2c(sqlErrD.3, 4) ,
            || d2c(sqlErrD.4, 4)d2c(sqlErrD.5, 4)d2c(sqlErrD.6, 4) ,
            || sqlWarnCat() || sqlState
    if length(sqlCa) <> 136 then
        call err 'sqlCa length' length(sqlCa) 'not 136' ,
                 '\n'sqlCaMsg() '==>'  ca', hex='c2x(ca)
    return sqlCa
endProcedure sqlRx2Ca

/*--- extract the fields from the SqlCA and put it to rexx vars ------*/
sqlCA2Rx: procedure expose m. ,
       sqlCode sqlErrMc sqlErrP sqlErrD. sqlWarn. sqlState
parse arg ca
    numeric digits 10
    if length(ca) < 136 | c2d(substr(ca, 9, 4), 4) <> 136 then
        call err 'bad sqlCa len' length(ca) 'not 136:' ca', hex='c2x(ca)
    sqlCode  = c2d(substr(ca, 13 ,4), 4)
    sqlErrMC = substr(ca, 19, c2d(substr(ca, 17, 2), 2))
    sqlErrP  = substr(ca, 89, 8)
    do ix=1 to 6
        sqlErrD.ix = c2d(substr(ca, 93 + 4 * ix, 4), 4)
        end
    do ix=0 to 10
        sqlWarn.ix = substr(ca, 121 + ix, 1)
        end
    sqlState = substr(ca, 132, 5)
    return sqlCode
endProcedure sqlCA2Rx

/*--- concat the sql warnings with Separator sep --------------------*/
sqlWarnCat: procedure expose m. sqlWarn.
parse arg sep
    return sqlWarn.0 || sep,
        || sqlWarn.1||sqlWarn.2||sqlWarn.3||sqlWarn.4||sqlWarn.5||sep ,
        || sqlWarn.6||sqlWarn.7||sqlWarn.8||sqlWarn.9||sqlWarn.10||sep
endProcedure sqlWarnCat

/*--- make the text for sqlWarnings ----------------------------------*/
sqlMsgWarn: procedure expose m. sqlWarn.
     r = ''
     text =' 1=W var truncated, 1=S scrollable, 1=N nonScrollable,'  ,
            '2=W nulls in aggregate,'                                ,
            '3=W more cols than vars,'                               ,
                             '3=Z more result sets than locators,'   ,
            '4=W no where, 4=D sensitive dynamic, 4=I insensitive,'  ,
                          '4=S sensitive static,'                    ,
            '5=W not valid sql, 5=1 readOnly, 5=2 readDelete,'       ,
                          '5=3 readDeleteUpdate,'                    ,
            '6=W day changed to month range,'                        ,
            '7=W dec digits truncated,'                              ,
            '8=W char substituted,'                                  ,
            '9=W arith excep in count, 9=Z multipe result sets,'     ,
            '10=W char conversion err in ca,'
     do wx = 1 to 10
         w = sqlWarn.wx
         if w = ' ' then
             iterate
         t = wx'='w
         cx = pos(' 'wx'='w' ', text)
         ex = pos(','         , text, cx + 1)
         if cx > 0 & ex > cx then
             r = r substr(text, cx+1, ex-cx)
         else
             r = r wx'='w '?,'
         end
     r = strip(r, 't', ',')
     if r = '' & sqlwarn.0 <> '' then
        call err 'sqlWarn.0='sqlWarn.0 'but all warns empty'
     return r
endProcedure sqlMsgWarn

/*--- show in the source src the point pos  (where error occured)
          a few lines from src around pos and arrow to pos ----------*/
sqlMsgSrcPos: procedure expose m.
parse arg src, pos
    liLe = 68
    liCn = 3
    afLe = 25
    t1 = space(left(src, pos), 1)
    t2 = left(' ', substr(src, pos, 1) == ' ' ,
                 | substr(src, pos+1, 1) == ' ') ,
         || space(substr(src, pos+1), 1)
    afLe = min(afLe, length(t2))
    if length(t1) + afLe > liLe * liCn then
        t1 = '...'right(t1, liLe * liCn - afLe -3)
    else if length(t1)+length(t2) > liLe then
        t1 = left(' ', (liCn * liLe - length(t1) -afLe) // liLe)||t1
    pL = length(t1) // liLe
    if length(t2) <= liLe-pL then
        tx = t1 || t2
    else
        tx = t1 || left(t2, liLe-pL-3)'...'
    res = '\nsrc' strip(substr(tx, 1, liLe), 't')
    do cx=1+liLe by liLe to length(tx)
        res = res || '\n  +' strip(substr(tx, cx, liLe), 't')
        end
    loc = 'pos' pos 'of' length(src)
    if length(loc)+6 < pL then
        return res'\n  >' right('>>>'loc'>>>', pL)
    else
        return res'\n  >' left('', pL-1)'<<<'loc'<<<'
endProcdedure sqlMsgSrcPos

/*--- get the hostVars in the sql in src and the word before ---------*/
sqlHostVars: procedure expose m.
parse arg src, cnt, st
    cx = 1
    sx = 0
    do cnt
        cx = pos(':', src, cx) + 1
        if cx < 2 then
           leave
        if pos(substr(src, cx, 1), m.mAlfRex1) < 1 then
            iterate
        ex = verify(src, m.mAlfRexR, 'n', cx)
        if ex - cx > 100 then
            iterate
        sx = sx + 1
        if ex < 1 then
            m.st.var.sx = substr(src, cx)
        else
            m.st.var.sx = substr(src, cx, ex - cx)
                       /* search word before */
        do bE = cx-2 by -1 to 1 ,
                while substr(src, bE, 1) == ' '
            end
        do bB = bE by -1 to max(1, bE-20),
                while pos(substr(src, bB, 1), m.mAlfa) > 0
            end
        if bB < bE & bB >= 0 then
            m.st.bef.sx = substr(src, bB+1, bE-bB)
        else
            m.st.bef.sx = ''
        end
    m.st.0 = sx
    return sx
endProcedure sqlHostVars

/*--- find the name of hostvar, by index or by before ----------------*/
sqlHostVarFind: procedure expose m.
parse arg st, fnd
    if datatype(fnd, 'n') & fnd <= m.st.0 then
        return m.st.var.fnd
    do ix=1 to m.st.0
        if translate(m.st.bef.ix) = fnd then
            return m.st.var.ix
        end
    return ''
endSubroutine sqlHostVarFind
/* copy sql    end   **************************************************/
/* copy j begin *******************************************************
    the j framework
         jReset
         jOpen
         jClose
         jRead
         jWrite
***********************************************************************/
jRead: procedure expose m.
parse arg m, var
    call objMetClaM m, 'jRead'
    if m.m.jReading then
        interpret ggCode
    else
        return err('jRead('m',' var') but not opened r')
endProcedure jRead

jReadO: procedure expose m.
parse arg m
if arg() > 1 then call err '???  old interface'
    call objMetClaM m, 'jReadO'
    if m.m.jReading then
        interpret ggCode
    else
        return err('jReadO('m',' var') but not opened r')
endProcedure jReadO

jWrite: procedure expose m.
parse arg m, line
    call objMetClaM m, 'jWrite'
    if \ m.m.jWriting then
        return err('jWrite('m',' line') but not opened w')
    interpret ggCode
    return
endProcedure jWrite

jWriteO: procedure expose m.
parse arg m, var
    call objMetClaM m, 'jWriteO'
    if \ m.m.jWriting then
        return err('jWriteO('m',' var') but not opened w')
    interpret ggCode
    return
endProcedure jWriteO

jWriteAll: procedure expose m.
parse arg m, rdr
    rdr = o2file(rdr)
    call objMetClaM m, 'jWriteAll'
    if \ m.m.jWriting then
        return err('jWriteAll('m',' rdr') but not opened w')
    interpret ggCode
    return
endProcedure jWriteAll

jWriteNow: procedure expose m.
parse arg m, rdr
    rdr = o2file(rdr)
    interpret objMet(m, 'jWriteNow')
    return
endProcedure jWriteNow

jCat: procedure expose m.
parse arg opt m
    if m = '' then do
        m = opt
        opt = m.j.cWri
        end
    call jOpen m, opt
    do ax=2 to arg()
        call jWriteAll m, arg(ax)
        end
    call jClose m
    return m
endProcedure jCat

jWriteNowImpl: procedure expose m.
parse arg m, rdr
    call jOpen rdr, m.j.cRead
    do while jRead(rdr, line)
        call jWrite m, m.line
        end
    call jClose rdr
    return
endProcedure jWriteNow

jWriteNowImplO: procedure expose m.
parse arg m, rdr
    call jOpen rdr, m.j.cRead
    do while assNN('li', jReadO(rdr))
        call jWriteO m, li
        end
    call jClose rdr
    return
endProcedure jWriteNow

/*--- reset JRW: fail if open, initialise ---------------------------*/
jReset: procedure expose m.
parse arg m, arg, arg2, arg3
    if m.m.jReading == 1 | m.m.jWriting == 1 then
        return err('still open jReset('m',' arg2')') / 3
    m.m.jReading = 0
    m.m.jWriting = 0
    m.m.jUsers = 0
    interpret objMet(m, 'jReset')
    return m
endProcedure jReset

jOpen: procedure expose m.
parse arg m, opt
    call objMetClaM m, 'jOpen'
    oUsers = m.m.jUsers
    if opt = m.j.cRead then do
        if m.m.jReading then
            nop
         else if m.m.jWriting then
            return err('already opened for writing jOpen('m',' opt')')
        else do
            interpret ggCode
            m.m.jReading = 1
            end
        end
    else if \ abbrev('>>', opt, 1) then do
        return err('bad option' opt 'in jOpen('m',' opt')')
        end
    else do
        if m.m.jWriting then
            nop
         else if m.m.jReading then
            return err('already opened for reading jOpen('m',' opt')')
        else do
            interpret ggCode
            m.m.jWriting = 1
            end
        end
    m.m.jUsers = oUsers + 1
    return m
endProcedure jOpen

jClose: procedure expose m.
parse arg m
    call objMetClaM m, 'jClose'
    oUsers = m.m.jUsers
    if oUsers = 1 then do
        interpret ggCode
        m.m.jReading = 0
        m.m.jWriting = 0
        end
    else if oUsers < 1 then
        call err 'jClose' m 'but already closed'
    m.m.jUsers = oUsers - 1
    return m
endProcedure jClose

/*--- cat the lines of the file together, with mid between lines,
                fail if not all lines are strings -------------------*/
jCatLines: procedure expose m.
parse arg m, fmt
    if abbrev(fmt, '-sql') then
        return jCatSql(m, substr(fmt, 5))
    if fmt == '' then
        fmt = '%+Q\s'
    call jOpen m, m.j.cRead
    if \ jRead(m, line) then do
        call jClose m
        return ''
        end
    res = f(fmt, m.line)
    do while jRead(m, line)
        res = res || f(fmt'%-Qnxt', m.line)
        end
    call jClose m
    fEnd = 'F.FORMAT.'fmt'%-Qend'
    return res || m.fEnd
endProcedure jCatLines

/*--- cat the line of a file, using comments
               fixline (with token wrapping) or separate lines -------*/
jCatSql: procedure expose m.
parse arg m, fLen
    call jCatSqlReset m'.JCATSQL', , jOpen(m, '<'), fLen
    res = jCatSqlNext(m'.JCATSQL')
    call jClose m
    return res
endProcedure jCatSql

jCatSqlReset: procedure expose m.
parse arg m, aSrc, m.m.rdr, m.m.fLen
    call jCatSqlNL m, aSrc
    return m
endProcedure jCatSqlReset

jCatSqlNL: procedure expose m.
parse arg m
    if m.m.rdr \== '' then
      if jRead(m.m.rdr, m'.SRC') then do
        if m.m.fLen \== '' then
            m.m.src = left(m.m.src, m.m.fLen)
        else if substr(m.m.src, length(m.m.src), 1) \== ' ' then
            m.m.src = m.m.src' '
        m.m.pos = 1
        return 1
        end
    m.m.pos = length(m.m.src)+1
    return 0
endProcedure jCatSqlNl

jCatSqlNext: procedure expose m.
parse arg m, stop
    res = ''
    st = ''
    bx = m.m.pos
    do forever
        call sbUntil m, '"''-/'stop
        if sbEnd(m) then do
            res = res || substr(m.m.src, bx)
            bx = 0
            end
        else if substr(m.m.src, m.m.pos, 2) = '--' then do
            res = res || substr(m.m.src, bx, m.m.pos-bx)' '
            bx = 0
            end
        else if substr(m.m.src, m.m.pos, 2) = '/*' then do
            res = res || substr(m.m.src, bx, m.m.pos-bx)' '
            do forever
                px = pos('*/', m.m.src, m.m.pos)
                if px > 0 then
                    leave
                if \ jCatSqlNL(m) then
                    return res
                end
            bx = px+2
            m.m.pos = bx
            end
        else if sbLit(m, ''' "') then do
            c1 = sbPrev(m)
            do while \ sbStrEnd(m, c1)
                res = res || substr(m.m.src, bx)
                if m.m.fLen \== '' then
                    if jCatSqlNl(m) then do
                        bx = m.m.pos
                        iterate
                        end
                call err 'unclosed' c1 'string:' m.m.src
                end
            end
        else if pos(substr(m.m.src, m.m.pos, 1), stop) > 0 then do
            res = strip(res||substr(m.m.src, bx, m.m.pos-bx), 't')
            call sbChar m, 1
            if res <> '' then
                return res
            bx = m.m.pos
            end
        if bx = 0 then
            if jCatSqlNl(m) then
                bx = m.m.pos
            else
                return res
        end
endProcedure jCatSqlNext

jIni: procedure expose m.
    if m.j.ini == 1 then
        return
    m.j.ini = 1
    m.j.cRead = '<'
    m.j.cWri = '>'
    m.j.cApp = '>>'
    call oIni
    am = "call err 'call of abstract method"
    call classNew 'n JRW u ORun, f JREADING v, f JWRITING v', 'm',
        , "new call jReset m, arg, arg2, arg3",
        , "jRead"   am "jRead('m',' var')'" ,
        , "jReadO if \ jRead(m, 'J.GGVAR') then return '';",
                "return s2o(m.j.ggVar)" ,
        , "jWrite" am "jWrite('m',' line')'" ,
        , "jWriteO call jWrite(m, o2string(var))" ,
        , "jWriteAll call jWriteNowImpl m, rdr",
        , "jWriteNow call jWriteNowImpl m, rdr",
        , "jReset",
        , "jOpen" am" jOpen('m',' opt')'" ,
        , "jClose" ,
        , "oRun call pipeWriteAll m",
        , "o2String return jCatLines(m, fmt)",
        , "o2File return m"
    call classNew 'n JRWO u JRW', 'm',
        , "jRead res = jReadO(m); if res == '' then return 0;" ,
                "m.var = o2string(res); return 1" ,
        , "jReadO"   am "jReadO('m')'" ,
        , "jWrite  call jWriteO(m, s2o(var))" ,
        , "jWriteO" am "jWriteO('m',' line')'",
        , "jWriteAll call jWriteNowImplO m, rdr",
        , "jWriteNow call jWriteNowImplO m, rdr",

    am = "call err 'call errObject"
    call classNew 'n JRWErr u JRW', 'm',
        , "jWriteAll" er "jWriteAll 'm', rdr'",
        , "jWriteNow" er "jWriteNow 'm', 'rdr'",
        , "jClose" er "jClose 'm'"
    call classNew 'n JSay u JRW', 'm',
        , "jWrite say line" ,
        , "jWriteO call classOut , var, 'outO: '",
        , "jOpen if \ abbrev(opt, m.j.cWri) then",
            "call err 'can only write JSay.jOpen('m',' opt')';" ,
            "else m.m.jWriting = 1"
    call classNew 'n JStem u JSay', 'm',
        , "jReset m.m.stem = arg;",
               "if \ dataType(m.arg.0, 'n') then m.arg.0 = 0" ,
        , "jWrite call mAdd m.m.stem, line"
    call classNew 'n JRWEof u JRW', 'm',
        , "jRead drop m.var; return 0",
        , "jOpen if pos('>', opt) > 0 then",
            "call err 'can only read JRWEof.jOpen('m',' opt')';" ,
            "else m.m.jReading = 1"
    m.j.in = jOpen(oNew('JRWEof'), m.j.cRead)
    call outDst
    call classNew "n JBuf u JRWO, f BUF s r", "m",
        , "jOpen call jBufOpen m, opt",
        , "jReset call jBufReset m, arg",
        , "jRead return jBufRead(m, var)",
        , "jReadO return jBufReadO(m)",
        , "jWrite call jBufWrite m, line",
        , "jWriteO call jBufWriteO m, var"
    call classNew "n JBufTxt u JBuf, f MAXL v ", "m",
        , "jReset call jBufReset m, arg; m.m.maxl = 80",
        , "jWriteO call jBufWrite m, o2Text(var, m.m.maxl)"
    return
endProcedure jIni

/*--- out interface of j --------------------------------------------*/
outIni: procedure expose m.
    call jIni
    return
endProcedure outIni

outDst: procedure expose m.
parse arg wrt
    oldOut = m.j.out
    if wrt == '' then
        wrt = jOpen(oNew('JSay'), '>')
    m.j.out = wrt
    return oldOut
endProcedure outDst

/*--- return a JRW from rdr or in ------------------------------------*/
j2Rdr: procedure expose m.
    parse arg rdr
    if oStrOrObj(rdr, m.j.in) then
        return jBuf(ggStr)
    else
        return o2file(ggObj)
endProcedure j2Rdr
      /* jstr is part of out interface --> inp2str */
inp2str: procedure expose m.
    parse arg rdr, fmt
    if oStrOrObj(rdr, m.j.in) then
        return ggStr
    else
        return o2String(ggObj, fmt)
endProcedure inp2str

j2Buf: procedure expose m.
    parse arg rdr
    if oStrOrObj(rdr, m.j.in) then
        return jBuf(ggStr)
    if classInheritsOf(ggCla, class4Name('JBuf')) ,
            & m.ggObj.jUsers < 1 then
        return ggObj
    b = jOpen(jBuf(), m.j.cWri)
    call jWriteNow b, o2File(ggObj)
    return jClose(b)
endProcedure j2Buf

in: procedure expose m.
parse arg arg
    return jRead(m.j.in, arg)
endProcedure in

inO: procedure expose m.
    if arg() > 0 then call err '??? old interface'
    return jReadO(m.j.in)
endProcedure in

out: procedure expose m.
parse arg line
    call jWrite m.j.out, line
    return 0
endProcedure out

outO: procedure expose m.
parse arg arg
    call jWriteO m.j.out, arg
    return
endProcedure outO

/*--- jBuf: buffer read or write (supports datataypes) ---------------*/
jBuf: procedure expose m.
    m = oNew('JBuf') /* calls jBufReset */
    do ax=1 to arg()
        m.m.buf.ax = arg(ax)
        end
    m.m.buf.0 = ax-1
    m.m.allV = 1
    return m
endProcedure jBuf
/*--- jBuf: buffer read or write (supports datataypes) ---------------*/
jBufTxt: procedure expose m.
    m = oNew('JBufTxt') /* calls jBufReset */
    do ax=1 to arg()
        m.m.buf.ax = arg(ax)
        end
    m.m.buf.0 = ax-1
    m.m.allV = 1
    return m
endProcedure jBufTxt

jBufReset: procedure expose m.
parse arg m
    m.m.stem = m'.BUF'
    do ax=1 to arg() - 1
        m.m.buf.ax = arg(ax+1)
        end
    m.m.buf.0 = ax-1
    m.m.allV = 1
    return m
endProcedure jBufReset

jBufOpen: procedure expose m.
parse arg m, opt
    if opt == m.j.cRead then do
        m.m.readIx = 0
        m.m.jReading = 1
        return m
        end
    if opt == m.j.cWri then do
        m.m.buf.0 = 0
        m.m.allV = 1
        end
    else if opt \== m.j.cApp then
         call err 'jBufOpen('m',' opt') with bad opt'
    m.m.jWriting = 1
    return m
endProcedure jBufOpen

jBufWrite: procedure expose m.
parse arg m, line
    if m.m.allV then
        call mAdd m'.BUF', line
    else
        call mAdd m'.BUF', s2o(line)
    return
endProcedure jBufWrite

jBufWriteStem: procedure expose m.
parse arg m, st
    ax = m.m.buf.0
    if m.m.allV then do
        do sx=1 to m.st.0
            ax = ax + 1
            m.m.buf.ax = m.st.sx
            end
        end
    else do
        do sx=1 to m.st.0
            ax = ax + 1
            m.m.buf.ax = o2String(m.st.sx)
            end
       end
       m.m.buf.0 = ax
    return m
endProcedure jBufWrite

jBufWriteO: procedure expose m.
parse arg m, ref
    if m.m.allV then do
        cl = objClass(ref)
        if cl = m.class.classV then do
            call mAdd m'.BUF', m.ref
            return
            end
        if cl == m.class.classW then do
            call mAdd m'.BUF', substr(ref, 2)
            return
            end
        m.m.allV = 0
        do ax=1 to m.m.buf.0
            m.m.buf.ax = s2o(m.m.buf.ax)
            end
        end
    call mAdd m'.BUF', ref
    return
endProcedure jBufWriteO

jBufReadO: procedure expose m.
parse arg m
    nx = m.m.readIx + 1
    if nx > m.m.buf.0 then
        return ''
    m.m.readIx = nx
    if m.m.allV then
        return s2o(m.m.buf.nx)
    else
        return m.m.buf.nx
endProcedure jBufReadO

jBufRead: procedure expose m.
parse arg m, var
    nx = m.m.readIx + 1
    if nx > m.m.buf.0 then
        return 0
    m.m.readIx = nx
    if m.m.allV then
        m.var = m.m.buf.nx
    else
        m.var = o2String(m'.BUF.'nx)
    return 1
endProcedure jBufRead

jBufTxtWriteO: procedure expose m.
parse arg m, ref
    if m.m.allV \== 1 then
        call err '1 \== allV' m.m.allV 'in jBufTxtWriteO('m',' ref')'
    cl = objClass(ref, '?')
    if cl = m.class.classV then
        call mAdd m'.BUF', m.ref
    else if cl == m.class.classW then
        call mAdd m'.BUF', substr(ref, 2)
    else if ref == '' then
        call mAdd m'.BUF', '@ null object'
    else if cl == '?' then
        call mAdd m'.BUF', '@'ref 'class=???'
    else do
        l = '@'ref 'class='className(cl)
        ff = cl'.FLDS'
        do fx=1 to m.ff.0 while length(l) < m.m.maxl + 3
            if m.ff.fx == '' then
                 l = l', .='m.ref
            else do
                 f1 = substr(m.ff.fx, 2)
                 l = l',' f1'='m.ref.f1
                 end
            end
        if length(l) > m.m.maxl then
            l = left(l, m.m.maxl-3)'...'
        call mAdd m'.BUF', l
        end
    return
endProcedure jBufTxtWriteO

/* copy j end *********************************************************/
/* copy o begin ******************************************************
    an object has a class which describes fields and methods
    an object has fields (e.g. m.o.fld1)
    an object may call it's methods (dynamic binding)
***********************************************************************/
oIni: procedure expose m.
    if m.o.ini = 1 then
        return
    m.o.ini = 1

    call classIni
    call oAdd1Method m.class.classV, 'o2String return m.m'
    m.class.escW = ']'
    call oAdd1Method m.class.classW, 'o2String return substr(m, 2)'
    or = classNew('n ORun u',
         , 'm oRun call err "call of abstract method oRun"',
         , 'm o2File return oRun2File(m)',
         , 'm o2String return jCatLines(oRun2File(m), fmt)')
                /* oRunner does not work yet ||||| */
    rc = classNew('n* ORun u ORun, m oRun call oClassAdded arg(2)')
    call oAddMethod rc'.OMET', rc
    call classAddedRegister oMutate(mNew(), rc)
    return
endProcedure oIni

/*--- when notified about a new class cl, build the redundancies ----*/
oClassAdded: procedure expose m.
parse arg cl
    m.class.o2c.cl = m.class.class
    call oAddMethod cl'.OMET', cl
    new = "m.class.o2c.m =" cl
    if m.cl.flds.0 > 0 | m.cl.stms.0 > 0 then
        new = new"; call oClear m, '"cl"'"
    new = new";" classMet(cl, 'new', '')
    if cl == m.class.class then
        call mAlias 'CLASS', cl
    else  /* object adresses */
        call mNewArea cl, 'O.'substr(cl,7), new
     if m.cl \== 'u' | m.cl.name == '' then
        return
    call mAlias cl, m.cl.name
    new = 'new'
    m.cl.oMet.new = ''
    co = ''                              /* build code for copy */
    do fx=1 to m.cl.flds.0
        nm = m.cl.flds.fx
          if translate(nm) == nm & \ abbrev(nm, 'GG') ,
              & pos('.M.', nm'.') < 1 & pos('.T.', nm'.') < 1 then
               co = co'm.t'nm '= m.m'nm';'
        else
            co = co 'f='quote(substr(nm, 2))';m.t.f = m.m.f;'
        end
    do fx=1 to m.cl.stms.0
        nm = m.cl.stms.fx
        sc = m.cl.stms.fx.class
        if nm == ''then
            co = co "m.t.0=m.m.0;" ,
               "do sx=1 to m.m.0;" ,
                 "call oClaCopy '"sc"',m'.'sx, t'.'sx; end;"
        else
            co = co "st='"substr(nm, 2)"';m.t.st.0=m.m.st.0;",
                "do sx=1 to m.m.st.0;",
                  "call oClaCopy '"sc"',m'.'st'.'sx, t'.'st'.'sx; end;"
        end
    p = cl'.OMET.oCopy'
    if symbol('m.p') \== VAR then
        m.p = co
    return
endProcedure oClassAdded

/*--- add the methods of class cl to the methodtable mt -------------*/
oAddMethod: procedure expose m.
parse arg mt, cl
     if pos(m.cl, 'frsv') > 0 then
         return
     if m.cl = 'm' then do
         nm = m.cl.name
         m.mt.nm = m.cl.met
         return
         end
/*     if m.cl.class \== '' then
         call oAddMethod mt, m.cl.class
*/   do x=1 to m.cl.0
         call oAddMethod mt, m.cl.x
         end
     return
endProcedure oAddMethod

/* add 1 method to a completed class and its subclasses -------------*/
oAdd1Method: procedure expose m.
parse arg clNm, met code
    cl = classAdd1Method(clNm, met code)
    m.cl.omet.met = code
    call oAdd1MethodSubs cl, met code
    return cl
endProcedure oAdd1Method

/* add 1 method code to OMET of all subclasses of cl  -------------*/
oAdd1MethodSubs: procedure expose m.
parse arg cl, met code
    do sx=1 to m.cl.sub.0
        sc = m.cl.sub.sx
        if pos(m.sc, 'nvw') > 0 then do
            do mx=1 to m.sc.0
                ms = m.sc.mx
                if m.ms == 'm' & m.ms.name == met then
                    call err 'method' med 'already in' sc
                end
            m.sc.omet.met = code
            end
        call oAdd1MethodSubs sc, met code
        end
    return cl
endProcedure oAdd1MethodSubs

/*--- create an an object of the class className
        mutate it to class but DO NOT call it's new method ----------*/
oBasicNew: procedure expose m.
parse arg cl
    return oMutate(mBasicNew(cl), cl)

/*--- create an an object of the class className
        and call it's new method ------------------------------------*/
oNew: procedure expose m.
signal labelMNew    /* work is done there |   ???? remove */

/*--- return the class of object obj --------------------------------*/
objClass: procedure expose m.
parse arg obj
    if symbol('m.class.o2c.obj') == 'VAR' then
         return m.class.o2c.obj
    if abbrev(obj, m.class.escW) then
        return m.class.classW
    if abbrev(obj, 'CLASS.CAST.') then
        return substr(obj, 12, pos(':', obj, 12)-12)
    if arg() >= 2 then
        return arg(2)
    return err('objClass no class found for object' obj)
endProcedure objClass

oKindOf: procedure expose m.
parse arg obj, sup
    cl = objClass(obj, '')
    if cl == '' then
        return 0
    return classInheritsOf(cl, class4name(sup))
endProcedure oKindOf

classInheritsOf: procedure expose m.
parse arg cl, sup    /* wkTst optimierung in classAdded */
    if cl == sup then
        return 1
    do while m.cl \== 'n' & m.cl \== 'u'
        if m.cl.class == '' then
            return 0
        cl = m.cl.class
        end
    do cx=1 to m.cl.0
        d = m.cl.cx
        if m.d == 'u' then
            if classInheritsOf(d, sup) then
                return 1
        end
    return 0
endProcedure classInheritsOf

classSetMet: procedure expose m.
parse arg na, me, code
    if symbol('m.class.n2c.na') \== 'VAR' then
        call err 'no class' na 'in classMet('na',' me')'
    cl = m.class.n2c.na
    if symbol('m.cl.oMet.me') \== 'VAR' then
        call err 'no method in classMet('na',' me')'
    m.cl.oMet.me = code
    return cl
endProcedure classSetMet

/*--- return the code of method me of the class with name na --------*/
classMet: procedure expose m.
parse arg na, me
    if symbol('m.class.n2c.na') \== 'VAR' then
        call err 'no class' na 'in classMet('na',' me')'
    cl = m.class.n2c.na
    if symbol('m.cl.oMet.me') == 'VAR' then
        return m.cl.oMet.me
    if arg() >= 3 then
        return arg(3)
    call err 'no method in classMet('na',' me')'
endProcedure classMethod

/*--- set m, ggClass, ggCode to the address, class and code
        of method me of object m ------------------------------------*/
objMetClaM: procedure expose m. m ggClass ggCode
parse arg m, me
    if symbol('m.class.o2c.m') == 'VAR' then
         ggClass =  m.class.o2c.m
    else if abbrev(m, 'CLASS.CAST.') then
        parse var m 'CLASS.CAST.' ggClass ':' m
    else
        return err('no class found for object' m)
    if symbol('m.ggClass.oMet.me') == 'VAR' then
        ggCode = m.ggClass.oMet.me
    else
         call err 'no method' me 'in class' className(ggClass),
              'of object' m
    return
endProcedure objMetClaM

/*--- return the code of method me of object obj --------------------*/
objMet: procedure expose m.
parse arg obj, me
        /* handle the easy and frequent case directly */
    if symbol('m.class.o2c.obj') == 'VAR' then
         c =  m.class.o2c.obj
    else if abbrev(obj, m.class.escW) then
         c = m.class.classW
    else do
        call objMetClaM obj, me
        return 'M="'m'";'ggCode
        end
     if symbol('m.c.oMet.me') == 'VAR' then
         return m.c.oMet.me
    return err('no method' me 'in class' className(c) 'of object' obj)
endProcedure objMet

/*--- return the stem of fieldnames of object m ---------------------*/
oFlds: procedure expose m.
parse arg m
    return objClass(m)'.FLDS'
endProcedure oFlds

/*--- return the contents of field f navigation along path ----*/
oGet: procedure expose m.
parse arg obj, path, clazz
    nullNew = 0
    ret = oAccPath(obj, path, clazz)
    if ret == 1 then
        ret = oAccStr(m, cl)
    if ret == 1 then
        return str
    return err(ret 'in oGet('obj',' path')')
endProcedure oGet

oAccStr: procedure expose m. str
parse arg m, cl
    if cl == m.class.classV then
        str = m.m
    else if m.cl.valueCl == '' then
        return 'no value @' m 'class' className(cl)
    else if m.m == '' then
        return 'null @' m 'class' className(cl)
    else if abbrev(m, m.class.escW) then
        str = substr(m ,2)
    else
        str = o2String(m.m)
    return 1
endProcedure oAccStr

oGetO: procedure expose m.
parse arg obj, path, opt, clazz
    nullNew = pos('n', opt) > 0
    ret = oAccPath(obj, path, clazz)
    if ret == 1 then
        ret = oAccO(m, cl, opt)
    if ret == 1 then
        return ref
    else
        return err(ret 'in oGetO('obj',' path')')
endProcedure oGetO

oAccO: procedure expose m. ref
parse arg m, cl, opt
    if cl == m.class.classV then do
        ref = s2o(m.m)
        end
    else if m.cl \== 'r' then do
        ref = m
        end
    else if m.m == '' then do
        if opt == '-b' then do
            m.m = jBuf()
            end
        else if opt == '-n' then do
            rsn = oRefSetNew(m, cl)
            if rsn \==1 then
               return rsn
            end
        ref = m.m
        end
    else if objClass(m.m, 0) \== 0 then do
        ref = m.m
        end
    else do
        return 'no class for' m.m '@' m 'class' cl
        end
    return 1
endProcedure oAccO

oPut: procedure expose m.
parse arg obj, path, str
    nullNew = 1
    res = oAccPath(obj, path)
    if res == 1 then
        res = ocPut(m, cl, str)
    if res == 1 then
        return str
    return err(res 'in oPut('obj',' path',' str')')
endProceudre oPut

ocPut: procedure expose m.
parse arg m, cl, str
    if m.cl.valueCl == m.class.classV then
        m.m = str
    else if m.cl.valueCl \== '' then
        m.m = s2o(str)
    else
        return 'no value @' m 'class' className(cl)
    return 1
endProcedure ocPut

oPutO: procedure expose m.
parse arg obj, path, ref
    nullNew = 1
    res = oAccPath(obj, path)
    if res == 1 then
        res = ocPutO(m, cl, ref)
    if res == 1 then
        return ref
    return err(ret 'in oPut('obj',' path',' ref')')
endProcedure oPutO

ocPutO: procedure expose m.
parse arg m, cl, ref
    if m.cl.valueCl == m.class.classV then
        m.m = o2string(ref)
    else if m.cl.valueCl \== '' then
        m.m = ref
    else if m.cl.stemCl \== '' then
        return 'implement put to stem'
    else
        return 'no value @' m 'class' className(cl)
    return 1
endProcedure ocPutO

oClear: procedure expose m.
parse arg obj, cl
    if cl == '' then
        cl = objClass(obj)
    do fx=1 to m.cl.flds.0
        f1 = m.cl.flds.fx
        o1 = obj || f1
        if f1 == '' then
            c1 = cl
        else do
            c1 = substr(f1, 2)
            c1 = m.cl.f2c.c1
            end
        if c1 == m.class.classW then
            m.o1 = m.class.escW
        else
            m.o1 = ''
        end
    do sx=1 to m.cl.stms.0
        f1 = obj || m.cl.stms.sx
        m.f1.0 = 0
        end
    return obj
endProcedure oClear

oAccPath: procedure expose m. m cl nullNew
parse arg m, pa, cl
    if cl == '' & m \== '' then do
        cl = objClass(m)
        end
    if pa == '' then
        return 1
    if abbrev(pa, m.class.cRef) ,
            | (\ m.cl.hasFlds & abbrev(pa, m.class.cNav)) then do
        if pa == m.class.cRef & m.cl.valueCl == m.class.classV then do
            cl = m.class.classV
            return 1
            end
        if (m.cl.valueCl == '' | m.cl.valueCl == m.class.classV) ,
              & m.cl \== 'r' then
            return 'no reference @' m 'class' cl
        if m.m = '' then do
            if \ nullNew then
                return 'null @' m 'class' className(cl)
            rsn = oRefSetNew(m, cl)
            if rsn \== 1 then
                return rsn
            end
        return oAccPath(m.m, substr(pa, 2))
        end
    if pos(left(pa, 1), m.class.cPath) > 0 then
        return oAccPath(m, substr(pa, 2), cl)
    px = verify(pa, m.class.cPath, 'm')
    if px < 1 then
        px = length(pa)+1
    fn = left(pa, px-1)
    pa = substr(pa, px)
    if symbol('m.cl.f2c.fn') == 'VAR' then
        return oAccPath(m'.'fn, pa, m.cl.f2c.fn)
    if m.cl.stemCl=='' | fn=='' | verify(fn, '0123456789','n')>0 then
        return 'no field' fn '@' m 'class' className(cl)
    if fn == 0 then
        return oAccPath(m'.0', pa, m.class.classV)
    if abbrev(fn, 0) | verify(m.m.0, '0123456789', 'n') > 0,
            | fn > m.m.0 then
        return 'bad stem index' fn'>'m.m.0 '@' m 'class' className(cl)
    return oAccPath(m'.'fn, pa, m.cl.stemCl)
endProcedure oAccPath

oRefSetNew: procedure expose m.
parse arg m, cl
    cr = m.cl.valueCl
    if m.cr.class = '' then
        return 'no class for null @' m 'class' className(cl)
    if m.cr.class = m.class.classW then
        m.m = o2s()
    else if m.cr \== 'r' then
        return 'class' className(cl) 'not ref'
    else
        m.m = mNew(m.cr.class)
    return 1
endProcedure oRefSetNew


/*--- mutate object m to the class named name -----------------------*/
oMutate: procedure expose m.
parse arg m, name
    m.class.o2c.m = class4Name(name)
    return m
endProcedure oMutate

/*--- return object obj cast'd to class named cl --------------------*/
oCast: procedure expose m.
parse arg obj, cl
     if abbrev(obj, 'CLASS.CAST.') then
         obj = substr(obj, 1 + pos(':', obj, 12))
     return 'CLASS.CAST.'class4Name(cl)':'obj
endProcedure oCast

/*--- copy object m of class c to t ---------------------------------*/
oClaCopy: procedure expose m.
parse arg ggCla, m, t
    if t == '' then do
        if ggCla == m.class.classW then
            return m
        t = mBasicNew(ggCla)
        end
     else if ggCla == m.class.classW then do
         m.t = o2String(m)
         m.class.o2c.t = m.class.classV
         return t
         end
     ggCode = ggCla'.OMET.oCopy'
     interpret m.ggCode
     m.class.o2c.t = ggCla
     return t
endProcedure oClaCopy

/*--- copy object m to t --------------------------------------------*/
oCopy: procedure expose m.
parse arg m, t
    return oClaCopy(objClass(m), m, t)
endProcedure oCopy

/*--- copy object to a newly created object -------------------------*/
oCopyNew: procedure expose m.
parse arg m
     if symbol('m.class.o2c.m') == 'VAR' then
         return oCopy(m, mBasicNew(m.class.o2c.m))
     return oCopy(m, mBasicNew(m.class.classV))
endProcedure oCopyNew

/*--- return a new instance of a subclass of Run
        with code code in method oRun -------------------------------*/
oRunner: procedure expose m.
    if arg() >= 1 then
           r = oNew(classNew('n* ORun u ORun, m oRun' arg(1)))
    else
           r = oNew(classNew('n| ORun u ORun'))
    return r
endProcedure oRunner

/*--- set code for runner -------------------------------------------*/
oRunnerCode: procedure expose m.
parse arg r, code
    call classSetMet objClass(r), 'oRun', code
    return r
endProcedure oRunnerCode

/*--- run method oRun of object m -----------------------------------*/
oRun: procedure expose m.
parse arg m, arg, arg2, arg3
    interpret objMet(m, 'oRun')
    return
endProcedure oRun

/*--- run method oRun and return output in new JBuf ------------------*/
oRun2File: procedure expose m.
parse arg rn
    b = jBuf()
    call pipeBeLa '>' b
    call oRun rn
    call pipeEnd
    return b
endProcedure oRun2File

/*--- cast the object to a file -------------------------------------*/
o2File: procedure expose m.
parse arg m
    interpret objMet(m, 'o2File')
    call err 'o2file did not return'
endProcedure o2File

/*--- cast the object to a String -----------------------------------*/
o2String: procedure expose m.
parse arg m, fmt
    if opt == '' then
        opt = '-b '
    interpret objMet(m, 'o2String')
    return err('o2String did not return')
endProcedure o2String

/*--- return true if object is kind of String------------------------*/
oStrOrObj: procedure expose m. ggStr ggObj ggCla
parse arg ggObj, def
    if ggObj == '' then
        ggObj = def
    ggCla = objClass(ggObj, '')

    if ggCla == '' then do
        ggStr = ggObj
        ggObj = ''
        return 1
        end
    else if wordPos(ggCla, m.class.classV m.class.classW) > 0 then do
        ggStr = o2String(ggObj)
        ggObj = ''
        return 1
        end
    else do
        ggStr = ''
        return 0
        end
endProcedure oStrOrObj

/*--- return true if object is kind of String ----- ???? -------------*/
oStrOrFile: procedure expose m. ggStr ggObj ggCla
parse arg m, def
    if oStrOrObj(m, def) then
        return 1
    ggObj = o2File(ggObj)
    return 0
endProcedure oStrOrFile

/*--- return a short string representation of an object -------------*/
o2Text: procedure expose m.
parse arg m, maxL
    if m == '' then
        return '@ null object'
    if maxL == '' then
        maxL = 80
    cl = objClass(m, '?')
    if cl = m.class.classV then
        l = m.m
    else if cl == m.class.classW then
        l = substr(m, 2)
    else if cl == '?' then
        l = '@'m 'class=???'
    else do
        l = '@'m 'class='className(cl)
        ff = cl'.FLDS'
        do fx=1 to m.ff.0 while length(l) < maxL + 3
            if m.ff.fx == '' then
                 l = l', .='m.m
            else do
                 f1 = substr(m.ff.fx, 2)
                 l = l',' f1'='m.m.f1
                 end
            end
        end
    if length(l) <= maxL then
        return l
    return left(l, maxL-3)'...'
endProcedure o2Text

/*--- cast a String to an object -----------------------------------*/
s2o: procedure expose m.
parse arg str
    return m.class.escW || str
    return r
endProcedure s2o

oIfStr: procedure expose m.
parse arg m
    if length(m) > 200 then
        return m
    cl = objClass(m, '')
    if cl = '' then
        return m
    else if cl = m.class.classV then
        return = m.m
    else if cl == m.class.classW then
        return = substr(m, 2)
    else if arg() >= 2 then
        return arg(2)
    else
        call err m 'of class' className(cl) 'not kind of string'
endProcedure oIfStr

/* copy o end *******************************************************/
/* copy class begin **************************************************
    a class has fields and methods,
    the class module handles only the metadata,
    object handling (instanciation, methodcalls etc.) is in O

    classes are represented by a metadata tree,
        its nodes of class class have diffenrent types:

class subTypes (implemented as choices)
    'u'    = union:    NAME -> name of class if <> '',
                    stem -> references component classes
    'f' = field:      NAME -> fieldName (x.name),
                    CLASSS -> reference to class of fieldValue
    's' = stem:     class -> ref to class at each stem element
    'c' = choice:   NAME -> selection value,
                    CLASS -> ref to class of choice
    'm' = method:    NAME -> methodName,
                    MET -> rexxCode
    'r' = reference CLASS -> ref to type at reference
special classes
    'v'    = Value     String Value
    'w'    = ValueAsA    StringValue packed into an adress (prefix escW)
    'o' = AnyClass    any class with dynamic classLookup on object
formal definition, see classIni

class expression (ce) allow the following syntax
    ce = className | classAdr | 'n'('?','*','|')? name union | union
        | 'f' name ce | 's' ce | 'c' name ce | 'm' name code | r ce?
    union = 'u' (ce (',' ce)*)?

    the modifiers of 'n' means
        none:    create new class, fail if name already defined
        '?':    create new class or return old of that name
        '*':    use an exisiting class of that definition
                or create new class with a unique name
        '|':    create a new class with a unique name
    'm' extends to then end of the ce (line)
    'u' allows several components, in classNew also multiple args
                Achtung, aber NICHT rekursiv|
***********************************************************************/
classIni: procedure expose m.
    if m.class.ini == 1 then
        return
    m.class.ini = 1
    call mapIni
    call mNewArea 'CLASS', 'CLASS'
    call mapReset 'CLASS.N2C'  /* name to class */
        /* to notify other modules (e.g. O) on every new named class */
    m.class.addedSeq.0 = 0
    m.class.addedListeners.0 = 0
    m.class.classV = classBasicNew('u', 'v')
    m.class.classW = classBasicNew('u', 'w')
    m.class.classO = classBasicNew('u', 'o')
    cr = mIterBegin('CLASS')
    do while assNN('cr', mIter(cr))
        call classAddedNotify cr
        end

    m.class.class = classNew('n class u v',
            , 'c u u f NAME v, s r class',
            , 'c f u f NAME v, f CLASS r class',
            , 'c s f CLASS r class' ,
            , 'c c u f NAME v, f CLASS r class',
            , 'c m u f NAME v, f MET  v' ,
            , 'c r f CLASS r class' )
    m.class.cNav = '.'
    m.class.cRef = '|'
    m.class.cDot = '%'
    m.class.cPath = m.class.cNav || m.class.cRef || m.class.cDot
    m.class.classR = classNew('r')
    return
endProcedure classIni


/*--- return the name of a class if it exists otherwise the class ---*/
className: procedure expose m.
parse arg cl
    if m.cl = 'u' & m.cl.name \= '' then
        return m.cl.name
    else
        return cl
endProcedure class4Name

/*--- return class of given name or class ---------------------------*/
class4Name: procedure expose m.
parse arg nm
    if symbol('m.class.n2c.nm') == 'VAR' then
        return m.class.n2c.nm
    if arg() > 1 then
        return arg(2)
    call err 'no class' nm
endProcedure class4Name

classBasicNew: procedure expose m.
parse arg ty, nm, cl, nmTy
    n = mNew('CLASS')
    m.n = ty
    m.n.name = nm
    m.n.nameComp = nm
    if ty == 'u' & nm \== '' then do
        if pos(nmTy, '*|') > 0 then do
            m.n.name = nm || substr(n, 1+lastPos('.', n))
            if nmTy == '*' then
                m.n.nameComp = nm'*'
            else
                m.n.nameComp = m.n.name
            end
        call mapAdd class.n2c, m.n.name, n
        end
    call mapAdd class.n2c, n, n
    m.n.class = ''
    m.n.met = ''
    m.n.0 = 0
    m.n.sub.0 = 0
    m.n.super.0 = 0
    if length(ty) \== 1 | pos(ty, 'ufscrm') < 1 then
        call err 'bad type' ty': classBasicNew('ty',' nm',' cl')'
    else if nm == '' & pos(ty, 'fm') > 0 then
        call err 'empty name: classBasicNew('ty',' nm',' cl')'
    else if nm \== '' & ty \== 'c'  ,
          & ( verify(nm, '0123456789') < 1 ,
            | verify(nm, ' .*|@', 'm') > 0 ) then
        call err 'bad name' nm': classBasicNew('ty',' nm',' cl')'
    else if nm \= '' & pos(ty, 'rs') > 0 then
        call err 'name for type' ty': classBasicNew('ty',' nm',' cl')'
    else if pos(ty, 'fcrs') > 0 then do
        if cl \== '' then
            m.n.class = mapGet(class.n2c, cl)
        else if ty == 'r' then
            m.n.class = m.class.classO
  /*    else say 'cl leer' ty nm nmTy   ???????*/
        end
    else if ty == 'm' then
        m.n.met = cl
    else if cl \== '' then
        call err 'class for type' ty': classBasicNew('ty',' nm',' cl')'
    return n
endProcedure classBasicNew


classNew: procedure expose m.
parse arg clEx 1 ty rest
    if abbrev(ty, 'n') then do
        if wordPos(ty, 'n n? n* n|') < 1 then
            call err 'bad type' ty': classNew('clEx')'
        nmTy = right(ty, 1)
        parse var rest nm ty rest
        if ty \== 'u' then
            call err 'class name' nm 'without u: classNew('clEx')'
        if nmTy == 'n' then do
             if mapHasKey(class.n2c, nm) then
                call err 'class' nm 'already defined: classNew('clEx')'
            end
        else if nmTy == '?' then do
            if mapHasKey(class.n2c, nm) then
                return mapGet(class.n2c, nm)
            end
        else if nmTy == '*' then do
            if arg() \== 1 then
                call err 'arg()='arg() 'for n* : classNew('clEx')'
            if mapHasKey(class.n2c, clEx) then
                return mapGet(class.n2c, clEx)
            end
        n = classBasicNew('u', nm, , nmTy)
        end
    else do
        nmTy = ''
        if arg() \== 1 then
            call err 'arg()='arg() 'without name: classNew('clEx')'
        if mapHasKey(class.n2c, clEx) then
               return mapGet(class.n2c, clEx)
        if length(ty) <> 1 | pos(ty, 'ufscmr') < 1 then
            call err 'bad type' ty': classNew('clEx')'
        nm = ''
        if pos(ty, 'usr') < 1 then
            parse var rest nm rest
        if ty = 'u'  then do
            n = classBasicNew(ty)
            end
        else if    ty = 'm' then do
            n = classBasicNew(ty, nm, rest)
            rest = ''
            end
        else do
            parse var rest t1 rest
            if wordPos(t1, 'u f s c m r') > 0 then do
                n = classBasicNew(ty, nm)
                m.n.class = classNew(t1 rest)
                rest = ''
                end
            else do
                n = classBasicNew(ty, nm, t1)
                end
            end
        end
    if ty \== 'u' then do
        if rest \== '' then
            call err 'rest' rest 'but end of classExp expected:' clEx
        end
    else do
        lx = 0
        do while lx < length(rest)
            cx = pos(',', rest, lx+1)
            if cx <= lx | word(substr(rest, lx+1), 1) == 'm' then
                cx = length(rest)+1
            a = mAdd(n, classNew(strip(substr(rest, lx+1, cx-lx-1))))
            lx=cx
            end
        pref = ''
        do ax=2 to arg()
            if length(arg(ax)) == 1 & arg(ax) \== ' ' then
                pref = arg(ax)' '
            else
                call mAdd n, classNew(pref || arg(ax))
            end
        end
    cr = mIterBegin('CLASS')
    do while assNN('cr', mIter(cr)) & \ classEqual(n, cr)
        end
    isNew = cr == n
    if \ isNew then do
        if mapRemove(class.n2c, n) \== n then
            call err 'mapRemove('n') mismatch'
        if m.n == 'u' & m.n.name \== '' then
            if mapRemove(class.n2c, m.n.name) \== n then
                call err 'mapRemove('m.n.name') mismatch'
        call mFree n
        n = cr
        end
    if nmTy == '' | nmTy == '*' then
        call mapAdd class.n2c, clEx, n
    if isNew then
        call classAddedNotify n
    return n
endProcedure classNew

classAdd1Method: procedure expose m.
parse arg clNm, met code
    cl = class4Name(clNm)
    if pos(m.cl, 'uvw') < 1 then
        call err 'class not nvw but' m.cl,
            'in classAdd1Method('clNm',' met code')'
    do sx = 1 to m.cl.0
        su = m.cl.sx
        if m.cl.sx = 'm' & m.cl.name == met then
            call err 'met' met 'already in' clNm
        end
    call mAdd cl, classNew('m' met code)
    return cl
endProcedure classAdd1Method

/*--- register a listener for newly defined classes
        and call it for all already defined classes -----------------*/
classAddedRegister: procedure expose m.
parse arg li
    call mAdd 'CLASS.ADDEDLISTENERS', li
    do cx = 1 to m.class.addedSeq.0
        call oRun li, m.class.addedSeq.cx
        end
    return
endProcedure classAddedRegister

/*--- to notify all listeners about a newly defined classes --------*/
classAddedNotify: procedure expose m.
parse arg cl
    call mAdd 'CLASS.ADDEDSEQ', cl
    if m.cl == 'u' then
        call classSuperSub cl
    m.cl.flds.0 = 0
    m.cl.stms.0 = 0
    m.cl.stemCl = ''
    m.cl.valueCl = ''
    call classAddFields cl, cl
    m.cl.hasFlds = m.cl.flds.0 > 1 ,
        | (m.cl.flds.0 == 1 & m.cl.flds.1 \== '') | m.cl.stms.0 > 0
    do lx = 1 to m.class.addedListeners.0
        call oRun m.class.addedListeners.lx, cl
        end
    return
endProcedure classAddedNotify

/*--- add supper and sub links for class cl -------------------------*/
classSuperSub: procedure expose m.
parse arg cl
    do ux=1 to m.cl.0
        u1 = m.cl.ux
        if m.u1 == 'u' then do
            if mPos(cl'.SUPER', u1) > 0 then
                call err u1 'is already in' cl'.SUPER.'sx ,
                    || ': classSuperSub('cl')'
            call mAdd cl'.SUPER', u1
            if mPos(cl'.SUB', cl) > 0 then
                call err cl 'is already in' u1'.SUB.'sx ,
                    || ': classSuperSub('cl')'
            call mAdd u1'.SUB', cl
            end
        end
    return
endProcedure classSuperSub

/*--- add the the fields of class cl to stem f ----------------------*/
classAddFields: procedure expose m.
parse arg f, cl, nm
    n1 = substr(nm, 1+abbrev(nm, '.') )
    if symbol('m.f.f2c.n1') \== 'VAR' then
        m.f.f2c.n1 = cl
/*    else if cl == m.f.f2c.n1 then
        return 0 */
    if cl == m.class.classV | cl == m.class.classW | m.cl=='r' then do
        if nm == '' then do
            if m.f.valueCl \== '' then
                return  err('value mistmatch')
            m.f.valueCl = cl
            end
        if nm == '' then do
             call mMove f'.FLDS', 1, 2
             m.f.flds.1 = ''
             end
        else do
            call mAdd f'.FLDS', nm
            end
        return 0
        end
    if m.cl = 's' then do
        if m.cl.class == '' then
            call err 'stem null class'
        a1 = mAdd(f'.STMS', nm)
        m.a1.class = m.cl.class
        if nm == '' then
            m.f.stemCl = m.cl.class
        return 0
        end
    if m.cl = 'f' then
        return classAddFields(f, m.cl.class, nm'.'m.cl.name)
    if m.cl.class \== '' then
        return classAddFields(f, m.cl.class, nm)
    do tx=1 to m.cl.0
        call classAddFields f, m.cl.tx, nm
        end
    return 0
endProcedure classAddFields

/*--- return true iff the two classes are equal -------------------*/
classEqual: procedure expose m.
parse arg l, r
    if m.l \== m.r | m.l.nameComp \== m.r.nameComp ,
            | m.l.class \== m.r.class | m.l.0 \== m.r.0  then
        return 0
    if m.l.met \== m.r.met  then
        return 0
    do sx=1 to m.l.0
        if m.l.sx \== m.r.sx then
            return 0
        end
    return 1
endProcedure classEqual

/*--- print object ---------------------------------------------------*/
objOut: procedure expose m.
parse arg m, pr, p1
   c = objClass(m, '')
   if c == '' then
       call out p1 'no class for' m
   else if c == m.class.classV then
       call out p1 || m.m
   else if c == m.class.classW then
       call out p1 || o2String(m)
   else
       call classOutDone c, m, pr, p1
   return
endProcedure objOut

/*--- recursively output (with out:) the object a with class t -------*/
classOut: procedure expose m.
parse arg t, a, pr, p1
    return classOutDone(if(t=='',m.class.classO, t), a, pr, p1)
endProcedure classOut

/*--- ouput object a with class t and stopper done ------------------*/
classOutDone: procedure expose m. done.
parse arg t, a, pr, p1
    if p1 == '' then
        p1 = pr
    if right(p1, 1) \== ' ' then
        p1 = p1' '
    if done.ini \== 1 then do
        done.ini = 1
        t = class4Name(t, t)
        p1 = p1'@'a' '
        end
    if done.t.a == 1 then
        return out(p1'done :'className(t) '@'a)
    done.t.a = 1
    if t = m.class.classO then do
        if a == '' then
            return out(p1'obj null')
        t = objClass(a, '')
        if t = '' then
            return out(p1'obj has no class @'m.a)
        else
            return classOutDone(t, a, pr, p1'isA')
        end

    if t == m.class.classV then
        return out(p1'=' m.a)
    if t == m.class.classW == 'w' then
        return out(p1'}' substr(a, 2))
    if m.t == 'f' then
        return classOutDone(m.t.class, a'.'m.t.name, pr, p1'.'m.t.name)
    if m.t == 'r' then do
        if m.a == '' then
            return out(p1'refTo :'className(m.t.class) '@null@')
        else
            return classOutDone(m.t.class, m.a, pr,
                    , p1'refTo @'m.a)
        end
    if m.t = 'u' then do
        t1 = m.t.1
        vv = m.t.0 > 0 & m.t.1 == m.class.classV
        call out p1 || if(m.t.name == '', 'union', ':'m.t.name) ,
             || copies(' =' m.a, vv)
        do ux=1+vv to m.t.0
            call classOutDone m.t.ux, a, pr' '
            end
        return 0
        end
    if m.t = 's' then do
        call out p1'stem' m.a.0
        do ux=1 to m.a.0
            call classOutDone m.t.class, a'.'ux, pr' ', pr' .'ux
            end
        return 0
        end
    if m.t = 'c' then do
        if m.t.name = m.a then
            call classOutDone m.t.class, a, pr, p1'choice' m.a
        return 0
        end
    if m.t = 'm' then
        return 0
    return err('bad class type' m.t)
endProcedure classOutDone
/* copy class end   ***************************************************/
/* copy map begin ******************************************************
    a map stores values at keys
    it may also maintain a list of keys
    the basic ideas are similar to the java Interface java.util.Map
    contrary to stems we also handle keys longer then 250 bytes
***********************************************************************/
/*--- initialize the module ------------------------------------------*/
mapIni: procedure expose m.
    if m.map.ini = 1 then
        return
    m.map.ini = 1
    call mIni
    m.map.0 = 0
    m.map.inlineSearch = 1
    call mapReset map.inlineName, map.inline
    return
endProcedure mapIni

mapInline: procedure expose m.
parse arg pName, opt
    if mapHasKey(map.inlineName, pName) then do
        im = mapGet(map.inlineName, pName)
        if pos('l', opt) < 1 & symbol('m.im.0') \== 'VAR' then do
            m.im.0 =  m.im.lEnd - m.im.lBegin - 1
            do ix=1 to m.im.0
                m.im.ix = strip(sourceline(ix+m.im.lBegin), 't')
                end
            end
        return im
        end
    name = '/'
    do lx = m.map.inlineSearch to sourceline()
        if \ abbrev(sourceline(lx), '$') then
            iterate
        li = sourceline(lx)
        s1 = pos('/', li)+ 1
        if s1 < 3 | s1 > 4 then
            iterate
        s2 = pos('/', li, s1)
        if s2 <= s1 then
            iterate
        if s1 == 3 then do
            if name \== substr(li, s1, s2-s1) then
                iterate
            im = 'MAP.INLINE.' || (m.map.inline.0+1)
            call mapAdd map.inlineName, name, im
            m.im.lBegin = lBeg
            m.im.lEnd = lx
            m.im.mark = mrk
            if name == pName then do
                m.map.inlineSearch = lx+1
                return mapInline(pName)
                end
            name = '/'
            end
        else if \ mapHasKey(map.inlineName,
                , substr(li, s1, s2-s1)) then do
            lBeg = lx
            mrk = substr(li, 2, s1-3)
            name = substr(li, s1, s2-s1)
            end
        else do
            name = '/'
            end
        end
    if pos('r', opt) > 0 then
        return ''
    return err('no inline data /'pName'/ found')
endProcedure mapInline

/*--- create a new map ----------------------------------------------*/
mapNew: procedure expose m.
parse arg opt
    m.map.0 = m.map.0 + 1
    return mapReset('MAP.'m.map.0 , opt)
endProcedure mapNew

/*--- make an empty map, if opt <> '' maintain stem of keys
                ('K' in map.keys, '=' in a else in opt) --------------*/
mapReset: procedure expose m.
    parse arg a, opt
    if symbol('m.map.keys.a') == 'VAR' then
        call mapClear a
    if opt = '=' then
        st = a
    else if translate(opt) = 'K' then
        st = 'MAP.KEYS.'a
    else
        st = opt
    m.map.keys.a = st
    if st \== '' then
        m.st.0 = 0
    return a
endProcedure

/*--- add a new key value pair to the map ----------------------------*/
mapAdd: procedure expose m.
parse arg a, ky, val
    vv = mapValAdr(a, ky, 'a')
    m.vv = val
    return val
endProcedure mapAdd

/*--- change the value at a key or add key value ---------------------*/
mapPut: procedure expose m.
parse arg a, ky, val
    vv = mapValAdr(a, ky, 'p')
    m.vv = val
    return val
endProcedure mapPut

/*--- return 1 if key ky exists in map a, 0 otherwise ----------------*/
mapHasKey: procedure expose m.
parse arg a, ky
    return mapValAdr(a, ky) \== ''
endProcedure mapHasKey

/*--- return the value of key ky in map a if it exists,
          else if called with a third argument return third argument
          else issue an error ----------------------------------------*/
mapGet: procedure expose m.
parse arg a, ky
    vv =  mapValAdr(a, ky)
    if vv \== '' then
        return m.vv
    else if arg() > 2 then
        return arg(3)
    else
        return err('missing key in mapGet('a',' ky')')
endProcedure mapGet

/*--- return a stem of all keys --------------------------------------*/
mapKeys: procedure expose m.
parse arg a
    if m.map.keys.a == '' then
        call err 'mapKeys('a') with no keys'
     return m.map.keys.a
endProcedure mapKeys

/*--- remove a key from the map, do nothing if it is missing ---------*/
mapRemove: procedure expose m.
parse arg a, ky
    vv = mapValAdr(a, ky)
    if vv == '' then
        return ''
    if m.map.keys.a \== '' then do
        trace ?R /* not tested yet ???wkTest */
        k = m.map.keys.a
        mx = m.k.0
        do i=1 to mx
            if m.k.i == ky then do
                m.k.i = m.k.mx
                m.k.0 = mx - 1
                return
                end
            end
        end
    val = m.vv
    drop m.a.ky
    return val
endProcedure mapRemove

/*--- remove all entries ---------------------------------------------*/
mapClear: procedure expose m.
parse arg a
    st = mapKeys(a)
    liLe = 243 - length(a)
    do kx=1 to m.st.0
        ky = m.st.kx
        drop m.st.kx
        if length(ky) <= liLe then do
            drop m.a.ky
            end
        else do
            adr = mapValAdr(a, ky)
            if adr \== '' then do
                ha = left(adr, lastPos('.', adr) - 3)
                do i = 1 to m.ha.k.0
                     drop m.ha.k.i m.ha.v.i
                     end
                 drop m.ha.k.0
                 end
            end
        end
    m.st.0 = 0
    return a
endProcedure mapClear

/*--- return the value pointer for a key, '' if non existing
             with fun = 'a' add a key, with 'p' put a key ------------*/
mapValAdr: procedure expose m.
parse arg a, ky, fun
    if length(ky) + length(a) <= 243 then do
        res = a'.'ky
         if symbol('m.res') == 'VAR' then do
            if fun == 'a' then
                call err 'duplicate key' ky 'in map' a
            return res
            end
        else if fun == '' then
            return ''
        end
    else do
        len = 243 - length(a)
        q = len % 4
        ha = a'.'left(ky, len - 2 * q) || substr(ky,
            , (length(ky)-len) % 2 + 2 * q, q) || right(ky, q)
        if symbol('M.ha.k.0') == 'VAR' then do
            do i=1 to m.ha.k.0
                if m.ha.k.i == ky then do
                    if fun == 'a' then
                        call err 'duplicate key' ky ,
                            'map' a 'hash' ha'.K.'i
                    return ha'.V.'i
                    end
                end
            end
        else do
            i = 1
            end
        if fun == '' then
            return ''
        if i > 9 then
            call err 'overflow long key' y 'in map' a 'hash' ha'.K.'i
        m.ha.k.0 = i
        m.ha.k.i = ky
        res = ha'.V.'i
        end
    if m.map.keys.a \== '' then
        call mAdd m.map.Keys.a, ky
    m.res = ''
    return res
endProcedure mapValAdr

/* copy map end *******************************************************/
/* copy mapExp begin **************************************************/
mapVia: procedure expose m.
parse arg a, ky
    sx = pos('|', ky)
    if sx < 1 then
        return mapGet(a, ky)
    via = mapGet(a, left(ky, sx-1))
    do while sx <= length(ky)
        fx = sx+1
        sx = pos('|', ky, fx)
        if sx < 1 then
            sx = length(ky) + 1
        if sx = fx then do
            if symbol('m.via') \== 'VAR' then
                call err 'missing m.'via 'at' sx 'in mapVia('a',' ky')'
            via = m.via
            end
        else do
            f = substr(ky, fx, sx - fx)
            if symbol('m.via.f') \== 'VAR' then
                call err 'missing m.'via'.'f ,
                     'at' sx 'in mapVia('a',' ky')'
            via = m.via.f
            end
        end
    return via
endProcedure mapVia

mapExpAt: procedure expose m.
parse arg a, src, sx
    m.map.ExpAt = 0
    cx = pos('$', src, sx)
    if cx < 1 then
        return substr(src, sx)
    res = substr(src, sx, cx-sx)
    do forever
        if substr(src, cx+1, 1) = '{' then do
            ex = pos('}', src, cx+2)
            if ex < 1 then
                call err 'missing } after' substr(src, cx) 'in' src
            res = res || mapVia(a, strip(substr(src, cx+2, ex-cx-2)))
            ex = ex + 1
            end
        else do
            ex = verify(src, m.mAlfDot, 'n', cx+1)
            if ex < 1 then
                return res || mapVia(a, substr(src, cx+1))
            if ex = cx+1 then do
                m.map.ExpAt = cx
                return res
                end
            res = res || mapVia(a, substr(src, cx+1, ex-cx-1))
            end
        cx = pos('$', src, ex)
        if cx < 1 then
            return res || substr(src, ex)
        res = res || substr(src, ex, cx-ex)
        end
endProcedure mapExpAt

mapExp: procedure expose m.
parse arg a, src
    res = mapExpAt(a, src, 1)
    if m.map.ExpAt \== 0 then
        call err 'mapExp stopped at' substr(src, map.ExpAt) 'in' src
    return res
endProcedure mapExp

mapExpAllAt: procedure expose m.
parse arg a, dst, src, sx, cx
    do while sx <= m.src.0
        li = mapExpAt(a, m.src.sx, cx)
        dx = m.map.ExpAt
        if (cx=1 & dx = 0) | li \= '' then
            call mAdd dst, li
        if dx = 0 then do
            cx = 1
            sx = sx+1
            end
        else do
            return sx dx
            end
        end
    return ''
endProcedure mapExpAllAt

mapExpAll: procedure expose m.
parse arg a, dst, src
    sto = mapExpAllAt(a, dst, src, 1, 1)
    if sto == '' then
         return
    lx = word(sto, 1)
    call err 'mapExpAll stopped at' sto':' m.src.lx
endProcedure mapExpAll
/* copy mapExp end ****************************************************/
/* copy m begin ********************************************************
  we use variables as follows
    m. stem m: all global data and object data that must survive
               a procedure call  (m for memory in Memoria of B5000)
        m.<mbr>.** to avoid conflicts: every rexx Module (copy) should
               only allocate addresses m.<mbr>.** with <mbr> the name of
               the rexx module
        we pass parameters around (e.g. a=address, m=memory, st=stem)
            and the called function may use m.a or m.a.subField etc.
    gg*: local variable in subroutines without procedure
    everything else: temporary data within procedure

  every subroutine is declared as procedure expose m.
        (also if no m. variable is used, because e.g. of error handling)
  the few subroutines that cannot use procedure, should use only
        variables starting with gg
***********************************************************************/
/*---make an area -----*/

mNewArea: procedure expose m.
parse arg nm, adr, newCd, freeCd
    m.m.area.0 = m.m.area.0 + 1
    a = 'M.AREA.'m.m.area.0
    if adr == '=' then
        adr = nm
    else if adr == '' then
        adr = 'M.'m.m.area.0
    if symbol('m.m.n2a.adr') == 'VAR' then
        call err 'adr' adr 'for area' nm 'already used'
    m.m.n2a.adr = a
    call mAlias adr, nm
    m.m.p2a.adr = a

    m.a.0 = 0
    m.a.free.0 = 0
    m.a.address = adr
    m.a.newCode = newCd
    m.a.freeCode = freeCd
    return nm
endProcedure mNewArea

mAlias: procedure expose m.
parse arg oldNa, newNa
    if symbol('m.m.n2a.oldNa') \== 'VAR' then
        call err 'area' oldNa 'does not exist'
    if oldNa == newNa then
        return
    if symbol('m.m.n2a.newNa') == 'VAR' then
        call err 'newName' newNa 'for old' oldNa 'already used'
    m.m.n2a.newNa = m.m.n2a.oldNa
    return
endProcedure mAlias

mBasicNew: procedure expose m. ggArea
parse arg name
    if symbol('m.m.n2a.name') \== 'VAR' then
        call err 'area' name 'does not exists'
    ggArea = m.m.n2a.name
    if m.ggArea.free.0 > 0 then do
        fx = m.ggArea.free.0
        m.ggArea.free.0 = fx-1
        m = m.ggArea.free.fx
        end
    else do
        m.ggArea.0 = m.ggArea.0 + 1
        m = m.ggArea.address'.'m.ggArea.0
        end
    return m
endProcedure mBasicNew

mNew: procedure expose m.
labelMNew:
parse arg name, arg, arg2, arg3
    m = mBasicNew(name)
    interpret m.ggArea.newCode
    return m
endProcedure mNew

mReset: procedure expose m.
parse arg a, name
    ggArea = m.m.n2a.name
    m = a
    interpret m.ggArea.newCode
    return m
endProcedure mReset

mFree: procedure expose m.
parse arg m
    p = 'M.P2A.'left(m, lastPos('.', m)-1)
    area = m.p
    if m.area.freeCode \== '' then
        interpret m.area.freeCode
    fx = m.area.free.0 + 1
    m.area.free.0 = fx
    m.area.free.fx = m
    return
endProcedure mFree
/*--- iterate over all allocate elements of an area ------------------*/
mIterBegin: procedure expose m.
parse arg nm
    a = m.m.n2a.nm
    return m.a.address'.0'
endProcedure mIterBegin

mIter: procedure expose m.
parse arg cur
    if cur == '' then
        return ''
    lx = lastPos('.', cur)
    p = 'M.P2A.'left(cur, lx-1)
    a = m.p
    ix = substr(cur, lx+1)
    do ix=ix+1 to m.a.0
        n = m.a.address'.'ix
        do fx=1 to m.a.free.0 while m.a.free \== n
            end
        if fx > m.a.free.0 then
            return n
        end
    return ''
endProcedure mIter

/*--- cut stem a to length len ---------------------------------------*/
mCut: procedure expose m.
parse arg a, len
    m.a.0 = len
    return a
endProcedure mCut

/*--- add one or several arguments to stem m.a -----------------------*/
mAdd: procedure expose m.
parse arg a
    ix = m.a.0
    do ax = 2 to arg()
        ix = ix + 1
        m.a.ix = arg(ax)
        end
    m.a.0 = ix
    return a'.'ix
endProcedure mAdd

/*--- pop last element from stem m.a ---------------------------------*/
mPop: procedure expose m.
parse arg a
    ix = m.a.0
    if ix < 1 then
        call err 'pop from empty stem' a
    m.a.0 = ix-1
    return m.a.ix
endProcedure mPop

/*--- add to m.dst.* a (sub)sequence of m.src.* ----------------------*/
mAddSt: procedure expose m.
    parse arg dst, src
    dx = m.dst.0
    do sx = 1 to m.src.0
        dx = dx + 1
        m.dst.dx = m.src.sx
        end
    m.dst.0 = dx
    return
endProcedure mAddSt

/*--- find position of first occurrence of ele in stem m,
        return 0 if nonemove a part of a stem -----------------------*/
mPos: procedure expose m.
parse arg m, ele, sx
    if sx == '' then
        sx = 1
    do x=sx to m.m.0
        if m.m.x = ele then
            return x
        end
    return 0
endProcedure mPos

/*--- move a part of a stem ------------------------------------------*/
mMove: procedure expose m.
parse arg m, sx, dx
    if dx < sx then do
        y = dx
        do x=sx to m.m.0
            m.m.y = m.m.x
            y = y + 1
            end
        end
    else if dx > sx then do
        y = m.m.0 + dx - sx
        do x=m.m.0 by -1 to sx
            m.m.y = m.m.x
            y = y - 1
            end
        end
    m.m.0 = m.m.0 + dx - sx
    return
endProcedure mMove

/*--- insert a stem into another ------------------------------------*/
mInsert: procedure expose m.
parse arg m, tx, st
    call mMove m, tx, tx+m.st.0
    do sx=1 to m.st.0
        dx = tx-1+sx
            m.m.dx = m.st.sx
            end
    return
endProcedure mInsert

/*--- strip all elements of a stem -----------------------------------*/
mStrip: procedure expose m.
parse arg st, opt
    if opt == '' then
        opt = 'b'
    do x=1 to m.st.0
        m.st.x = strip(m.st.x, opt)
        end
    return st
endProcedure mStrip


/* cat the lines of a stem, possibly repeated --------------------------
       args: stem, fmt see fGen: -------------------------------------*/
mCat: procedure expose m.
parse arg st, fmt
    if m.st.0 < 1 then
        return ''
    res = f(fmt, m.st.1)
    do sx=2 to m.st.0
        res = res || fPlus(fmt 'nxt', m.st.sx)
        end
    return res || fFld(fmt 'end')
endProcedure mCat

mIni: procedure expose m.
    if m.m.ini = 1 then
        return
    m.m.ini = 1
    m.mAlfLC  = 'abcdefghijklmnopqrstuvwxyz'
    m.mAlfUC  = translate(m.mAlfLC)
    m.mAlfa   = m.mAlfLC || m.mAlfUC
    m.mAlfNum = m.mAlfa || '0123456789'
    m.mAlfDot = m.mAlfNum || '.'
    m.mAlfRex1 = m.mAlfa'@#$?'  /* charset problem with ¬|  */
    m.mAlfRexR = m.mAlfRex1'.0123456789'
    m.m.area.0 = 0
    call mNewArea
    return
endProcedure mIni
/* copy m end *********************************************************/
/* copy adrIsp begin *************************************************/
/**********************************************************************
    lmd: catalog read
    call sequence: lmdBegin, lmdNext*, lmdEnd
        mit lmd service (mit save in file und read,
                         weil list zu langsam und listcat abstürzt)
        1. arg (grp)     als group dataset für lmd save
                         und dd name für file read
***********************************************************************/
lmdBegin: procedure expose m.
    parse arg grp, lev
    call adrIsp 'lmdinit listid(lmdId) level('lev')'
    res = adrIsp('lmdlist listid(&lmdId) option(save) group('grp')', 4)
    call adrIsp 'lmdfree listid(&lmdId)'
    if res = 0 then do
        call trc timing() 'lmdlist save' grp lev
        call adrTso 'alloc dd('grp') shr dsn('grp'.datasets)'
        end
    else do
        call trc 'no datasets found' timing() 'lmdlist save' grp lev
        call adrTso 'alloc dd('grp') dummy'
        end
    call readDDBegin grp
return /* end lmdBegin */

lmdNext:
    parse arg ggGrp, ggSt, withVolume
    if \ readDD(ggGrp, ggSt) then
         return 0
    if withVolume \== 1 then
        do ggIx=1 to value(ggSt'0')
            x = value(ggSt || ggIx, word(value(ggSt || ggIx), 1))
            end
    return 1
endSubroutin lmdNext

lmdEnd: procedure expose m.
    parse arg grp
    call readDDEnd grp
    call adrTso 'free dd('grp')'
return /* end lmdEnd */

lmd: procedure expose m.
    parse arg lev, withVol
    call lmdBegin gg1, lev
    do while lmdNext(gg1, q., withVol)
        do x=1 to q.0
           call out q.x
           end
        end
    call lmdEnd gg1
    return
endProcedure lmd
/**********************************************************************
    member list of a pds:
        call sequence x=lmmBegin(dsn) lmmNext(x) * lmmEnd(x)
***********************************************************************/
lmm: procedure expose m.
parse arg dsn
    id = lmmBegin(dsn)
    do ix=1 by 1
        m = lmmNext(id)
        if m = '' then
            leave
        call out m
        end
    call lmmEnd id
    return
endProcedure lmm

lmmBegin: procedure expose m.
parse arg dsn
    mbr = dsnGetMbr(dsn)
    pds = dsnSetMbr(dsn, )
    call adrIsp "LMINIT DATAID(lmmId) DATASET('"pds"') ENQ(SHRW)"
    call adrIsp "LMOPEN DATAID("lmmId") OPTION(INPUT) "
    res = lmmId
    if mbr <> '' then
        res = res 'pattern('mbr')'
    return res
endProcedure lmmBegin

lmmEnd: procedure expose m.
parse arg lmmId opt
    call adrIsp "LMMLIST DATAID("lmmId") option(free)", 8
    call adrIsp "LMCLOSE DATAID("lmmId")"
    call adrIsp "LMFREE DATAID("lmmId")"
    return
endProcedure lmmEnd

lmmNext: procedure expose m.
parse arg lmmId opt
    if adrIsp("LMMLIST DATAID("lmmid")" ,
               "OPTION(LIST) MEMBER(Mbr)" opt, 4 8) = 0 then
        return strip(mbr)
    else
        return ''
endProcedure lmmNext

lmmRm: procedure expose m.
parse arg dsn, mbrs
    mbrs = dsnGetMbr(dsn) mbrs
    pds = dsnSetMbr(dsn, )
    call adrIsp "LMINIT DATAID(lmmId) DATASET("pds") ENQ(SHRW)"
    call adrIsp "LMOPEN DATAID("lmmId") OPTION(OUTPUT) "
    err = ''
    do wx=1 to words(mbrs)
        m1 = word(mbrs, wx)
        rr = adrIsp("lmmDel dataid("lmmId") member("m1")", 0 8 12)
        if rc = 0 then
            say 'removed' m1 'from' pds
        else if rc = 8 then
            say 'not found' m1 'in' pds
        else do
            err = 'error deleting' m1 'in' pds 'rc' rr strip(zerrlm)
            say err
            leave
            end
        end
    call adrIsp "LMCLOSE DATAID("lmmId")"
    call adrIsp "LMFREE DATAID("lmmId")"
    if err <> '' then
        call err err
    return
endProcedure lmmRm
/*--- address ispf with error checking -------------------------------*/
adrIsp:
    parse arg ggIspCmd, ggRet
    address ispexec ggIspCmd
    if rc = 0 then return 0
    else if ggRet == '*' then return rc
    else if wordPOS(rc, ggRet) > 0 then return rc
    else
        call err 'adr ispExec rc' rc 'in' ggIspCmd':' strip(zerrlm)
endSubroutine adrIsp

/*--- address editor with error checking -----------------------------*/
adrEdit:
    parse arg ggEditCmd, ggRet
    address isrEdit ggEditCmd
    if rc = 0 then return 0
    else if ggRet == '*' then return rc
    else if wordPOS(rc, ggRet) > 0 then return rc
    else
        call err 'adr isrEdit rc' rc 'for' ggEditCmd
endSubroutine adrEdit
/* copy adrIsp end   *************************************************/
/* copy adrTso begin *************************************************/
/*--- send ggTsoCmd to tso, fail if rc <> 0 or not listed in ggRet ---*/
adrTso:
    parse arg ggTsoCmd, ggRet
    address tso ggTsoCmd
    if rc == 0                     then return 0
    else if ggRet == '*'           then return rc
    else if wordPos(rc, ggRet) > 0 then return rc
    else
        call err 'adrTso rc' rc 'for' ggTsoCmd
return /* end adrTso */

/*--- format dsn from tso format to jcl format -----------------------*/
dsn2jcl: procedure expose m.
parse upper arg dsn ., addPrefix
    if left(dsn,1) = "'" then
        return strip(dsn, 'b', "'")
    sp = sysvar('SYSPREF')
    if sp == '' then
        sp = userid()
    cx = pos('~', dsn)
    if cx < 1 & addPrefix == 1 then
        return sp'.'dsn
    do while cx \== 0
        le = left(dsn, cx-1)
        ri = substr(dsn, cx+1)
        if right(le, 1) == '.' | left(ri, 1) == '.' then
            dsn = le || sp || ri
        else
            dsn = le || left('.', le \== '') || sp ,
                     || left('.', ri \== '') || ri
        cx = pos('~', spec, cx)
        end
    return dsn
endProcedure dsn2Jcl

/*--- format dsn from jcl format to tso format -----------------------*/
jcl2dsn: procedure expose m.
parse arg dsn .
    return "'"dsn"'"
endProcedure jcl2dsn

dsnSetMbr: procedure expose m.
parse arg dsn, mbr
     bx = pos('(', dsn)
     if bx > 0 then
         dsn = strip(left(dsn, bx-1))
     if mbr <> '' then
         dsn = dsn'('strip(mbr)')'
     return dsn
endProcedure dsnSetMbr

dsnGetMbr: procedure expose m.
parse arg dsn
     lx = pos('(', dsn)
     rx = pos(')', dsn, lx+1)
     if lx < 1 then
         return ''
     else if lx < rx then
         return substr(dsn, lx+1, rx-lx-1)
     else
         return strip(substr(dsn,lx+1))
endProcedure dsnGetMbr
/**********************************************************************
    io: read or write a dataset with the following callsequences:
        read:  readDDBegin, readDD*,  readDDEnd
        write: writeBegin,  writeDD*, writeEnd

        readDD returns true if data read, false at eof
***********************************************************************/

/*--- prepare reading from a DD --------------------------------------*/
readDDBegin: procedure expose m.
    parse upper arg ggDD
    call errAddCleanup 'call readDDEnd' ggDD', "*"'
return /* end readDDBegin */

/*--- read from DD ggDD into ggSt, return false at eof ---------------*/
readDD:
    parse arg ggDD, ggSt, ggCnt
    if ggCnt = '' then
        ggCnt = 100
    call adrTso 'execio' ggCnt 'diskr' ggDD '(stem' ggSt')', 2
    return (value(ggSt'0') > 0)
return /* end readDD */

/*--- finish reading DD  ggDD ----------------------------------------*/
readDDEnd: procedure expose m.
    parse upper arg ggDD, ggRet
    call errRmCleanup 'call readDDEnd' ggDD', "*"'
    return adrTso('execio 0 diskr' ggDD '(finis)', ggRet)
endProcedure readDDEnd

/*--- prepare writing to DD ggDD -------------------------------------*/
writeDDBegin: procedure expose m.
    parse upper arg ggDD
                  /* ensure file is erased, if no records are written */
    call adrTso 'execio' 0 'diskw' ggDD '(open)'
    call errAddCleanup 'call writeDDEnd' ggDD', "*"'
return /* end writeDDBegin */

/*--- write to gg ggDD from stem ggSt, ggCnt records -----------------*/
writeDD:
    parse arg ggDD, ggSt, ggCnt
    if ggCnt == '' then
        ggCnt = value(ggst'0')
    call adrTso 'execio' ggCnt 'diskw' ggDD '(stem' ggSt')'
    return
endSubroutine writeDD

/*--- end writing to dd ggDD (close) --------------------------------*/
writeDDEnd: procedure expose m.
parse upper arg ggDD, ggRet
    call errRmCleanup 'call writeDDEnd' ggDD', "*"'
    return adrTso('execio 0 diskw' ggDD '(finis)', ggRet)
endProcedure writeDDEnd

/*--- readNx: read next line, using buffer ---------------------------*/
/*--- begin: allocate dsnSpec and ini ------------------------ -------*/
readNxBegin: procedure expose m.
    parse arg m, m.m.dsn, m.m.dd, m.m.Cnt
    if m.m.dd = '' then
        m.m.dd = 'DDNX'
    if m.m.cnt = '' then
        m.m.cnt = 1000
    m.m.cx = m.m.cnt + 999
    m.m.buf0x = 0
    m.m.0 = 0
    call dsnAlloc 'dd('m.m.dd')' m.m.dsn
    call readDDBegin m.m.dd
    return m
endProcedure readDDNxBegin

/*--- return the stem of the next line, or '' at end -----------------*/
readNx: procedure expose m.
parse arg m
    ix = m.m.cx + 1
    m.m.cx = ix
    if m.m.cx <= m.m.0 then
        return m'.'ix
    m.m.buf0x = m.m.buf0x + m.m.0
    if \ readDD(m.m.dd, 'M.'m'.', m.m.cnt)then
        return ''
    m.m.cx = 1
    return m'.1'
endProcedure readDDNx

/*--- return the position (line number) of reader
           plus le characters of the current line (default 50) ------*/
readnxPos: procedure expose m.
parse arg m, le
    li = m'.'m.m.cx
    li = strip(m.li, 't')
    if arg() < 2 then
        le = 50
    if le < 1 then
        li = ''
    else if length(li) <= le then
        li = ':' li
    else
        li = ':' left(li, le-3)'...'
    return 'line' (m.m.buf0x + m.m.cx)li
endProcedure readnxPos

/*--- close and deallocate ------------------------------------------*/
readNxEnd: procedure expose m.
parse arg m
    call readDDEnd m.m.dd
    call tsoFree m.m.dd
    return
endProcedure readDDNxEnd

/*--- standardise a dsn spec
       word1             dsName or -
       word2             dd     or -
       word3             disp   or -
       word4 to first :  attributes in tso format
       after first :     attributes for new allocation
----------------------------------------------------------------------*/
dsnSpec: procedure expose m.
parse upper arg spec
    rr = ''      /* put leading - in separate words */
    do sx=1 while words(rr) < 3 & wx \= ''
        wx = word(spec, sx)
        do while abbrev(wx, '-') & words(rr) < 3
            wx = substr(wx, 2)
            rr = rr '-'
            end
        rr = rr wx
        end
    spec = rr subWord(spec, sx)
    na = ''
    dd = ''
    di = ''
    if left(spec, 1) = '&' then /* external spec is handled ok */
        spec = strip(substr(spec, 2))
    do wx=1 by 1
        w = word(spec, wx)
        if w = '' | abbrev(w, '.') | abbrev(w, ':') then
            leave
        else if wordPos(w, 'OLD SHR MOD NEW')>0 | abbrev(w, 'SYSO') then
            di = w
        else if w = 'CATALOG' then
            di = di w
        else if abbrev(w, 'DD(') then
            dd = substr(w, 4, length(w)-4)
        else if abbrev(w, 'DSN(') then
            na = strip(substr(w, 5, length(w)-5))
        else if na == '' then
            na = dsn2jcl(w)
        else if dd == '' then
            dd = w
        else if di == '' then
            di = w
        else
            leave
        end
    if na == '' then
        na = '-'
    else if abbrev(na, "'") then
        na = substr(na, 2, length(na)-2)
    if dd == '' then dd = '-'
    if di == '' then di = '-'
    re = subword(spec, wx)
    if abbrev(re, '.') then
        re = substr(re, 2)
    return na dd di re
endProcedure dsnSpec

/*--- alloc a dsn with dsnAlloc
          if the dsn is inuse wait and retry
          until either the allocation is successfull
          or the timeout occurs --------------------------------------*/
dsnAllocWait: procedure expose m.
parse upper arg spec, pDi, pDD, timeOut
    x = max(1, arg() - 1)
    do rt=0
        m.adrTsoAl.1 = ''
        m.adrTsoAl.2 = ''
        m.adrTsoAl.3 = ''
        call outtrap m.adrTsoAl.
        res = dsnAlloc(spec, pDi, pDD, '*')
        call outtrap off
        if \ abbrev(res, ' ') then
            return res
        msg = m.adrTsoAl.1'\n'm.adrTsoAl.2'\n'm.adrTsoAl.3
        if rt > timeOut & timeOut \== '' then
            return err('timeout allocating' spec time() '\n'msg)
        if pos('DATA SET IS ALLOCATED TO ANOTHER', msg) < 1 then
            return err('allocating' spec'\n'msg)
        say time() 'sleep and retry alloc' spec
        call sleep 1, 0
        end
endProcedure dsnAllocWait

/*--- alloc a dsn or a dd
          spec '-'<ddName>
               datasetName? disposition? '.'? attributes? (':' newAtts)?
          disp default disposition
          dd   default dd name
          retRc   erlaubte ReturnCodes (leer = 0)
          returns if ok then ddName <rexx for free> otherwise rc -----*/
dsnAlloc: procedure expose m.
parse upper arg spec, pDi, pDD, retRc
    parse value dsnSpec(spec) with na dd di rest
    if na = '-' then
        m.dsnAlloc.dsn = ''
    else
        m.dsnAlloc.dsn = na
    if na == '-' & dd \== '-' & di == '-' & rest = '' then
        return dd
    if dd == '-' & pDD \== '' then
        dd = pDD
    if dd == '-' then do
        if symbol('m.adrTso.ddNum') = 'VAR' then
            dd = m.adrTso.ddNum + 1
        else
            dd = 1
        m.adrTso.ddNum = dd
        dd = 'DD' || dd
        end
    if di = '-' & pDi \== '' then
        di = pDi
    if di = '-' then
        di = 'SHR'
    else if pos('(', na) < 1 then
        nop
    else if di = 'MOD' then
        call err 'disp mod for' na
    else
        di = 'SHR'
    if pos('/', na) > 0 then
        return csmAlloc(na dd di rest, retRc)
    else
        return tsoAlloc(na dd di rest, retRc)
endProcedure dsnAlloc

tsoAlloc: procedure expose m.
parse arg na dd disp rest ':' nn, retRc
    c = 'alloc dd('dd')' disp
    if na \== '-' then
        c = c "DSN('"na"')"
    else if disp = 'NEW' and nn \== '' then
        c = c dsnCreateAtts(,nn)
    call outtrap m.adrTsoAl.
    alRc = adrTso(c rest, '*')
    call outtrap off
    if alRc =  0 then do
        call errAddCleanup 'call tsoFree' dd', "*"'
        return dd 'call tsoFree' dd';'
        end
    if nn \= '' & wordPos(disp, 'OLD SHR') > 0 ,
          & sysDsn("'"m.dsnAlloc.dsn"'") == 'DATASET NOT FOUND' then do
        say 'tsoAlloc creating' c rest ':'nn
        call adrTso 'alloc dd('dd') new catalog' dsnCreateAtts(na, nn)
        call adrTso 'free  dd('dd')'
        return tsoAlloc(na dd disp rest, retRc)
        end
    do ax=1 to m.adrTsoAl.0
        say m.adrTsoal.ax
        end
    if retRc = '*' | wordPos(alRc, retRc) > 0 then
        return ' ' alRc
    call err 'tsoAlloc rc' alRc 'for' c rest
endProcedure tsoAlloc

tsoAtts: procedure expose m.
parse arg dsn
    rc = listDsi("'"dsn"' SMSINFO")
    if rc = 0 then
        mv = ''
    else if rc = 4 & sysReason = 19 then do
        mv = 'UNITCNT(30)'
        say 'multi volume' mv
        end
    else if rc ^= 0 then
        call err 'listDsi rc' rc 'reason' sysReason,
                             sysMsgLvl1 sysMsgLvl2
    al = 'CAT'
    al = ''
    if right(sysDsSms, 7) == 'LIBRARY' ,
        | abbrev(sysDsSms, 'PDS') then
         al = al 'DSNTYPE(LIBRARY)'
    if sysUnits = 'TRACK' then
        sysUnits = 'TRACKS'
    al = al "DSORG("sysDSorg") MGMTCLAS("sysMgmtClass")",
        "DATACLAS("sysDataClass")" ,
        "RECFM("translate('1 2 3', ' 'sysREcFM, ' 123')")",
        "LRECL("SYSLRECL")",
        "blksize("sysBLkSIZE")",
        "SPACE("sysPrimary"," sysSeconds")" sysUnits mv
    return al
endProcedure tsoAtts

tsoFree: procedure expose m.
parse arg dd, ggRet
    call adrTso 'free dd('dd')', ggRet
    call errRmCleanup 'call tsoFree' dd', "*"'
    return
endProcedure tsoFree

dsnCreateAtts: procedure expose m.
parse arg dsn, atts, forCsm
    forCsm = forCsm == 1
    aU = ' 'translate(atts)
    if abbrev(atts, ':') then do
        parse var atts a1 atts
        bl = 32760
        rl = substr(a1, 3)
        if abbrev(a1, ':F') then do
            if rl = '' then
                rl = 80
            recfm='f b'
            bl = bl - (bl // rl)
            end
        else do
            if rl = '' then
                rl = bl-4
            recfm = substr(a1, 2, 1) 'b'
            end
        if forCsm then
            atts =  atts "recfm("space(recfm, 0)") lrecl("rl")",
                    "blkSize("bl")"
        else
            atts = atts "recfm("recfm") lrecl("rl") block("bl")"
        end
    if pos('(', dsn) > 0 & pos(' DSNTYPE(', aU) < 1 ,
                         & pos(' DSORG(',   aU) < 1 then
        atts = atts 'dsntype(library) dsorg(po)'
    if pos(' MGMTCLAS(', aU) < 1 then
        atts = atts 'mgmtclas(COM#A091)'
    if pos(' SPACE(', aU) < 1 then
        if forCsm then
            atts = atts 'space(10, 1000) cylinder'
        else
            atts = atts 'space(10, 1000) cyl'
    if dsn == '' then
       return atts
    return "dataset('"dsnSetMbr(dsn)"')" atts
endProcedure dsnCreateAtts

/*--- read the dataset specified in ggDsnSpec to stem ggSt -----------*/
readDSN:
parse arg ggDsnSpec, ggSt
    ggAlloc = dsnAlloc(ggDsnSpec, 'SHR', 'readDsN')
    call adrTso 'execio * diskr' word(ggAlloc, 1) '(stem' ggSt' finis)'
    interpret subword(ggAlloc, 2)
    return
endSubroutine readDsn

/*--- write the dataset specified in ggDsnSpec from stem ggSt
          write ggCnt records if not empty otherwise ggst0
          if ggSay 1 then say ... records written to ... -------------*/
writeDSN:
parse arg ggDsnSpec, ggSt, ggCnt, ggSay
    if ggCnt == '' then
        ggCnt = value(ggst'0')
    ggAlloc = dsnAlloc(ggDsnSpec, 'OLD', 'readDsN')
    call adrTso 'execio' ggCnt 'diskw' word(ggAlloc, 1) ,
            '(stem' ggSt 'open finis)'
    interpret subword(ggAlloc, 2)
    if ggSay == 1 | m.debug == 1 then
       say ggCnt 'records written to' ggDsnSpec
    return
endSubroutine writeDsn

copyDSN: procedure expose m.
parse arg frSpec, toSpec, ggSay
    frDD = dsnAlloc(frSpec, 'SHR', 'FRDD')
    toDD = dsnAlloc(toSpec, 'OLD', 'TODD')
    call readDDBegin word(frDD, 1)
    call writeDDBegin word(toDD, 1)
    cnt = 0
    do while readDD(word(frDD, 1), r.)
        call writeDD word(toDD, 1), r.
        cnt = cnt + r.0
        end
    call readDDEnd word(frDD, 1)
    call writeDDEnd word(toDD, 1)
    interpret ';' subword(frDD, 2) '; ; ;' subword(toDD, 2)
    if ggSay == 1 | m.debug == 1 then
       say cnt 'records copied from' frSpec 'to' to toSpec
    return
endSubroutine writeDsn
/* copy adrTso end ****************************************************/
/* copy err begin ******************************************************
    messages, errorhandling,help
***********************************************************************/
errIni: procedure expose m.
    if m.err.ini == 1 then
        return
    call outIni
    m.err.ini     = 1
    m.err.handler = ''
    m.err.cleanup = ';'
    m.err.opt     = ''
    return
endProcedure errIni
/* configure err -----------------------------------------------------*/
errReset: procedure expose m.
    call errIni
    parse arg m.err.opt, m.err.handler
    if pos('I', translate(m.err.opt)) > 0 then
        if errOS() \== 'LINUX' then
            if sysVar('sysISPF') = 'ACTIVE' then
                    call adrIsp 'control errors return'
    return
endSubroutine errReset

/*--- error routine: abend with message ------------------------------*/
err:
    parse arg ggTxt, ggOpt
    call errIni
    drop err handler cleanup opt call return
    if ggOpt == '' & m.err.handler \== '' then do
        if 1 then do /* no detection of recursive err call loop
                        --> will anyway fail by stack overflow */
            interpret m.err.handler
            end
        else do
                     /* avoid recursive err call loop */
            drop call return
            if symbol('m.err.call') \== 'VAR' then
                m.err.call = 1
            else
                m.err.call = m.err.call + 1
            if m.err.call > 10 then do
                say 'errHandler loop:' m.err.handler
                end
            else do
                m.err.return = 1
                call errInterpret m.err.handler
                m.err.call = m.err.call - 1
                if m.err.return then
                    return result
                end
            end
        end
    call outDst
    call errSay ggTxt, 'e'
    call errCleanup
    if ggOpt == '' then
        ggOpt = m.err.opt
    ggOpt = translate(ggOpt)
    if pos('T', ggOpt) > 0  then do
        trace ?r
        say 'trace ?r in err'
        end
    if pos('H', ggOpt) > 0  then do
        call errSay 'divide by zero to show stackHistory', 'e'
        x = 1 / 0
        end
    call errSay 'exit(12)', 'e'
    exit errSetRc(12)
endSubroutine err

/*--- error routine: user message cleanup exit -----------------------*/
errEx:
    parse arg ggTxt
    call errIni
    call outDst
    call errSay ggTxt
    call errCleanup
    exit 8
endProcedure errEx

errAddCleanup: procedure expose m.
parse arg code
    if m.err.ini \== 1 then
        call errIni
        /* concatenate backwards, then it is executed also backwards */
    m.err.cleanup = ';'code || m.err.cleanup
    return
endProcedure errAddCleanup

errRmCleanup: procedure expose m.
parse arg code
    if m.err.ini \== 1 then
        call errIni
    cx = pos(';'code';', m.err.cleanup)
    if cx > 0 then
        m.err.cleanup = left(m.err.cleanup, cx) ,
                     || substr(m.err.cleanup, cx + length(code)+2)
    return
endProcedure errRmCleanup

errCleanup: procedure expose m.
    call errIni
    cl = m.err.cleanup
    if cl = ';' then
        return
    m.err.cleanup = ';'
    say 'err cleanup begin' cl
    call errInterpret cl
    say 'err cleanup end' cl
    return
endProcedure errCleanup

errInterpret: procedure expose m.
parse arg code
    interpret code
    m.err.return = 0
    return
endProcedure errInterpret
/*--- assert that the passed rexx expression evaluates to true -------*/
assert:
    interpret 'assertRes =' arg(1)
    if \ assertRes then
        call err 'assert failed' arg(1)':' arg(2)
    return
endProcedure assert

/*--- output an errorMessage msg with pref pref
           split message in lines at '/n' ---------------------------*/
errSay: procedure expose m.
parse arg msg, pref
    parse source . . ggS3 .                           /* current rexx */
    if pref == 'e' then
        msg = 'fatal error in' ggS3':' msg
    else if pref == 'w' then
        msg = 'warning in' ggS3':' msg
    else if pref \== '' then
        msg = pref':' msg
    return outNl(msg)
endProcedure errSay

/*--- abend with Message after displaying help -----------------------*/
errHelp: procedure expose m.
parse arg msg, op
    call errSay msg, 'e'
    call help
    call err msg, op
endProcedure errHelp

/*--- return the Operating System we are running on: TSO or LINUX ---*/
errOS: procedure expose m.
    parse source os .
    return os
endProcedure errOS

/*--- set rc for ispf: -------------------------------------------------
    if a cmd is run by ispStart, its RC is ignored,
         but ISPF passes the value of the shared varible 3IspfRc
         back as return code
----------------------------------------------------------------------*/
errSetRc: procedure expose m.
parse arg zIspfRc
    if errOS() \== 'LINUX' then
           if sysVar('sysISPF') = 'ACTIVE' then
            address ispExec vput 'zIspfRc' shared
    return zIspfRc
endProcedure errSetRc

/*--- output a trace message if m.tr is set -----------------------*/
trc: procedure expose m.
parse arg msg
    if m.trace == 1 then
        call out 'trc:' msg
    return
endProcedure trc

debug: procedure expose m.
parse arg msg
    if m.debug == 1 then
        call out 'debug' msg
    return
endProcedure debug

/*--- display the first comment block of the source as help ----------*/
help: procedure expose m.
    parse source . . s3 .
    call out right(' help for rexx' s3, 79, '*')
    do ax=1 to arg()
        say ' ' arg(ax)
        end
    do lx=1 by 1
        if pos('/*', sourceLine(lx)) > 0 then
            leave
        else if lx > 10 then do
            call out 'initial commentblock not found for help'
            return
            end
        end
    do lx=lx+1 by 1
        li = strip(sourceLine(lx), 't', ' ')
        if pos('*/', li) > 0 then
            leave
        call out li
        end
    call out right(' end help for rexx' s3, 79, '*')
    return 4
endProcedure help

/*--- output all lines (separated by '\n') of all args --------------*/
outNl: procedure expose m.
    do ax=1 to max(1, arg())
        msg = arg(ax)
        sx = 0
        bx = -1
        do lx=1 until bx >= length(msg)
                ex = pos('\n', msg, bx+2)
            if ex < 1 then
                ex = length(msg)+1
            call out substr(msg, bx+2, ex-bx-2)
            bx = ex
         end
        end
    return 0
endProcedure outNl
/* copy err end   *****************************************************/
/* copy ut begin  *****************************************************/
/*--- if function  warning all3 arguments get evaluated|
                   e.g if(x=0, 'infinity', 1/0) will fail| -----------*/
if: procedure expose m.
parse arg co, ifTrue, ifFalse
    if co then
        return ifTrue
    else
        return ifFalse
endProcedure if
/*--- embedded ASSignement:
      assign the second argument to the variable with name in first arg
      and return the value assigned ----------------------------------*/
ass:
    call value arg(1), arg(2)
    return arg(2)
/*--- embedded ASSignement only if NotNull:
      assign the second argument to the variable with name in first arg
      if the second arg is not null, return new value ---------------*/
assIf:
    if arg(2) == '' then
        return value(arg(1))
    call value arg(1), arg(2)
    return arg(2)

/*--- return first nonNull argument ---------------------------------*/
nn:
    if arg(1) \== '' then
        return arg(1)
    if arg(2) \== '' then
        return arg(2)
    call err 'nn() both args empty'

/*--- embedded ASSignement return NotNull:
      assign the second argument to the variable with name in first arg
      and return 1 if value not null, 0 if null ----------------------*/
assNN:
    call value arg(1), arg(2)
    return arg(2) \== ''

/*--- return current time and cpu usage ------------------------------*/
timing: procedure expose m.
return return time() 'ela='time('E') 'cpu='sysvar('syscpu'),
            'su='sysvar('syssrv')

timingTest: procedure expose m.
    say 'begin' timing()  sysvar('sysnode')
    do 30000000
       end
    say 'end  ' timing()
return

/*--- sleep several seconds ------------------------------------------*/
sleep: procedure expose m.
parse arg secs, sayIt
    if sayit <> 0 then
        say 'sleeping' secs 'secs' time()
    CALL SYSCALLS 'ON'
    ADDRESS SYSCALL "sleep" secs
    CALL SYSCALLS 'OFF'
    if sayit <> 0 then
        say 'slept' secs 'secs' time()
    return
endProcedure sleep

/*--- elongate inp with spaces up to given len -----------------------*/
elong: procedure expose m.
parse arg inp, len
    if length(inp) >= len then
        return inp
    return left(inp, len)
endProcedure elong

/*--- quote string txt using quoteChar qu ("""" ==> ") ---------------*/
quote: procedure expose m.
parse arg txt, qu
    if qu = '' then
        qu = '"'
    res = qu
    ix = 1
    do forever
        qx = pos(qu, txt, ix)
        if qx = 0 then
            return res || substr(txt, ix) || qu
        res = res || substr(txt, ix, qx-ix) || qu || qu
        ix = qx + length(qu)
        end
endProcedure quote

/*--- return the count of occurrences of needle in heyStack ----------*/
posCount: procedure expose m.
parse arg needle, hayStack, start, fin
    if start = '' then
        start = 1
    if fin = '' then
        fin = length(hayStack) + 1 - length(needle)
    do cnt = 0 by 1
        start = pos(needle, haystack, start)
        if start < 1 | start > fin then
             return cnt
        start = start + length(needle)
        end
endProcedure posCount

repAll: procedure expose m.
parse arg src, w, new
    res = ''
    cx = 1
    do forever
        nx = pos(w, src, cx)
        if nx < 1 then
            return res || substr(src, cx)
        res = res || substr(src, cx, nx-cx) || new
        cx = nx + length(w)
        end
endProcedure repAll

repAllWords: procedure expose m.
parse arg src, w, new
    res = ''
    wx = 0
    cx = 1
    do forever
        wx = wordPos(w, src, wx+1)
        if wx < 1 then
            return res || substr(src, cx)
        nx = wordindex(src, wx)
        res = res || substr(src, cx, nx-cx) || new
        cx = nx + length(w)
        end
endProcedure repAllWords
/* copy ut end ********************************************************/